|
12 | 12 | from strawberry.file_uploads import Upload |
13 | 13 | from strawberry.types import Info |
14 | 14 |
|
| 15 | +from api.managers.dvc_manager import DVCManager |
15 | 16 | from api.models import ( |
16 | 17 | Dataset, |
17 | 18 | Resource, |
18 | 19 | ResourceFileDetails, |
19 | 20 | ResourcePreviewDetails, |
20 | 21 | ResourceSchema, |
| 22 | + ResourceVersion, |
21 | 23 | ) |
| 24 | +from api.models.Resource import _increment_version |
22 | 25 | from api.types.type_resource import TypeResource |
23 | 26 | from api.utils.constants import FORMAT_MAPPING |
24 | 27 | from api.utils.data_indexing import index_resource_data |
@@ -64,6 +67,16 @@ class UpdateFileResourceInput: |
64 | 67 | preview_details: Optional[PreviewDetails] = strawberry.field(default=None) |
65 | 68 |
|
66 | 69 |
|
| 70 | +@strawberry.input |
| 71 | +class CreateMajorVersionInput: |
| 72 | + """Input type for creating a major version of a resource.""" |
| 73 | + |
| 74 | + resource_id: uuid.UUID = strawberry.field() |
| 75 | + description: str = strawberry.field( |
| 76 | + description="Description of the changes in this major version" |
| 77 | + ) |
| 78 | + |
| 79 | + |
67 | 80 | @strawberry.enum |
68 | 81 | class FieldType(Enum): |
69 | 82 | """Enum for field types.""" |
@@ -339,3 +352,71 @@ def delete_file_resource(self, info: Info, resource_id: uuid.UUID) -> bool: |
339 | 352 | return True |
340 | 353 | except Resource.DoesNotExist as e: |
341 | 354 | raise ValueError(f"Resource with ID {resource_id} does not exist.") |
| 355 | + |
| 356 | + @strawberry_django.mutation(handle_django_errors=True) |
| 357 | + @trace_resolver(name="create_major_version", attributes={"component": "resource"}) |
| 358 | + def create_major_version( |
| 359 | + self, info: Info, input: CreateMajorVersionInput |
| 360 | + ) -> TypeResource: |
| 361 | + """Create a major version for a resource. |
| 362 | +
|
| 363 | + This should be used when significant changes are made to the resource data structure, |
| 364 | + such as schema changes, column additions/removals, or other breaking changes. |
| 365 | + """ |
| 366 | + import os |
| 367 | + |
| 368 | + from django.conf import settings |
| 369 | + |
| 370 | + try: |
| 371 | + # Get the resource |
| 372 | + resource = Resource.objects.get(id=input.resource_id) |
| 373 | + except Resource.DoesNotExist: |
| 374 | + raise ValueError(f"Resource with ID {input.resource_id} does not exist") |
| 375 | + |
| 376 | + # Get the latest version |
| 377 | + last_version = resource.versions.order_by("-created_at").first() |
| 378 | + |
| 379 | + if not last_version: |
| 380 | + logger.warning( |
| 381 | + f"No previous version found for resource {resource.name}, creating initial version" |
| 382 | + ) |
| 383 | + new_version = "v1.0.0" |
| 384 | + else: |
| 385 | + # Increment major version |
| 386 | + new_version = _increment_version( |
| 387 | + last_version.version_number, increment_type="major" |
| 388 | + ) |
| 389 | + |
| 390 | + # Initialize DVC manager |
| 391 | + dvc = DVCManager(settings.DVC_REPO_PATH) |
| 392 | + |
| 393 | + # Get the resource file path |
| 394 | + file_path = resource.resourcefiledetails.file.path |
| 395 | + |
| 396 | + # Determine if file is large and should use chunking |
| 397 | + file_size = ( |
| 398 | + resource.resourcefiledetails.file.size |
| 399 | + if hasattr(resource.resourcefiledetails.file, "size") |
| 400 | + else os.path.getsize(file_path) |
| 401 | + ) |
| 402 | + use_chunked = file_size > 100 * 1024 * 1024 # 100MB threshold |
| 403 | + |
| 404 | + # Track with DVC |
| 405 | + dvc_file = dvc.track_resource(file_path, chunked=use_chunked) |
| 406 | + message = f"Major version update for resource: {resource.name} to version {new_version}" |
| 407 | + dvc.commit_version(dvc_file, message) |
| 408 | + dvc.tag_version(f"{resource.name}-{new_version}") |
| 409 | + |
| 410 | + # Create version record |
| 411 | + ResourceVersion.objects.create( |
| 412 | + resource=resource, |
| 413 | + version_number=new_version, |
| 414 | + change_description=input.description, |
| 415 | + ) |
| 416 | + |
| 417 | + # Update resource version field |
| 418 | + resource.version = new_version |
| 419 | + resource.save(update_fields=["version"]) |
| 420 | + |
| 421 | + logger.info(f"Created major version {new_version} for resource {resource.name}") |
| 422 | + return TypeResource.from_django(resource) |
0 commit comments