From a32e813dd787a2600f65040dafc505ae5f263516 Mon Sep 17 00:00:00 2001 From: Li Ma Date: Tue, 26 May 2026 09:24:06 -0700 Subject: [PATCH] release 0.1.24 --- kaggle_object_tests.py | 574 +++++++++++++++++ kagglesdk/__init__.py | 2 +- .../services/benchmark_tasks_api_service.py | 37 +- .../types/benchmark_tasks_api_service.py | 250 ++++++++ kagglesdk/benchmarks/types/benchmark_types.py | 46 ++ .../services/competition_api_service.py | 41 +- .../services/hackathon_api_service.py | 71 +++ .../types/competition_api_service.py | 190 ------ .../competitions/types/competition_enums.py | 12 +- kagglesdk/competitions/types/episode.py | 6 + .../types/hackathon_api_service.py | 578 ++++++++++++++++++ kagglesdk/competitions/types/hackathons.py | 2 +- .../services/discussions_api_service.py | 44 +- .../services/writeups_api_service.py | 19 + .../types/discussions_api_service.py | 384 ++++++++++++ kagglesdk/discussions/types/writeup_enums.py | 11 + kagglesdk/discussions/types/writeup_types.py | 293 ++++++++- .../discussions/types/writeups_api_service.py | 74 +++ kagglesdk/kaggle_client.py | 6 +- kagglesdk/kaggle_env.py | 7 - .../kernels/services/kernels_api_service.py | 26 +- .../kernels/types/kernels_api_service.py | 66 ++ sample.py | 131 ++++ test.py | 113 ++++ 24 files changed, 2732 insertions(+), 251 deletions(-) create mode 100644 kaggle_object_tests.py create mode 100644 kagglesdk/competitions/services/hackathon_api_service.py create mode 100644 kagglesdk/competitions/types/hackathon_api_service.py create mode 100644 kagglesdk/discussions/services/writeups_api_service.py create mode 100644 kagglesdk/discussions/types/writeups_api_service.py create mode 100644 sample.py create mode 100644 test.py diff --git a/kaggle_object_tests.py b/kaggle_object_tests.py new file mode 100644 index 0000000..40eacb3 --- /dev/null +++ b/kaggle_object_tests.py @@ -0,0 +1,574 @@ +import datetime +import unittest +from google.protobuf.field_mask_pb2 import FieldMask +from kagglesdk.common.types.common_types import Category +from kagglesdk.competitions.legacy.types.legacy_background_operation import ( + BackgroundOperation, +) +from kagglesdk.competitions.types.competition_service import ListCompetitionsRequest +from kagglesdk.datasets.data_viewer.types.dataviewer_service import ( + AndFilter, + BooleanFilter, + ConstantFilter, + ColumnFilter, + Filter, + NotFilter, + OrFilter, +) +from kagglesdk.kernels.types.kernels_enums import ( + AcceleratorType, + EditorType, + KernelVersionType, +) +from kagglesdk.kernels.types.kernels_service import KernelSession +from kagglesdk.kernels.types.kernels_types import ( + AccessBehavior, + GetKernelListDetailsRequest, +) + +Selector = ListCompetitionsRequest.Selector + + +class ListCompetitionsRequestTests(unittest.TestCase): + + def test_default_values(self): + req = ListCompetitionsRequest() + self.assertIsNone(req.selector) + self.assertEqual(0, req.page_size) + self.assertEqual("", req.page_token) + + selector = Selector() + self.assertEqual([], selector.competition_ids) + self.assertEqual(0, selector.list_for_user_id) + self.assertFalse("list_for_user_id" in selector) + self.assertEqual(0, selector.host_segment_id_filter) + self.assertFalse("host_segment_id_filter" in selector) + self.assertEqual([], selector.tag_ids) + self.assertFalse("tag_ids" in selector) + self.assertEqual(Selector.ListOption.LIST_OPTION_DEFAULT, selector.list_option) + self.assertFalse("list_option" in selector) + self.assertEqual(Selector.SortOption.SORT_OPTION_DEFAULT, selector.sort_option) + self.assertFalse("sort_option" in selector) + self.assertEqual( + Selector.PrestigeFilter.PRESTIGE_FILTER_UNSPECIFIED, + selector.prestige_filter, + ) + self.assertFalse("prestige_filter" in selector) + self.assertEqual( + Selector.VisibilityFilter.VISIBILITY_FILTER_UNSPECIFIED, + selector.visibility_filter, + ) + self.assertFalse("visibility_filter" in selector) + self.assertEqual( + Selector.ParticipationFilter.PARTICIPATION_FILTER_UNSPECIFIED, + selector.participation_filter, + ) + self.assertFalse("participation_filter" in selector) + self.assertEqual("", selector.search_query) + self.assertFalse("search_query" in selector) + self.assertFalse(selector.require_simulations) + self.assertFalse("require_simulations" in selector) + + def test_type_safety(self): + req = ListCompetitionsRequest() + + with self.assertRaises(Exception) as context: + req.page_size = "14" + self.assertEqual("page_size must be of type int", str(context.exception)) + + with self.assertRaises(Exception) as context: + req.page_token = 51 + self.assertEqual("page_token must be of type str", str(context.exception)) + + with self.assertRaises(Exception) as context: + req.selector = 21 + self.assertEqual( + "selector must be of type ListCompetitionsRequest.Selector", + str(context.exception), + ) + + with self.assertRaises(Exception) as context: + req.selector = Selector() + req.selector.list_option = "some value" + self.assertEqual( + "list_option must be of type ListCompetitionsRequest.Selector.ListOption", + str(context.exception), + ) + + with self.assertRaises(Exception) as context: + req.selector = Selector() + req.selector.competition_ids = "some value" + self.assertEqual("competition_ids must be of type list", str(context.exception)) + + with self.assertRaises(Exception) as context: + req.selector = Selector() + req.selector.competition_ids = ["some value", 12] + self.assertEqual( + "competition_ids must contain only items of type int", + str(context.exception), + ) + + def test_nonexisting_field(self): + req = ListCompetitionsRequest() + + with self.assertRaises(Exception) as context: + req.does_not_exist = 1 + self.assertEqual( + "Unknown field for ListCompetitionsRequest: does_not_exist", + str(context.exception), + ) + + with self.assertRaises(Exception) as context: + "does_not_exist" in req + self.assertEqual( + 'Protocol message ListCompetitionsRequest has no "does_not_exist" field.', + str(context.exception), + ) + + with self.assertRaises(Exception) as context: + del req.does_not_exist + self.assertEqual( + 'Protocol message ListCompetitionsRequest has no "does_not_exist" field.', + str(context.exception), + ) + + def test_clear_field(self): + req = ListCompetitionsRequest() + req.page_size = 10 + req.page_token = "some token" + req.selector = Selector() + + self.assertTrue("page_size" in req) + self.assertTrue("page_token" in req) + self.assertTrue("selector" in req) + + req.page_size = None + self.assertFalse("page_size" in req) + req.page_token = None + self.assertFalse("page_token" in req) + req.selector = None + self.assertFalse("selector" in req) + + def test_clear_field_with_del(self): + req = ListCompetitionsRequest() + req.page_size = 10 + req.page_token = "some token" + req.selector = Selector() + + self.assertTrue("page_size" in req) + self.assertTrue("page_token" in req) + self.assertTrue("selector" in req) + + del req.page_size + self.assertFalse("page_size" in req) + del req.page_token + self.assertFalse("page_token" in req) + del req.selector + self.assertFalse("selector" in req) + + def test_to_dict(self): + req = ListCompetitionsRequest() + req.page_size = 100 + req.selector = Selector() + req.selector.competition_ids.extend([12, 15]) + req.selector.list_option = Selector.ListOption.LIST_OPTION_USER_ENTERED + req.selector.sort_option = Selector.SortOption.SORT_OPTION_OLDEST + req.selector.visibility_filter = Selector.VisibilityFilter.VISIBILITY_FILTER_PUBLIC + req.selector.search_query = "some query" + + json_dict = ListCompetitionsRequest.to_dict(req) + + self.assertEqual(100, json_dict["pageSize"]) + self.assertEqual([12, 15], json_dict["selector"]["competitionIds"]) + self.assertEqual("LIST_OPTION_USER_ENTERED", json_dict["selector"]["listOption"]) + self.assertEqual("SORT_OPTION_OLDEST", json_dict["selector"]["sortOption"]) + self.assertEqual("VISIBILITY_FILTER_PUBLIC", json_dict["selector"]["visibilityFilter"]) + self.assertEqual("some query", json_dict["selector"]["searchQuery"]) + + def test_to_dict_empty(self): + json_dict = ListCompetitionsRequest.to_dict(ListCompetitionsRequest()) + self.assertEqual({}, json_dict) + + json_dict = ListCompetitionsRequest.to_dict(ListCompetitionsRequest(), ignore_defaults=False) + self.assertEqual(0, json_dict["pageSize"]) + self.assertEqual("", json_dict["pageToken"]) + self.assertIsNone(json_dict["selector"]) + + def test_from_dict(self): + req = ListCompetitionsRequest.from_dict( + { + "pageSize": 200, + "selector": { + "tagIds": [1, 3], + "hostSegmentIdFilter": 12, + "listOption": "LIST_OPTION_USER_HOSTED", + "sortOption": "SORT_OPTION_RECENTLY_CLOSED", + "participationFilter": "PARTICIPATION_FILTER_LIMITED", + "requireSimulations": True, + }, + } + ) + + self.assertEqual(200, req.page_size) + self.assertEqual("", req.page_token) + self.assertEqual([], req.selector.competition_ids) + self.assertEqual([1, 3], req.selector.tag_ids) + self.assertEqual(12, req.selector.host_segment_id_filter) + self.assertEqual(Selector.ListOption.LIST_OPTION_USER_HOSTED, req.selector.list_option) + self.assertEqual( + Selector.VisibilityFilter.VISIBILITY_FILTER_UNSPECIFIED, + req.selector.visibility_filter, + ) + self.assertEqual( + Selector.ParticipationFilter.PARTICIPATION_FILTER_LIMITED, + req.selector.participation_filter, + ) + self.assertEqual( + Selector.PrestigeFilter.PRESTIGE_FILTER_UNSPECIFIED, + req.selector.prestige_filter, + ) + self.assertEqual("", req.selector.search_query) + self.assertTrue(req.selector.require_simulations) + self.assertEqual(0, req.selector.list_for_user_id) + + +class GetKernelListDetailsRequestTests(unittest.TestCase): + + def test_default_values(self): + req = GetKernelListDetailsRequest() + self.assertEqual([], req.kernel_ids) + self.assertEqual([], req.output_file_types) + self.assertEqual(0, req.max_output_files_per_kernel) + self.assertEqual(0, req.gcs_timeout_ms) + self.assertIsNone(req.read_mask) + self.assertFalse(req.want_output_files) + self.assertFalse(req.exclude_results_files_outputs) + self.assertEqual(AccessBehavior.RETURN_SHELL_ENTRY, req.unauthorized_access_behavior) + self.assertEqual(AccessBehavior.RETURN_SHELL_ENTRY, req.deleted_access_behavior) + + def test_to_dict(self): + req = GetKernelListDetailsRequest() + req.kernel_ids = [101, 102, 103] + req.output_file_types = ["csv", "sqlite"] + req.want_output_files = True + req.read_mask = FieldMask() + req.read_mask.FromJsonString("author,id,dataSources") + + json_dict = GetKernelListDetailsRequest.to_dict(req) + + self.assertEqual([101, 102, 103], json_dict["kernelIds"]) + self.assertEqual(["csv", "sqlite"], json_dict["outputFileTypes"]) + self.assertTrue(json_dict["wantOutputFiles"]) + self.assertEqual("author,id,dataSources", json_dict["readMask"]) + + def test_to_dict_empty(self): + json_dict = GetKernelListDetailsRequest.to_dict(GetKernelListDetailsRequest()) + self.assertEqual({}, json_dict) + + json_dict = GetKernelListDetailsRequest.to_dict(GetKernelListDetailsRequest(), ignore_defaults=False) + self.assertEqual([], json_dict["kernelIds"]) + self.assertEqual([], json_dict["outputFileTypes"]) + self.assertIsNone(json_dict["readMask"]) + self.assertEqual("RETURN_SHELL_ENTRY", json_dict["unauthorizedAccessBehavior"]) + self.assertFalse(json_dict["wantOutputFiles"]) + + def test_from_dict(self): + req = GetKernelListDetailsRequest.from_dict( + { + "kernelIds": [1, 5, 6, 9], + "outputFileTypes": ["csv", "png"], + "unauthorizedAccessBehavior": "RETURN_NOTHING", + "maxOutputFilesPerKernel": 10, + "readMask": "author,id", + } + ) + + self.assertEqual([1, 5, 6, 9], req.kernel_ids) + self.assertEqual(["csv", "png"], req.output_file_types) + self.assertEqual(AccessBehavior.RETURN_NOTHING, req.unauthorized_access_behavior) + self.assertEqual(10, req.max_output_files_per_kernel) + self.assertEqual(["author", "id"], req.read_mask.paths) + self.assertEqual(AccessBehavior.RETURN_SHELL_ENTRY, req.deleted_access_behavior) + + +class KernelSessionTests(unittest.TestCase): + + def test_to_dict(self): + session = KernelSession() + session.kernel_id = 12 + session.kernel_run_id = 45 + session.type = KernelVersionType.BATCH + session.date_created = datetime.datetime.now() + session.accelerator = AcceleratorType.TPU_V3_8 + + json_dict = KernelSession.to_dict(session) + + self.assertEqual(12, json_dict["kernelId"]) + self.assertEqual(45, json_dict["kernelRunId"]) + self.assertEqual("BATCH", json_dict["type"]) + self.assertEqual( + session.date_created.isoformat(timespec="milliseconds") + "Z", + json_dict["dateCreated"], + ) + self.assertEqual("TPU_V3_8", json_dict["accelerator"]) + + def test_from_dict(self): + session = KernelSession.from_dict( + { + "kernelId": 15, + "kernelRunId": 13, + "title": "some kernel", + "type": "INTERACTIVE", + "sourceType": "EDITOR_TYPE_NOTEBOOK", + "accelerator": "GPU", + "languageName": "Python", + "versionName": "new version", + } + ) + + self.assertEqual(15, session.kernel_id) + self.assertEqual(13, session.kernel_run_id) + self.assertEqual("some kernel", session.title) + self.assertEqual(KernelVersionType.INTERACTIVE, session.type) + self.assertEqual(EditorType.EDITOR_TYPE_NOTEBOOK, session.source_type) + self.assertEqual(AcceleratorType.GPU, session.accelerator) + self.assertEqual("Python", session.language_name) + self.assertEqual("new version", session.version_name) + self.assertEqual(0, session.version_number) + self.assertIsNone(session.date_created) + + +class DataViewerFilterTests(unittest.TestCase): + + def test_to_dict(self): + constant1 = ConstantFilter() + constant1.value = True + constant2 = ConstantFilter() + constant2.value = False + filter1 = Filter() + filter1.constant_filter = constant1 + filter2 = Filter() + filter2.constant_filter = constant2 + filter = Filter() + filter.and_filter = AndFilter() + filter.and_filter.filters.append(filter1) + filter.and_filter.filters.append(filter2) + + json_dict = Filter.to_dict(filter) + + self.assertEqual(2, len(json_dict["andFilter"]["filters"])) + self.assertTrue(json_dict["andFilter"]["filters"][0]["constantFilter"]["value"]) + self.assertIsNone(json_dict["andFilter"]["filters"][1]["constantFilter"].get("value")) + + def test_from_dict(self): + filter = Filter.from_dict( + { + "orFilter": { + "filters": [ + { + "constantFilter": { + "value": False, + } + }, + { + "andFilter": { + "filters": [ + { + "columnFilter": { + "column": { + "firestorePath": "/foo/bar", + }, + "booleanFilter": { + "value": True, + }, + }, + }, + { + "constantFilter": { + "value": "True", + }, + }, + ], + }, + }, + ], + }, + } + ) + + self.assertIsNone(filter.and_filter) + self.assertIsNone(filter.not_filter) + self.assertIsNone(filter.column_filter) + self.assertIsNone(filter.constant_filter) + self.assertEqual(2, len(filter.or_filter.filters)) + self.assertFalse(filter.or_filter.filters[0].constant_filter.value) + self.assertIsNone(filter.or_filter.filters[0].and_filter) + self.assertIsNone(filter.or_filter.filters[0].or_filter) + self.assertEqual(2, len(filter.or_filter.filters[1].and_filter.filters)) + self.assertEqual( + "/foo/bar", + filter.or_filter.filters[1].and_filter.filters[0].column_filter.column.firestore_path, + ) + self.assertTrue(filter.or_filter.filters[1].and_filter.filters[0].column_filter.boolean_filter.value) + self.assertTrue(filter.or_filter.filters[1].and_filter.filters[1].constant_filter.value) + + def test_oneof_clears_others(self): + filter = Filter() + + filter.and_filter = AndFilter() + self.assertIsNotNone(filter.and_filter) + self.assertIsNone(filter.or_filter) + self.assertIsNone(filter.not_filter) + self.assertIsNone(filter.column_filter) + self.assertIsNone(filter.constant_filter) + + filter.constant_filter = ConstantFilter() + self.assertIsNone(filter.and_filter) + self.assertIsNone(filter.or_filter) + self.assertIsNone(filter.not_filter) + self.assertIsNone(filter.column_filter) + self.assertIsNotNone(filter.constant_filter) + + filter.column_filter = ColumnFilter() + self.assertIsNone(filter.and_filter) + self.assertIsNone(filter.or_filter) + self.assertIsNone(filter.not_filter) + self.assertIsNotNone(filter.column_filter) + self.assertIsNone(filter.constant_filter) + + filter.or_filter = OrFilter() + self.assertIsNone(filter.and_filter) + self.assertIsNotNone(filter.or_filter) + self.assertIsNone(filter.not_filter) + self.assertIsNone(filter.column_filter) + self.assertIsNone(filter.constant_filter) + + filter.not_filter = NotFilter() + self.assertIsNone(filter.and_filter) + self.assertIsNone(filter.or_filter) + self.assertIsNotNone(filter.not_filter) + self.assertIsNone(filter.column_filter) + self.assertIsNone(filter.constant_filter) + + +class CategoryTests(unittest.TestCase): + + def test_clear_field(self): + category = Category() + category.id = 10 + category.name = "gpu" + category.description = "GPU category" + + self.assertTrue("id" in category) + self.assertTrue("name" in category) + self.assertTrue("description" in category) + + category.id = None + self.assertFalse("id" in category) + category.name = None + self.assertFalse("name" in category) + category.description = None + self.assertFalse("description" in category) + category.description = "" + self.assertTrue("description" in category) + + def test_clear_field_with_del(self): + req = ListCompetitionsRequest() + req.page_size = 10 + req.page_token = "some token" + req.selector = Selector() + + self.assertTrue("page_size" in req) + self.assertTrue("page_token" in req) + self.assertTrue("selector" in req) + + del req.page_size + self.assertFalse("page_size" in req) + del req.page_token + self.assertFalse("page_token" in req) + del req.selector + self.assertFalse("selector" in req) + + def test_to_dict(self): + category = Category() + category.id = 12 + category.name = "gpu" + category.full_path = "/gpu" + category.competition_count = 45 + category.script_count = 0 + category.dataset_count = None + + json_dict = Category.to_dict(category) + + self.assertEqual(12, json_dict["id"]) + self.assertEqual("gpu", json_dict["name"]) + self.assertFalse("description" in json_dict) + self.assertEqual("/gpu", json_dict["fullPath"]) + self.assertEqual(45, json_dict["competitionCount"]) + self.assertFalse("datasetCount" in json_dict) + self.assertFalse("scriptCount" in json_dict) + self.assertFalse("totalCount" in json_dict) + + def test_from_dict(self): + category = Category.from_dict( + { + "id": 15, + "name": "gpu", + "fullPath": "", + "description": "GPU", + "datasetCount": 12, + "scriptCount": 21, + "competitionCount": 0, + } + ) + + self.assertEqual(15, category.id) + # Optional field explicitly set to non-default + self.assertEqual("gpu", category.name) + self.assertTrue("name" in category) + # Optional field explicitly set to default + self.assertEqual("", category.full_path) + self.assertTrue("full_path" in category) + # Optional field not set + self.assertEqual("", category.listing_url) + self.assertFalse("listing_url" in category) + # Optional field explicitly set to non-default + self.assertEqual("GPU", category.description) + self.assertTrue("description" in category) + # Optional field not set + self.assertFalse("", category.tag_url) + self.assertFalse("tag_url" in category) + # Non-optional field explicitly set to non-default + self.assertEqual(12, category.dataset_count) + self.assertTrue("dataset_count" in category) + # Non-optional field explicitly set to non-default + self.assertEqual(21, category.script_count) + self.assertTrue("script_count" in category) + # Non-optional field explicitly set to default + self.assertEqual(0, category.competition_count) + self.assertFalse("competition_count" in category) + # Non-optional field not set + self.assertEqual(0, category.total_count) + self.assertFalse("total_count" in category) + + +class BackgroundOperationTests(unittest.TestCase): + + def test_oneof(self): + operation = BackgroundOperation() + + operation.error = "some error" + self.assertTrue("error" in operation) + self.assertEqual("some error", operation.error) + self.assertFalse("progress" in operation) + self.assertEqual("", operation.progress) + + operation.progress = "90%" + self.assertFalse("error" in operation) + self.assertEqual("", operation.error) + self.assertTrue("progress" in operation) + self.assertEqual("90%", operation.progress) + + +if __name__ == "__main__": + unittest.main() diff --git a/kagglesdk/__init__.py b/kagglesdk/__init__.py index 7efcaab..7f7670c 100644 --- a/kagglesdk/__init__.py +++ b/kagglesdk/__init__.py @@ -1,4 +1,4 @@ -__version__ = "0.1.23" +__version__ = "0.1.24" from kagglesdk.kaggle_client import KaggleClient from kagglesdk.kaggle_creds import KaggleCredentials diff --git a/kagglesdk/benchmarks/services/benchmark_tasks_api_service.py b/kagglesdk/benchmarks/services/benchmark_tasks_api_service.py index 2a3562e..dc0125f 100644 --- a/kagglesdk/benchmarks/services/benchmark_tasks_api_service.py +++ b/kagglesdk/benchmarks/services/benchmark_tasks_api_service.py @@ -1,4 +1,4 @@ -from kagglesdk.benchmarks.types.benchmark_tasks_api_service import ApiBatchScheduleBenchmarkTaskRunsRequest, ApiBatchScheduleBenchmarkTaskRunsResponse, ApiBenchmarkTask, ApiCreateBenchmarkTaskRequest, ApiDownloadBenchmarkTaskRunOutputRequest, ApiGetBenchmarkTaskQuotaRequest, ApiGetBenchmarkTaskQuotaResponse, ApiGetBenchmarkTaskRequest, ApiListBenchmarkTaskRunsRequest, ApiListBenchmarkTaskRunsResponse, ApiListBenchmarkTasksRequest, ApiListBenchmarkTasksResponse +from kagglesdk.benchmarks.types.benchmark_tasks_api_service import ApiBatchScheduleBenchmarkTaskRunsRequest, ApiBatchScheduleBenchmarkTaskRunsResponse, ApiBenchmarkTask, ApiCreateBenchmarkTaskRequest, ApiDownloadBenchmarkTaskRunOutputRequest, ApiGetBenchmarkTaskQuotaRequest, ApiGetBenchmarkTaskQuotaResponse, ApiGetBenchmarkTaskRequest, ApiGetBenchmarkTaskRunLogsRequest, ApiListBenchmarkTaskRunsRequest, ApiListBenchmarkTaskRunsResponse, ApiListBenchmarkTasksRequest, ApiListBenchmarkTasksResponse, ApiPublishBenchmarkTaskRequest from kagglesdk.common.types.file_download import FileDownload from kagglesdk.kaggle_http_client import KaggleHttpClient @@ -93,6 +93,26 @@ def download_benchmark_task_run_output(self, request: ApiDownloadBenchmarkTaskRu return self._client.call("benchmarks.BenchmarkTasksApiService", "DownloadBenchmarkTaskRunOutput", request, FileDownload) + def get_benchmark_task_run_logs(self, request: ApiGetBenchmarkTaskRunLogsRequest = None) -> FileDownload: + r""" + Get execution logs for a task run. + + While the underlying notebook session is still running, the response is + a Server-Sent Events stream (Content-Type: text/event-stream). Once the + session has terminated, the persisted log file is returned as written by + the worker (Content-Type: application/json). Callers (e.g. the Kaggle + CLI) should branch on the response Content-Type. + + Args: + request (ApiGetBenchmarkTaskRunLogsRequest): + The request object; initialized to empty instance if not specified. + """ + + if request is None: + request = ApiGetBenchmarkTaskRunLogsRequest() + + return self._client.call("benchmarks.BenchmarkTasksApiService", "GetBenchmarkTaskRunLogs", request, FileDownload) + def get_benchmark_task_quota(self, request: ApiGetBenchmarkTaskQuotaRequest = None) -> ApiGetBenchmarkTaskQuotaResponse: r""" Return the current user's model proxy quota. @@ -106,3 +126,18 @@ def get_benchmark_task_quota(self, request: ApiGetBenchmarkTaskQuotaRequest = No request = ApiGetBenchmarkTaskQuotaRequest() return self._client.call("benchmarks.BenchmarkTasksApiService", "GetBenchmarkTaskQuota", request, ApiGetBenchmarkTaskQuotaResponse) + + def publish_benchmark_task(self, request: ApiPublishBenchmarkTaskRequest = None) -> ApiBenchmarkTask: + r""" + Publish a particular task for a user. Optionally publishes the backing + notebook (`source_kernel_id`) in the same request. + + Args: + request (ApiPublishBenchmarkTaskRequest): + The request object; initialized to empty instance if not specified. + """ + + if request is None: + request = ApiPublishBenchmarkTaskRequest() + + return self._client.call("benchmarks.BenchmarkTasksApiService", "PublishBenchmarkTask", request, ApiBenchmarkTask) diff --git a/kagglesdk/benchmarks/types/benchmark_tasks_api_service.py b/kagglesdk/benchmarks/types/benchmark_tasks_api_service.py index 31b1108..3be7612 100644 --- a/kagglesdk/benchmarks/types/benchmark_tasks_api_service.py +++ b/kagglesdk/benchmarks/types/benchmark_tasks_api_service.py @@ -1,6 +1,7 @@ from datetime import datetime from kagglesdk.benchmarks.types.benchmark_enums import BenchmarkTaskRunState, BenchmarkTaskVersionCreationState from kagglesdk.benchmarks.types.benchmark_task_run_service import BatchScheduleBenchmarkModelVersionResult +from kagglesdk.benchmarks.types.benchmark_types import BenchmarkTaskOptions from kagglesdk.kaggle_object import * from typing import List, Optional @@ -111,6 +112,18 @@ class ApiBenchmarkTask(KaggleObject): When this task version was created. creation_error_message (str) Error message from task version creation, if it failed. + is_public (bool) + Whether the task itself is public. + source_kernel_id (int) + ID of the backing notebook (kernel) associated with the task, if any. + is_backing_notebook_published (bool) + Whether the backing notebook (`source_kernel_id`) is published (public). + Null when the task has no backing notebook. + options (BenchmarkTaskOptions) + Options persisted on the task version (e.g. data sources). Echoed back + from CreateBenchmarkTask so callers can see what was attached, and + returned on reads so the CLI can display the currently-attached sources. + De-duplicated relative to the request input. """ def __init__(self): @@ -120,6 +133,10 @@ def __init__(self): self._creation_state = BenchmarkTaskVersionCreationState.BENCHMARK_TASK_VERSION_CREATION_STATE_UNSPECIFIED self._create_time = None self._creation_error_message = None + self._is_public = None + self._source_kernel_id = None + self._is_backing_notebook_published = None + self._options = None self._freeze() @property @@ -209,6 +226,70 @@ def creation_error_message(self, creation_error_message: Optional[str]): raise TypeError('creation_error_message must be of type str') self._creation_error_message = creation_error_message + @property + def is_public(self) -> bool: + """Whether the task itself is public.""" + return self._is_public or False + + @is_public.setter + def is_public(self, is_public: Optional[bool]): + if is_public is None: + del self.is_public + return + if not isinstance(is_public, bool): + raise TypeError('is_public must be of type bool') + self._is_public = is_public + + @property + def source_kernel_id(self) -> int: + """ID of the backing notebook (kernel) associated with the task, if any.""" + return self._source_kernel_id or 0 + + @source_kernel_id.setter + def source_kernel_id(self, source_kernel_id: Optional[int]): + if source_kernel_id is None: + del self.source_kernel_id + return + if not isinstance(source_kernel_id, int): + raise TypeError('source_kernel_id must be of type int') + self._source_kernel_id = source_kernel_id + + @property + def is_backing_notebook_published(self) -> bool: + r""" + Whether the backing notebook (`source_kernel_id`) is published (public). + Null when the task has no backing notebook. + """ + return self._is_backing_notebook_published or False + + @is_backing_notebook_published.setter + def is_backing_notebook_published(self, is_backing_notebook_published: Optional[bool]): + if is_backing_notebook_published is None: + del self.is_backing_notebook_published + return + if not isinstance(is_backing_notebook_published, bool): + raise TypeError('is_backing_notebook_published must be of type bool') + self._is_backing_notebook_published = is_backing_notebook_published + + @property + def options(self) -> Optional['BenchmarkTaskOptions']: + r""" + Options persisted on the task version (e.g. data sources). Echoed back + from CreateBenchmarkTask so callers can see what was attached, and + returned on reads so the CLI can display the currently-attached sources. + De-duplicated relative to the request input. + """ + return self._options or None + + @options.setter + def options(self, options: Optional[Optional['BenchmarkTaskOptions']]): + if options is None: + del self.options + return + if not isinstance(options, BenchmarkTaskOptions): + raise TypeError('options must be of type BenchmarkTaskOptions') + self._options = options + class ApiBenchmarkTaskRun(KaggleObject): r""" @@ -425,11 +506,23 @@ class ApiCreateBenchmarkTaskRequest(KaggleObject): decorator in the code. text (str) The task's (or task version's) source code + options (BenchmarkTaskOptions) + Optional task options (e.g. data sources) to attach to the underlying + notebook for this task version. Changing the options bumps the task + version. + + Semantics for omitted/empty fields: each sub-field within `options` is + applied as-given. Omitting `options` entirely on a new version is + equivalent to sending an empty `BenchmarkTaskOptions`, which clears + all previously-attached sources for that version. Callers that want to + preserve the previous version's options must echo them back on each + push. """ def __init__(self): self._slug = "" self._text = "" + self._options = None self._freeze() @property @@ -464,6 +557,31 @@ def text(self, text: str): raise TypeError('text must be of type str') self._text = text + @property + def options(self) -> Optional['BenchmarkTaskOptions']: + r""" + Optional task options (e.g. data sources) to attach to the underlying + notebook for this task version. Changing the options bumps the task + version. + + Semantics for omitted/empty fields: each sub-field within `options` is + applied as-given. Omitting `options` entirely on a new version is + equivalent to sending an empty `BenchmarkTaskOptions`, which clears + all previously-attached sources for that version. Callers that want to + preserve the previous version's options must echo them back on each + push. + """ + return self._options or None + + @options.setter + def options(self, options: Optional[Optional['BenchmarkTaskOptions']]): + if options is None: + del self.options + return + if not isinstance(options, BenchmarkTaskOptions): + raise TypeError('options must be of type BenchmarkTaskOptions') + self._options = options + def endpoint(self): path = '/api/v1/benchmarks/tasks/push' return path.format_map(self.to_field_map(self)) @@ -482,10 +600,15 @@ class ApiDownloadBenchmarkTaskRunOutputRequest(KaggleObject): r""" Attributes: run_id (int) + include_source (bool) + When true, the returned zip also contains the kernel session's source + files. For benchmark task runs (which are notebook kernels) that means + `__notebook__.ipynb` and `__notebook_source__.ipynb`. """ def __init__(self): self._run_id = 0 + self._include_source = False self._freeze() @property @@ -501,6 +624,24 @@ def run_id(self, run_id: int): raise TypeError('run_id must be of type int') self._run_id = run_id + @property + def include_source(self) -> bool: + r""" + When true, the returned zip also contains the kernel session's source + files. For benchmark task runs (which are notebook kernels) that means + `__notebook__.ipynb` and `__notebook_source__.ipynb`. + """ + return self._include_source + + @include_source.setter + def include_source(self, include_source: bool): + if include_source is None: + del self.include_source + return + if not isinstance(include_source, bool): + raise TypeError('include_source must be of type bool') + self._include_source = include_source + def endpoint(self): path = '/api/v1/benchmarks/tasks/runs/{run_id}/output' return path.format_map(self.to_field_map(self)) @@ -603,6 +744,38 @@ def endpoint_path(): return '/api/v1/benchmarks/tasks/{slug.task_slug}' +class ApiGetBenchmarkTaskRunLogsRequest(KaggleObject): + r""" + Attributes: + run_id (int) + """ + + def __init__(self): + self._run_id = 0 + self._freeze() + + @property + def run_id(self) -> int: + return self._run_id + + @run_id.setter + def run_id(self, run_id: int): + if run_id is None: + del self.run_id + return + if not isinstance(run_id, int): + raise TypeError('run_id must be of type int') + self._run_id = run_id + + def endpoint(self): + path = '/api/v1/benchmarks/tasks/runs/{run_id}/logs' + return path.format_map(self.to_field_map(self)) + + @staticmethod + def endpoint_path(): + return '/api/v1/benchmarks/tasks/runs/{run_id}/logs' + + class ApiListBenchmarkTaskRunsRequest(KaggleObject): r""" Attributes: @@ -923,6 +1096,68 @@ def nextPageToken(self): return self.next_page_token +class ApiPublishBenchmarkTaskRequest(KaggleObject): + r""" + Attributes: + slug (ApiBenchmarkTaskSlug) + The task to publish. + publish_backing_notebook (bool) + When true, also publishes the backing notebook (`source_kernel_id`) in + the same request. When false/unset, the backing notebook is left alone. + Unpublishing the notebook is not supported through this endpoint. + """ + + def __init__(self): + self._slug = None + self._publish_backing_notebook = False + self._freeze() + + @property + def slug(self) -> Optional['ApiBenchmarkTaskSlug']: + """The task to publish.""" + return self._slug + + @slug.setter + def slug(self, slug: Optional['ApiBenchmarkTaskSlug']): + if slug is None: + del self.slug + return + if not isinstance(slug, ApiBenchmarkTaskSlug): + raise TypeError('slug must be of type ApiBenchmarkTaskSlug') + self._slug = slug + + @property + def publish_backing_notebook(self) -> bool: + r""" + When true, also publishes the backing notebook (`source_kernel_id`) in + the same request. When false/unset, the backing notebook is left alone. + Unpublishing the notebook is not supported through this endpoint. + """ + return self._publish_backing_notebook + + @publish_backing_notebook.setter + def publish_backing_notebook(self, publish_backing_notebook: bool): + if publish_backing_notebook is None: + del self.publish_backing_notebook + return + if not isinstance(publish_backing_notebook, bool): + raise TypeError('publish_backing_notebook must be of type bool') + self._publish_backing_notebook = publish_backing_notebook + + def endpoint(self): + path = '/api/v1/benchmarks/tasks/{slug.task_slug}/publish' + return path.format_map(self.to_field_map(self)) + + + @staticmethod + def method(): + return 'POST' + + @staticmethod + def body_fields(): + return '*' + + ApiBatchScheduleBenchmarkTaskRunsRequest._fields = [ FieldMetadata("taskSlugs", "task_slugs", "_task_slugs", ApiBenchmarkTaskSlug, [], ListSerializer(KaggleObjectSerializer())), FieldMetadata("modelVersionSlugs", "model_version_slugs", "_model_version_slugs", str, [], ListSerializer(PredefinedSerializer())), @@ -939,6 +1174,10 @@ def nextPageToken(self): FieldMetadata("creationState", "creation_state", "_creation_state", BenchmarkTaskVersionCreationState, BenchmarkTaskVersionCreationState.BENCHMARK_TASK_VERSION_CREATION_STATE_UNSPECIFIED, EnumSerializer()), FieldMetadata("createTime", "create_time", "_create_time", datetime, None, DateTimeSerializer()), FieldMetadata("creationErrorMessage", "creation_error_message", "_creation_error_message", str, None, PredefinedSerializer(), optional=True), + FieldMetadata("isPublic", "is_public", "_is_public", bool, None, PredefinedSerializer(), optional=True), + FieldMetadata("sourceKernelId", "source_kernel_id", "_source_kernel_id", int, None, PredefinedSerializer(), optional=True), + FieldMetadata("isBackingNotebookPublished", "is_backing_notebook_published", "_is_backing_notebook_published", bool, None, PredefinedSerializer(), optional=True), + FieldMetadata("options", "options", "_options", BenchmarkTaskOptions, None, KaggleObjectSerializer(), optional=True), ] ApiBenchmarkTaskRun._fields = [ @@ -960,10 +1199,12 @@ def nextPageToken(self): ApiCreateBenchmarkTaskRequest._fields = [ FieldMetadata("slug", "slug", "_slug", str, "", PredefinedSerializer()), FieldMetadata("text", "text", "_text", str, "", PredefinedSerializer()), + FieldMetadata("options", "options", "_options", BenchmarkTaskOptions, None, KaggleObjectSerializer(), optional=True), ] ApiDownloadBenchmarkTaskRunOutputRequest._fields = [ FieldMetadata("runId", "run_id", "_run_id", int, 0, PredefinedSerializer()), + FieldMetadata("includeSource", "include_source", "_include_source", bool, False, PredefinedSerializer()), ] ApiGetBenchmarkTaskQuotaRequest._fields = [] @@ -977,6 +1218,10 @@ def nextPageToken(self): FieldMetadata("slug", "slug", "_slug", ApiBenchmarkTaskSlug, None, KaggleObjectSerializer()), ] +ApiGetBenchmarkTaskRunLogsRequest._fields = [ + FieldMetadata("runId", "run_id", "_run_id", int, 0, PredefinedSerializer()), +] + ApiListBenchmarkTaskRunsRequest._fields = [ FieldMetadata("taskSlug", "task_slug", "_task_slug", ApiBenchmarkTaskSlug, None, KaggleObjectSerializer()), FieldMetadata("modelVersionSlugs", "model_version_slugs", "_model_version_slugs", str, [], ListSerializer(PredefinedSerializer())), @@ -1005,3 +1250,8 @@ def nextPageToken(self): FieldMetadata("nextPageToken", "next_page_token", "_next_page_token", str, "", PredefinedSerializer()), ] +ApiPublishBenchmarkTaskRequest._fields = [ + FieldMetadata("slug", "slug", "_slug", ApiBenchmarkTaskSlug, None, KaggleObjectSerializer()), + FieldMetadata("publishBackingNotebook", "publish_backing_notebook", "_publish_backing_notebook", bool, False, PredefinedSerializer()), +] + diff --git a/kagglesdk/benchmarks/types/benchmark_types.py b/kagglesdk/benchmarks/types/benchmark_types.py index 5fae7fd..39292cc 100644 --- a/kagglesdk/benchmarks/types/benchmark_types.py +++ b/kagglesdk/benchmarks/types/benchmark_types.py @@ -662,6 +662,48 @@ def evaluation_date(self, evaluation_date: Optional[datetime]): self._evaluation_date = evaluation_date +class BenchmarkTaskOptions(KaggleObject): + r""" + Options persisted on a BenchmarkTaskVersion. Currently only carries + data-source attachments; more fields (model/competition/kernel sources, + compute knobs) will be added incrementally, so field numbers 2-20 are + reserved. Defined here (rather than under the API surface) because the + type is shared between the SDK read shape and the public API proto. + + Attributes: + dataset_data_sources (str) + Dataset data sources attached to the underlying notebook. Each entry is + '{owner}/{dataset-slug}'. The latest published version of the dataset is + attached; pinning to a specific version ('owner/dataset/versions/N') is + not yet supported. + """ + + def __init__(self): + self._dataset_data_sources = [] + self._freeze() + + @property + def dataset_data_sources(self) -> Optional[List[str]]: + r""" + Dataset data sources attached to the underlying notebook. Each entry is + '{owner}/{dataset-slug}'. The latest published version of the dataset is + attached; pinning to a specific version ('owner/dataset/versions/N') is + not yet supported. + """ + return self._dataset_data_sources + + @dataset_data_sources.setter + def dataset_data_sources(self, dataset_data_sources: Optional[List[str]]): + if dataset_data_sources is None: + del self.dataset_data_sources + return + if not isinstance(dataset_data_sources, list): + raise TypeError('dataset_data_sources must be of type list') + if not all([isinstance(t, str) for t in dataset_data_sources]): + raise TypeError('dataset_data_sources must contain only items of type str') + self._dataset_data_sources = dataset_data_sources + + class CustomResult(KaggleObject): r""" Attributes: @@ -852,6 +894,10 @@ def minus(self, minus: float): FieldMetadata("taskVersionId", "task_version_id", "_task_version_id", int, None, PredefinedSerializer(), optional=True), ] +BenchmarkTaskOptions._fields = [ + FieldMetadata("datasetDataSources", "dataset_data_sources", "_dataset_data_sources", str, [], ListSerializer(PredefinedSerializer())), +] + CustomResult._fields = [ FieldMetadata("key", "key", "_key", str, "", PredefinedSerializer()), FieldMetadata("value", "value", "_value", str, "", PredefinedSerializer()), diff --git a/kagglesdk/competitions/services/competition_api_service.py b/kagglesdk/competitions/services/competition_api_service.py index 1df4cdf..f50ac10 100644 --- a/kagglesdk/competitions/services/competition_api_service.py +++ b/kagglesdk/competitions/services/competition_api_service.py @@ -1,9 +1,6 @@ from kagglesdk.common.types.file_download import FileDownload from kagglesdk.common.types.http_redirect import HttpRedirect -from kagglesdk.competitions.types.competition_api_service import ApiCompetition, ApiCreateCodeSubmissionRequest, ApiCreateCodeSubmissionResponse, ApiCreateSubmissionRequest, ApiCreateSubmissionResponse, ApiDownloadDataFileRequest, ApiDownloadDataFilesRequest, ApiDownloadLeaderboardRequest, ApiGetCompetitionDataFilesSummaryRequest, ApiGetCompetitionRequest, ApiGetEpisodeAgentLogsRequest, ApiGetEpisodeReplayRequest, ApiGetHackathonOverviewRequest, ApiGetHackathonWriteUpRequest, ApiGetLeaderboardRequest, ApiGetLeaderboardResponse, ApiGetSubmissionRequest, ApiListCompetitionPagesRequest, ApiListCompetitionPagesResponse, ApiListCompetitionsRequest, ApiListCompetitionsResponse, ApiListCompetitionTopicsRequest, ApiListCompetitionTopicsResponse, ApiListDataFilesRequest, ApiListDataFilesResponse, ApiListDataTreeFilesRequest, ApiListHackathonWriteUpsRequest, ApiListSubmissionEpisodesRequest, ApiListSubmissionEpisodesResponse, ApiListSubmissionsRequest, ApiListSubmissionsResponse, ApiListTopicMessagesRequest, ApiListTopicMessagesResponse, ApiStartSubmissionUploadRequest, ApiStartSubmissionUploadResponse, ApiSubmission -from kagglesdk.competitions.types.hackathon_service import ListHackathonWriteUpsResponse -from kagglesdk.competitions.types.hackathons import HackathonWriteUp -from kagglesdk.competitions.types.page_service import ListPagesResponse +from kagglesdk.competitions.types.competition_api_service import ApiCompetition, ApiCreateCodeSubmissionRequest, ApiCreateCodeSubmissionResponse, ApiCreateSubmissionRequest, ApiCreateSubmissionResponse, ApiDownloadDataFileRequest, ApiDownloadDataFilesRequest, ApiDownloadLeaderboardRequest, ApiGetCompetitionDataFilesSummaryRequest, ApiGetCompetitionRequest, ApiGetEpisodeAgentLogsRequest, ApiGetEpisodeReplayRequest, ApiGetLeaderboardRequest, ApiGetLeaderboardResponse, ApiGetSubmissionRequest, ApiListCompetitionPagesRequest, ApiListCompetitionPagesResponse, ApiListCompetitionsRequest, ApiListCompetitionsResponse, ApiListCompetitionTopicsRequest, ApiListCompetitionTopicsResponse, ApiListDataFilesRequest, ApiListDataFilesResponse, ApiListDataTreeFilesRequest, ApiListSubmissionEpisodesRequest, ApiListSubmissionEpisodesResponse, ApiListSubmissionsRequest, ApiListSubmissionsResponse, ApiListTopicMessagesRequest, ApiListTopicMessagesResponse, ApiStartSubmissionUploadRequest, ApiStartSubmissionUploadResponse, ApiSubmission from kagglesdk.datasets.databundles.types.databundle_api_types import ApiDirectoryContent, ApiFilesSummary from kagglesdk.kaggle_http_client import KaggleHttpClient @@ -180,30 +177,6 @@ def get_competition_data_files_summary(self, request: ApiGetCompetitionDataFiles return self._client.call("competitions.CompetitionApiService", "GetCompetitionDataFilesSummary", request, ApiFilesSummary) - def list_hackathon_write_ups(self, request: ApiListHackathonWriteUpsRequest = None) -> ListHackathonWriteUpsResponse: - r""" - Args: - request (ApiListHackathonWriteUpsRequest): - The request object; initialized to empty instance if not specified. - """ - - if request is None: - request = ApiListHackathonWriteUpsRequest() - - return self._client.call("competitions.CompetitionApiService", "ListHackathonWriteUps", request, ListHackathonWriteUpsResponse) - - def get_hackathon_write_up(self, request: ApiGetHackathonWriteUpRequest = None) -> HackathonWriteUp: - r""" - Args: - request (ApiGetHackathonWriteUpRequest): - The request object; initialized to empty instance if not specified. - """ - - if request is None: - request = ApiGetHackathonWriteUpRequest() - - return self._client.call("competitions.CompetitionApiService", "GetHackathonWriteUp", request, HackathonWriteUp) - def list_submission_episodes(self, request: ApiListSubmissionEpisodesRequest = None) -> ApiListSubmissionEpisodesResponse: r""" Args: @@ -252,18 +225,6 @@ def list_competition_pages(self, request: ApiListCompetitionPagesRequest = None) return self._client.call("competitions.CompetitionApiService", "ListCompetitionPages", request, ApiListCompetitionPagesResponse) - def get_hackathon_overview(self, request: ApiGetHackathonOverviewRequest = None) -> ListPagesResponse: - r""" - Args: - request (ApiGetHackathonOverviewRequest): - The request object; initialized to empty instance if not specified. - """ - - if request is None: - request = ApiGetHackathonOverviewRequest() - - return self._client.call("competitions.CompetitionApiService", "GetHackathonOverview", request, ListPagesResponse) - def list_competition_topics(self, request: ApiListCompetitionTopicsRequest = None) -> ApiListCompetitionTopicsResponse: r""" Args: diff --git a/kagglesdk/competitions/services/hackathon_api_service.py b/kagglesdk/competitions/services/hackathon_api_service.py new file mode 100644 index 0000000..1464053 --- /dev/null +++ b/kagglesdk/competitions/services/hackathon_api_service.py @@ -0,0 +1,71 @@ +from kagglesdk.competitions.types.hackathon_api_service import ApiExportHackathonWriteUpsCsvRequest, ApiExportHackathonWriteUpsCsvResponse, ApiGetHackathonOverviewRequest, ApiGetHackathonOverviewResponse, ApiGetHackathonWriteUpRequest, ApiListHackathonTracksRequest, ApiListHackathonTracksResponse, ApiListHackathonWriteUpsRequest, ApiListHackathonWriteUpsResponse +from kagglesdk.competitions.types.hackathons import HackathonWriteUp +from kagglesdk.kaggle_http_client import KaggleHttpClient + +class HackathonApiClient(object): + + def __init__(self, client: KaggleHttpClient): + self._client = client + + def get_hackathon_write_up(self, request: ApiGetHackathonWriteUpRequest = None) -> HackathonWriteUp: + r""" + Args: + request (ApiGetHackathonWriteUpRequest): + The request object; initialized to empty instance if not specified. + """ + + if request is None: + request = ApiGetHackathonWriteUpRequest() + + return self._client.call("competitions.HackathonApiService", "GetHackathonWriteUp", request, HackathonWriteUp) + + def list_hackathon_write_ups(self, request: ApiListHackathonWriteUpsRequest = None) -> ApiListHackathonWriteUpsResponse: + r""" + Args: + request (ApiListHackathonWriteUpsRequest): + The request object; initialized to empty instance if not specified. + """ + + if request is None: + request = ApiListHackathonWriteUpsRequest() + + return self._client.call("competitions.HackathonApiService", "ListHackathonWriteUps", request, ApiListHackathonWriteUpsResponse) + + def list_hackathon_tracks(self, request: ApiListHackathonTracksRequest = None) -> ApiListHackathonTracksResponse: + r""" + Args: + request (ApiListHackathonTracksRequest): + The request object; initialized to empty instance if not specified. + """ + + if request is None: + request = ApiListHackathonTracksRequest() + + return self._client.call("competitions.HackathonApiService", "ListHackathonTracks", request, ApiListHackathonTracksResponse) + + def export_hackathon_write_ups_csv(self, request: ApiExportHackathonWriteUpsCsvRequest = None) -> ApiExportHackathonWriteUpsCsvResponse: + r""" + Exports a CSV with the data from all submitted writeups after the + hackathon has closed. Returns plain CSV text for use by AI agents via MCP. + + Args: + request (ApiExportHackathonWriteUpsCsvRequest): + The request object; initialized to empty instance if not specified. + """ + + if request is None: + request = ApiExportHackathonWriteUpsCsvRequest() + + return self._client.call("competitions.HackathonApiService", "ExportHackathonWriteUpsCsv", request, ApiExportHackathonWriteUpsCsvResponse) + + def get_hackathon_overview(self, request: ApiGetHackathonOverviewRequest = None) -> ApiGetHackathonOverviewResponse: + r""" + Args: + request (ApiGetHackathonOverviewRequest): + The request object; initialized to empty instance if not specified. + """ + + if request is None: + request = ApiGetHackathonOverviewRequest() + + return self._client.call("competitions.HackathonApiService", "GetHackathonOverview", request, ApiGetHackathonOverviewResponse) diff --git a/kagglesdk/competitions/types/competition_api_service.py b/kagglesdk/competitions/types/competition_api_service.py index 5b7f8e4..141da86 100644 --- a/kagglesdk/competitions/types/competition_api_service.py +++ b/kagglesdk/competitions/types/competition_api_service.py @@ -1608,85 +1608,6 @@ def endpoint_path(): return '/api/v1/competitions/episodes/{episode_id}/replay' -class ApiGetHackathonOverviewRequest(KaggleObject): - r""" - Attributes: - competition_name (str) - """ - - def __init__(self): - self._competition_name = "" - self._freeze() - - @property - def competition_name(self) -> str: - return self._competition_name - - @competition_name.setter - def competition_name(self, competition_name: str): - if competition_name is None: - del self.competition_name - return - if not isinstance(competition_name, str): - raise TypeError('competition_name must be of type str') - self._competition_name = competition_name - - def endpoint(self): - path = '/api/v1/competitions/{competition_name}/hackathon-overview' - return path.format_map(self.to_field_map(self)) - - @staticmethod - def endpoint_path(): - return '/api/v1/competitions/{competition_name}/hackathon-overview' - - -class ApiGetHackathonWriteUpRequest(KaggleObject): - r""" - Attributes: - competition_name (str) - hackathon_write_up_id (int) - """ - - def __init__(self): - self._competition_name = "" - self._hackathon_write_up_id = 0 - self._freeze() - - @property - def competition_name(self) -> str: - return self._competition_name - - @competition_name.setter - def competition_name(self, competition_name: str): - if competition_name is None: - del self.competition_name - return - if not isinstance(competition_name, str): - raise TypeError('competition_name must be of type str') - self._competition_name = competition_name - - @property - def hackathon_write_up_id(self) -> int: - return self._hackathon_write_up_id - - @hackathon_write_up_id.setter - def hackathon_write_up_id(self, hackathon_write_up_id: int): - if hackathon_write_up_id is None: - del self.hackathon_write_up_id - return - if not isinstance(hackathon_write_up_id, int): - raise TypeError('hackathon_write_up_id must be of type int') - self._hackathon_write_up_id = hackathon_write_up_id - - def endpoint(self): - path = '/api/v1/competitions/{competition_name}/hackathon-write-ups/{hackathon_write_up_id}' - return path.format_map(self.to_field_map(self)) - - @staticmethod - def endpoint_path(): - return '/api/v1/competitions/{competition_name}/hackathon-write-ups/{hackathon_write_up_id}' - - class ApiGetLeaderboardRequest(KaggleObject): r""" Attributes: @@ -2515,100 +2436,6 @@ def endpoint_path(): return '/api/v1/competitions/{competition_name}/data-tree/list/' -class ApiListHackathonWriteUpsRequest(KaggleObject): - r""" - Attributes: - competition_name (str) - hackathon_track_ids (int) - winner (bool) - page_size (int) - page_token (str) - """ - - def __init__(self): - self._competition_name = "" - self._hackathon_track_ids = [] - self._winner = None - self._page_size = None - self._page_token = None - self._freeze() - - @property - def competition_name(self) -> str: - return self._competition_name - - @competition_name.setter - def competition_name(self, competition_name: str): - if competition_name is None: - del self.competition_name - return - if not isinstance(competition_name, str): - raise TypeError('competition_name must be of type str') - self._competition_name = competition_name - - @property - def hackathon_track_ids(self) -> Optional[List[int]]: - return self._hackathon_track_ids - - @hackathon_track_ids.setter - def hackathon_track_ids(self, hackathon_track_ids: Optional[List[int]]): - if hackathon_track_ids is None: - del self.hackathon_track_ids - return - if not isinstance(hackathon_track_ids, list): - raise TypeError('hackathon_track_ids must be of type list') - if not all([isinstance(t, int) for t in hackathon_track_ids]): - raise TypeError('hackathon_track_ids must contain only items of type int') - self._hackathon_track_ids = hackathon_track_ids - - @property - def winner(self) -> bool: - return self._winner or False - - @winner.setter - def winner(self, winner: Optional[bool]): - if winner is None: - del self.winner - return - if not isinstance(winner, bool): - raise TypeError('winner must be of type bool') - self._winner = winner - - @property - def page_size(self) -> int: - return self._page_size or 0 - - @page_size.setter - def page_size(self, page_size: Optional[int]): - if page_size is None: - del self.page_size - return - if not isinstance(page_size, int): - raise TypeError('page_size must be of type int') - self._page_size = page_size - - @property - def page_token(self) -> str: - return self._page_token or "" - - @page_token.setter - def page_token(self, page_token: Optional[str]): - if page_token is None: - del self.page_token - return - if not isinstance(page_token, str): - raise TypeError('page_token must be of type str') - self._page_token = page_token - - def endpoint(self): - path = '/api/v1/competitions/{competition_name}/hackathon-write-ups' - return path.format_map(self.to_field_map(self)) - - @staticmethod - def endpoint_path(): - return '/api/v1/competitions/{competition_name}/hackathon-write-ups' - - class ApiListSubmissionEpisodesRequest(KaggleObject): r""" Attributes: @@ -3560,15 +3387,6 @@ def replies(self, replies: Optional[List[Optional['ApiTopicMessage']]]): FieldMetadata("episodeId", "episode_id", "_episode_id", int, 0, PredefinedSerializer()), ] -ApiGetHackathonOverviewRequest._fields = [ - FieldMetadata("competitionName", "competition_name", "_competition_name", str, "", PredefinedSerializer()), -] - -ApiGetHackathonWriteUpRequest._fields = [ - FieldMetadata("competitionName", "competition_name", "_competition_name", str, "", PredefinedSerializer()), - FieldMetadata("hackathonWriteUpId", "hackathon_write_up_id", "_hackathon_write_up_id", int, 0, PredefinedSerializer()), -] - ApiGetLeaderboardRequest._fields = [ FieldMetadata("competitionName", "competition_name", "_competition_name", str, "", PredefinedSerializer()), FieldMetadata("overridePublic", "override_public", "_override_public", bool, None, PredefinedSerializer(), optional=True), @@ -3646,14 +3464,6 @@ def replies(self, replies: Optional[List[Optional['ApiTopicMessage']]]): FieldMetadata("pageToken", "page_token", "_page_token", str, None, PredefinedSerializer(), optional=True), ] -ApiListHackathonWriteUpsRequest._fields = [ - FieldMetadata("competitionName", "competition_name", "_competition_name", str, "", PredefinedSerializer()), - FieldMetadata("hackathonTrackIds", "hackathon_track_ids", "_hackathon_track_ids", int, [], ListSerializer(PredefinedSerializer())), - FieldMetadata("winner", "winner", "_winner", bool, None, PredefinedSerializer(), optional=True), - FieldMetadata("pageSize", "page_size", "_page_size", int, None, PredefinedSerializer(), optional=True), - FieldMetadata("pageToken", "page_token", "_page_token", str, None, PredefinedSerializer(), optional=True), -] - ApiListSubmissionEpisodesRequest._fields = [ FieldMetadata("submissionId", "submission_id", "_submission_id", int, 0, PredefinedSerializer()), ] diff --git a/kagglesdk/competitions/types/competition_enums.py b/kagglesdk/competitions/types/competition_enums.py index 704f573..7531f99 100644 --- a/kagglesdk/competitions/types/competition_enums.py +++ b/kagglesdk/competitions/types/competition_enums.py @@ -21,6 +21,12 @@ class CompetitionSortBy(enum.Enum): COMPETITION_SORT_BY_RELEVANCE = 6 COMPETITION_SORT_BY_RECENTLY_CREATED = 7 +class HackathonTrackPrizeType(enum.Enum): + HACKATHON_TRACK_PRIZE_TYPE_UNSPECIFIED = 0 + HACKATHON_TRACK_PRIZE_TYPE_KUDOS = 1 + HACKATHON_TRACK_PRIZE_TYPE_NON_MONETARY = 2 + HACKATHON_TRACK_PRIZE_TYPE_MONETARY = 3 + class HostSegment(enum.Enum): r""" NOTE: Keep in Sync with Kaggle.Entities.HostSegment until migrated! Also keep @@ -51,9 +57,3 @@ class SubmissionSortBy(enum.Enum): SUBMISSION_SORT_BY_PRIVATE_SCORE = 2 SUBMISSION_SORT_BY_PUBLIC_SCORE = 3 -class HackathonTrackPrizeType(enum.Enum): - HACKATHON_TRACK_PRIZE_TYPE_UNSPECIFIED = 0 - HACKATHON_TRACK_PRIZE_TYPE_KUDOS = 1 - HACKATHON_TRACK_PRIZE_TYPE_NON_MONETARY = 2 - HACKATHON_TRACK_PRIZE_TYPE_MONETARY = 3 - diff --git a/kagglesdk/competitions/types/episode.py b/kagglesdk/competitions/types/episode.py index f18a378..18a7261 100644 --- a/kagglesdk/competitions/types/episode.py +++ b/kagglesdk/competitions/types/episode.py @@ -29,5 +29,11 @@ class EpisodeType(enum.Enum): EPISODE_TYPE_PRIVATE = 2 """This is deprecated / was never supported.""" EPISODE_TYPE_EXHIBITION = 3 + r""" + Used exclusively for 'canary' episodes generated by GenerateEpisodesScheduledHandler + for Game Arena leaderboard competitions. These episodes do not affect leaderboard + scores and are used to validate agent behavior and provide coverage across + eligible submissions. + """ EPISODE_TYPE_VALIDATION = 4 diff --git a/kagglesdk/competitions/types/hackathon_api_service.py b/kagglesdk/competitions/types/hackathon_api_service.py new file mode 100644 index 0000000..bee77ef --- /dev/null +++ b/kagglesdk/competitions/types/hackathon_api_service.py @@ -0,0 +1,578 @@ +from google.protobuf.field_mask_pb2 import FieldMask +from kagglesdk.competitions.types.hackathons import HackathonTrack, HackathonWriteUp +from kagglesdk.competitions.types.page import Page +from kagglesdk.kaggle_object import * +from typing import Optional, List + +class ApiExportHackathonWriteUpsCsvRequest(KaggleObject): + r""" + Attributes: + competition_id (int) + competition_name (str) + """ + + def __init__(self): + self._competition_id = None + self._competition_name = None + self._freeze() + + @property + def competition_id(self) -> int: + return self._competition_id or 0 + + @competition_id.setter + def competition_id(self, competition_id: Optional[int]): + if competition_id is None: + del self.competition_id + return + if not isinstance(competition_id, int): + raise TypeError('competition_id must be of type int') + self._competition_id = competition_id + + @property + def competition_name(self) -> str: + return self._competition_name or "" + + @competition_name.setter + def competition_name(self, competition_name: Optional[str]): + if competition_name is None: + del self.competition_name + return + if not isinstance(competition_name, str): + raise TypeError('competition_name must be of type str') + self._competition_name = competition_name + + def endpoint(self): + path = '/api/v1/competitions/{competition_name}/hackathon-write-ups-csv' + return path.format_map(self.to_field_map(self)) + + @staticmethod + def endpoint_path(): + return '/api/v1/competitions/{competition_name}/hackathon-write-ups-csv' + + +class ApiExportHackathonWriteUpsCsvResponse(KaggleObject): + r""" + Attributes: + csv_content (str) + """ + + def __init__(self): + self._csv_content = "" + self._freeze() + + @property + def csv_content(self) -> str: + return self._csv_content + + @csv_content.setter + def csv_content(self, csv_content: str): + if csv_content is None: + del self.csv_content + return + if not isinstance(csv_content, str): + raise TypeError('csv_content must be of type str') + self._csv_content = csv_content + + @property + def csvContent(self): + return self.csv_content + + +class ApiGetHackathonOverviewRequest(KaggleObject): + r""" + Attributes: + competition_name (str) + """ + + def __init__(self): + self._competition_name = "" + self._freeze() + + @property + def competition_name(self) -> str: + return self._competition_name + + @competition_name.setter + def competition_name(self, competition_name: str): + if competition_name is None: + del self.competition_name + return + if not isinstance(competition_name, str): + raise TypeError('competition_name must be of type str') + self._competition_name = competition_name + + def endpoint(self): + path = '/api/v1/competitions/{competition_name}/hackathon-overview' + return path.format_map(self.to_field_map(self)) + + @staticmethod + def endpoint_path(): + return '/api/v1/competitions/{competition_name}/hackathon-overview' + + +class ApiGetHackathonOverviewResponse(KaggleObject): + r""" + Attributes: + pages (Page) + """ + + def __init__(self): + self._pages = [] + self._freeze() + + @property + def pages(self) -> Optional[List[Optional['Page']]]: + return self._pages + + @pages.setter + def pages(self, pages: Optional[List[Optional['Page']]]): + if pages is None: + del self.pages + return + if not isinstance(pages, list): + raise TypeError('pages must be of type list') + if not all([isinstance(t, Page) for t in pages]): + raise TypeError('pages must contain only items of type Page') + self._pages = pages + + +class ApiGetHackathonWriteUpRequest(KaggleObject): + r""" + Attributes: + competition_name (str) + hackathon_write_up_id (int) + """ + + def __init__(self): + self._competition_name = "" + self._hackathon_write_up_id = 0 + self._freeze() + + @property + def competition_name(self) -> str: + return self._competition_name + + @competition_name.setter + def competition_name(self, competition_name: str): + if competition_name is None: + del self.competition_name + return + if not isinstance(competition_name, str): + raise TypeError('competition_name must be of type str') + self._competition_name = competition_name + + @property + def hackathon_write_up_id(self) -> int: + return self._hackathon_write_up_id + + @hackathon_write_up_id.setter + def hackathon_write_up_id(self, hackathon_write_up_id: int): + if hackathon_write_up_id is None: + del self.hackathon_write_up_id + return + if not isinstance(hackathon_write_up_id, int): + raise TypeError('hackathon_write_up_id must be of type int') + self._hackathon_write_up_id = hackathon_write_up_id + + def endpoint(self): + path = '/api/v1/competitions/{competition_name}/hackathon-write-ups/{hackathon_write_up_id}' + return path.format_map(self.to_field_map(self)) + + @staticmethod + def endpoint_path(): + return '/api/v1/competitions/{competition_name}/hackathon-write-ups/{hackathon_write_up_id}' + + +class ApiListHackathonTracksRequest(KaggleObject): + r""" + Attributes: + competition_name (str) + """ + + def __init__(self): + self._competition_name = "" + self._freeze() + + @property + def competition_name(self) -> str: + return self._competition_name + + @competition_name.setter + def competition_name(self, competition_name: str): + if competition_name is None: + del self.competition_name + return + if not isinstance(competition_name, str): + raise TypeError('competition_name must be of type str') + self._competition_name = competition_name + + def endpoint(self): + path = '/api/v1/competitions/{competition_name}/hackathon-tracks' + return path.format_map(self.to_field_map(self)) + + @staticmethod + def endpoint_path(): + return '/api/v1/competitions/{competition_name}/hackathon-tracks' + + +class ApiListHackathonTracksResponse(KaggleObject): + r""" + Attributes: + tracks (HackathonTrack) + """ + + def __init__(self): + self._tracks = [] + self._freeze() + + @property + def tracks(self) -> Optional[List[Optional['HackathonTrack']]]: + return self._tracks + + @tracks.setter + def tracks(self, tracks: Optional[List[Optional['HackathonTrack']]]): + if tracks is None: + del self.tracks + return + if not isinstance(tracks, list): + raise TypeError('tracks must be of type list') + if not all([isinstance(t, HackathonTrack) for t in tracks]): + raise TypeError('tracks must contain only items of type HackathonTrack') + self._tracks = tracks + + +class ApiListHackathonWriteUpsRequest(KaggleObject): + r""" + Attributes: + competition_id (int) + team_id (int) + optionally filter responses to this team id + hackathon_track_ids (int) + optionally filter responses to these track ids + published (bool) + if requesting for your team, you can request to include drafts + unset = draft/publish, false = draft, true = published + winner (bool) + unset = winners/non-winners false = no winners, true = winners + template (bool) + unset = template & non-template, false = no templates, true = templates + page_size (int) + page_token (str) + skip (int) + read_mask (FieldMask) + host_or_judge (bool) + optionally filter responses to those owned by hosts or judges + competition_name (str) + """ + + def __init__(self): + self._competition_id = None + self._team_id = None + self._hackathon_track_ids = [] + self._published = None + self._winner = None + self._template = None + self._page_size = None + self._page_token = None + self._skip = None + self._read_mask = None + self._host_or_judge = None + self._competition_name = None + self._freeze() + + @property + def competition_id(self) -> int: + return self._competition_id or 0 + + @competition_id.setter + def competition_id(self, competition_id: Optional[int]): + if competition_id is None: + del self.competition_id + return + if not isinstance(competition_id, int): + raise TypeError('competition_id must be of type int') + self._competition_id = competition_id + + @property + def team_id(self) -> int: + """optionally filter responses to this team id""" + return self._team_id or 0 + + @team_id.setter + def team_id(self, team_id: Optional[int]): + if team_id is None: + del self.team_id + return + if not isinstance(team_id, int): + raise TypeError('team_id must be of type int') + self._team_id = team_id + + @property + def hackathon_track_ids(self) -> Optional[List[int]]: + """optionally filter responses to these track ids""" + return self._hackathon_track_ids + + @hackathon_track_ids.setter + def hackathon_track_ids(self, hackathon_track_ids: Optional[List[int]]): + if hackathon_track_ids is None: + del self.hackathon_track_ids + return + if not isinstance(hackathon_track_ids, list): + raise TypeError('hackathon_track_ids must be of type list') + if not all([isinstance(t, int) for t in hackathon_track_ids]): + raise TypeError('hackathon_track_ids must contain only items of type int') + self._hackathon_track_ids = hackathon_track_ids + + @property + def published(self) -> bool: + r""" + if requesting for your team, you can request to include drafts + unset = draft/publish, false = draft, true = published + """ + return self._published or False + + @published.setter + def published(self, published: Optional[bool]): + if published is None: + del self.published + return + if not isinstance(published, bool): + raise TypeError('published must be of type bool') + self._published = published + + @property + def winner(self) -> bool: + """unset = winners/non-winners false = no winners, true = winners""" + return self._winner or False + + @winner.setter + def winner(self, winner: Optional[bool]): + if winner is None: + del self.winner + return + if not isinstance(winner, bool): + raise TypeError('winner must be of type bool') + self._winner = winner + + @property + def template(self) -> bool: + """unset = template & non-template, false = no templates, true = templates""" + return self._template or False + + @template.setter + def template(self, template: Optional[bool]): + if template is None: + del self.template + return + if not isinstance(template, bool): + raise TypeError('template must be of type bool') + self._template = template + + @property + def page_size(self) -> int: + return self._page_size or 0 + + @page_size.setter + def page_size(self, page_size: Optional[int]): + if page_size is None: + del self.page_size + return + if not isinstance(page_size, int): + raise TypeError('page_size must be of type int') + self._page_size = page_size + + @property + def page_token(self) -> str: + return self._page_token or "" + + @page_token.setter + def page_token(self, page_token: Optional[str]): + if page_token is None: + del self.page_token + return + if not isinstance(page_token, str): + raise TypeError('page_token must be of type str') + self._page_token = page_token + + @property + def skip(self) -> int: + return self._skip or 0 + + @skip.setter + def skip(self, skip: Optional[int]): + if skip is None: + del self.skip + return + if not isinstance(skip, int): + raise TypeError('skip must be of type int') + self._skip = skip + + @property + def read_mask(self) -> FieldMask: + return self._read_mask + + @read_mask.setter + def read_mask(self, read_mask: FieldMask): + if read_mask is None: + del self.read_mask + return + if not isinstance(read_mask, FieldMask): + raise TypeError('read_mask must be of type FieldMask') + self._read_mask = read_mask + + @property + def host_or_judge(self) -> bool: + """optionally filter responses to those owned by hosts or judges""" + return self._host_or_judge or False + + @host_or_judge.setter + def host_or_judge(self, host_or_judge: Optional[bool]): + if host_or_judge is None: + del self.host_or_judge + return + if not isinstance(host_or_judge, bool): + raise TypeError('host_or_judge must be of type bool') + self._host_or_judge = host_or_judge + + @property + def competition_name(self) -> str: + return self._competition_name or "" + + @competition_name.setter + def competition_name(self, competition_name: Optional[str]): + if competition_name is None: + del self.competition_name + return + if not isinstance(competition_name, str): + raise TypeError('competition_name must be of type str') + self._competition_name = competition_name + + def endpoint(self): + path = '/api/v1/competitions/{competition_name}/hackathon-write-ups' + return path.format_map(self.to_field_map(self)) + + @staticmethod + def endpoint_path(): + return '/api/v1/competitions/{competition_name}/hackathon-write-ups' + + +class ApiListHackathonWriteUpsResponse(KaggleObject): + r""" + Attributes: + hackathon_write_ups (HackathonWriteUp) + next_page_token (str) + total_count (int) + """ + + def __init__(self): + self._hackathon_write_ups = [] + self._next_page_token = "" + self._total_count = 0 + self._freeze() + + @property + def hackathon_write_ups(self) -> Optional[List[Optional['HackathonWriteUp']]]: + return self._hackathon_write_ups + + @hackathon_write_ups.setter + def hackathon_write_ups(self, hackathon_write_ups: Optional[List[Optional['HackathonWriteUp']]]): + if hackathon_write_ups is None: + del self.hackathon_write_ups + return + if not isinstance(hackathon_write_ups, list): + raise TypeError('hackathon_write_ups must be of type list') + if not all([isinstance(t, HackathonWriteUp) for t in hackathon_write_ups]): + raise TypeError('hackathon_write_ups must contain only items of type HackathonWriteUp') + self._hackathon_write_ups = hackathon_write_ups + + @property + def next_page_token(self) -> str: + return self._next_page_token + + @next_page_token.setter + def next_page_token(self, next_page_token: str): + if next_page_token is None: + del self.next_page_token + return + if not isinstance(next_page_token, str): + raise TypeError('next_page_token must be of type str') + self._next_page_token = next_page_token + + @property + def total_count(self) -> int: + return self._total_count + + @total_count.setter + def total_count(self, total_count: int): + if total_count is None: + del self.total_count + return + if not isinstance(total_count, int): + raise TypeError('total_count must be of type int') + self._total_count = total_count + + @property + def hackathonWriteUps(self): + return self.hackathon_write_ups + + @property + def nextPageToken(self): + return self.next_page_token + + @property + def totalCount(self): + return self.total_count + + +ApiExportHackathonWriteUpsCsvRequest._fields = [ + FieldMetadata("competitionId", "competition_id", "_competition_id", int, None, PredefinedSerializer(), optional=True), + FieldMetadata("competitionName", "competition_name", "_competition_name", str, None, PredefinedSerializer(), optional=True), +] + +ApiExportHackathonWriteUpsCsvResponse._fields = [ + FieldMetadata("csvContent", "csv_content", "_csv_content", str, "", PredefinedSerializer()), +] + +ApiGetHackathonOverviewRequest._fields = [ + FieldMetadata("competitionName", "competition_name", "_competition_name", str, "", PredefinedSerializer()), +] + +ApiGetHackathonOverviewResponse._fields = [ + FieldMetadata("pages", "pages", "_pages", Page, [], ListSerializer(KaggleObjectSerializer())), +] + +ApiGetHackathonWriteUpRequest._fields = [ + FieldMetadata("competitionName", "competition_name", "_competition_name", str, "", PredefinedSerializer()), + FieldMetadata("hackathonWriteUpId", "hackathon_write_up_id", "_hackathon_write_up_id", int, 0, PredefinedSerializer()), +] + +ApiListHackathonTracksRequest._fields = [ + FieldMetadata("competitionName", "competition_name", "_competition_name", str, "", PredefinedSerializer()), +] + +ApiListHackathonTracksResponse._fields = [ + FieldMetadata("tracks", "tracks", "_tracks", HackathonTrack, [], ListSerializer(KaggleObjectSerializer())), +] + +ApiListHackathonWriteUpsRequest._fields = [ + FieldMetadata("competitionId", "competition_id", "_competition_id", int, None, PredefinedSerializer(), optional=True), + FieldMetadata("teamId", "team_id", "_team_id", int, None, PredefinedSerializer(), optional=True), + FieldMetadata("hackathonTrackIds", "hackathon_track_ids", "_hackathon_track_ids", int, [], ListSerializer(PredefinedSerializer())), + FieldMetadata("published", "published", "_published", bool, None, PredefinedSerializer(), optional=True), + FieldMetadata("winner", "winner", "_winner", bool, None, PredefinedSerializer(), optional=True), + FieldMetadata("template", "template", "_template", bool, None, PredefinedSerializer(), optional=True), + FieldMetadata("pageSize", "page_size", "_page_size", int, None, PredefinedSerializer(), optional=True), + FieldMetadata("pageToken", "page_token", "_page_token", str, None, PredefinedSerializer(), optional=True), + FieldMetadata("skip", "skip", "_skip", int, None, PredefinedSerializer(), optional=True), + FieldMetadata("readMask", "read_mask", "_read_mask", FieldMask, None, FieldMaskSerializer()), + FieldMetadata("hostOrJudge", "host_or_judge", "_host_or_judge", bool, None, PredefinedSerializer(), optional=True), + FieldMetadata("competitionName", "competition_name", "_competition_name", str, None, PredefinedSerializer(), optional=True), +] + +ApiListHackathonWriteUpsResponse._fields = [ + FieldMetadata("hackathonWriteUps", "hackathon_write_ups", "_hackathon_write_ups", HackathonWriteUp, [], ListSerializer(KaggleObjectSerializer())), + FieldMetadata("nextPageToken", "next_page_token", "_next_page_token", str, "", PredefinedSerializer()), + FieldMetadata("totalCount", "total_count", "_total_count", int, 0, PredefinedSerializer()), +] + diff --git a/kagglesdk/competitions/types/hackathons.py b/kagglesdk/competitions/types/hackathons.py index 9055230..950486a 100644 --- a/kagglesdk/competitions/types/hackathons.py +++ b/kagglesdk/competitions/types/hackathons.py @@ -2,7 +2,7 @@ from kagglesdk.competitions.types.team import Team from kagglesdk.discussions.types.writeup_types import WriteUp from kagglesdk.kaggle_object import * -from typing import Optional, List +from typing import List, Optional class HackathonTrack(KaggleObject): r""" diff --git a/kagglesdk/discussions/services/discussions_api_service.py b/kagglesdk/discussions/services/discussions_api_service.py index c82e2a6..442e78b 100644 --- a/kagglesdk/discussions/services/discussions_api_service.py +++ b/kagglesdk/discussions/services/discussions_api_service.py @@ -1,4 +1,4 @@ -from kagglesdk.discussions.types.discussions_api_service import ApiGetTopicRequest, ApiGetTopicResponse, ApiListCommentsRequest, ApiListCommentsResponse, ApiListForumsRequest, ApiListForumsResponse, ApiListTopicsRequest, ApiListTopicsResponse +from kagglesdk.discussions.types.discussions_api_service import ApiGetTopicRequest, ApiGetTopicResponse, ApiListBenchmarkTopicsRequest, ApiListCommentsRequest, ApiListCommentsResponse, ApiListDatasetTopicsRequest, ApiListForumsRequest, ApiListForumsResponse, ApiListModelTopicsRequest, ApiListTopicsRequest, ApiListTopicsResponse from kagglesdk.kaggle_http_client import KaggleHttpClient class DiscussionApiClient(object): @@ -61,3 +61,45 @@ def list_comments(self, request: ApiListCommentsRequest = None) -> ApiListCommen request = ApiListCommentsRequest() return self._client.call("discussions.DiscussionApiService", "ListComments", request, ApiListCommentsResponse) + + def list_dataset_topics(self, request: ApiListDatasetTopicsRequest = None) -> ApiListTopicsResponse: + r""" + List discussion topics for a dataset. + + Args: + request (ApiListDatasetTopicsRequest): + The request object; initialized to empty instance if not specified. + """ + + if request is None: + request = ApiListDatasetTopicsRequest() + + return self._client.call("discussions.DiscussionApiService", "ListDatasetTopics", request, ApiListTopicsResponse) + + def list_model_topics(self, request: ApiListModelTopicsRequest = None) -> ApiListTopicsResponse: + r""" + List discussion topics for a model. + + Args: + request (ApiListModelTopicsRequest): + The request object; initialized to empty instance if not specified. + """ + + if request is None: + request = ApiListModelTopicsRequest() + + return self._client.call("discussions.DiscussionApiService", "ListModelTopics", request, ApiListTopicsResponse) + + def list_benchmark_topics(self, request: ApiListBenchmarkTopicsRequest = None) -> ApiListTopicsResponse: + r""" + List discussion topics for a benchmark. + + Args: + request (ApiListBenchmarkTopicsRequest): + The request object; initialized to empty instance if not specified. + """ + + if request is None: + request = ApiListBenchmarkTopicsRequest() + + return self._client.call("discussions.DiscussionApiService", "ListBenchmarkTopics", request, ApiListTopicsResponse) diff --git a/kagglesdk/discussions/services/writeups_api_service.py b/kagglesdk/discussions/services/writeups_api_service.py new file mode 100644 index 0000000..1c2f043 --- /dev/null +++ b/kagglesdk/discussions/services/writeups_api_service.py @@ -0,0 +1,19 @@ +from kagglesdk.discussions.types.writeups_api_service import ApiGetResolvedWriteUpLinksRequest, ApiGetResolvedWriteUpLinksResponse +from kagglesdk.kaggle_http_client import KaggleHttpClient + +class WriteUpsApiClient(object): + + def __init__(self, client: KaggleHttpClient): + self._client = client + + def get_resolved_write_up_links(self, request: ApiGetResolvedWriteUpLinksRequest = None) -> ApiGetResolvedWriteUpLinksResponse: + r""" + Args: + request (ApiGetResolvedWriteUpLinksRequest): + The request object; initialized to empty instance if not specified. + """ + + if request is None: + request = ApiGetResolvedWriteUpLinksRequest() + + return self._client.call("discussions.WriteUpsApiService", "GetResolvedWriteUpLinks", request, ApiGetResolvedWriteUpLinksResponse) diff --git a/kagglesdk/discussions/types/discussions_api_service.py b/kagglesdk/discussions/types/discussions_api_service.py index 437cba8..2d07c16 100644 --- a/kagglesdk/discussions/types/discussions_api_service.py +++ b/kagglesdk/discussions/types/discussions_api_service.py @@ -443,6 +443,125 @@ def topic(self, topic: Optional['ApiDiscussionTopic']): self._topic = topic +class ApiListBenchmarkTopicsRequest(KaggleObject): + r""" + Attributes: + owner_slug (str) + The owner (user or organization) slug for the benchmark. + benchmark_slug (str) + The benchmark slug. + sort_by (TopicListSortBy) + Sort order for the results. + page_size (int) + Page size for results. + page_token (str) + Page token used for pagination. + search_query (str) + Optional search query to filter topics by. + """ + + def __init__(self): + self._owner_slug = "" + self._benchmark_slug = "" + self._sort_by = None + self._page_size = None + self._page_token = None + self._search_query = None + self._freeze() + + @property + def owner_slug(self) -> str: + """The owner (user or organization) slug for the benchmark.""" + return self._owner_slug + + @owner_slug.setter + def owner_slug(self, owner_slug: str): + if owner_slug is None: + del self.owner_slug + return + if not isinstance(owner_slug, str): + raise TypeError('owner_slug must be of type str') + self._owner_slug = owner_slug + + @property + def benchmark_slug(self) -> str: + """The benchmark slug.""" + return self._benchmark_slug + + @benchmark_slug.setter + def benchmark_slug(self, benchmark_slug: str): + if benchmark_slug is None: + del self.benchmark_slug + return + if not isinstance(benchmark_slug, str): + raise TypeError('benchmark_slug must be of type str') + self._benchmark_slug = benchmark_slug + + @property + def sort_by(self) -> 'TopicListSortBy': + """Sort order for the results.""" + return self._sort_by or TopicListSortBy.TOPIC_LIST_SORT_BY_UNSPECIFIED + + @sort_by.setter + def sort_by(self, sort_by: Optional['TopicListSortBy']): + if sort_by is None: + del self.sort_by + return + if not isinstance(sort_by, TopicListSortBy): + raise TypeError('sort_by must be of type TopicListSortBy') + self._sort_by = sort_by + + @property + def page_size(self) -> int: + """Page size for results.""" + return self._page_size or 0 + + @page_size.setter + def page_size(self, page_size: Optional[int]): + if page_size is None: + del self.page_size + return + if not isinstance(page_size, int): + raise TypeError('page_size must be of type int') + self._page_size = page_size + + @property + def page_token(self) -> str: + """Page token used for pagination.""" + return self._page_token or "" + + @page_token.setter + def page_token(self, page_token: Optional[str]): + if page_token is None: + del self.page_token + return + if not isinstance(page_token, str): + raise TypeError('page_token must be of type str') + self._page_token = page_token + + @property + def search_query(self) -> str: + """Optional search query to filter topics by.""" + return self._search_query or "" + + @search_query.setter + def search_query(self, search_query: Optional[str]): + if search_query is None: + del self.search_query + return + if not isinstance(search_query, str): + raise TypeError('search_query must be of type str') + self._search_query = search_query + + def endpoint(self): + path = '/api/v1/benchmarks/{owner_slug}/{benchmark_slug}/topics/list' + return path.format_map(self.to_field_map(self)) + + @staticmethod + def endpoint_path(): + return '/api/v1/benchmarks/{owner_slug}/{benchmark_slug}/topics/list' + + class ApiListCommentsRequest(KaggleObject): r""" Attributes: @@ -556,6 +675,125 @@ def nextPageToken(self): return self.next_page_token +class ApiListDatasetTopicsRequest(KaggleObject): + r""" + Attributes: + owner_slug (str) + The owner (user or organization) slug for the dataset. + dataset_slug (str) + The dataset slug. + sort_by (TopicListSortBy) + Sort order for the results. + page_size (int) + Page size for results. + page_token (str) + Page token used for pagination. + search_query (str) + Optional search query to filter topics by. + """ + + def __init__(self): + self._owner_slug = "" + self._dataset_slug = "" + self._sort_by = None + self._page_size = None + self._page_token = None + self._search_query = None + self._freeze() + + @property + def owner_slug(self) -> str: + """The owner (user or organization) slug for the dataset.""" + return self._owner_slug + + @owner_slug.setter + def owner_slug(self, owner_slug: str): + if owner_slug is None: + del self.owner_slug + return + if not isinstance(owner_slug, str): + raise TypeError('owner_slug must be of type str') + self._owner_slug = owner_slug + + @property + def dataset_slug(self) -> str: + """The dataset slug.""" + return self._dataset_slug + + @dataset_slug.setter + def dataset_slug(self, dataset_slug: str): + if dataset_slug is None: + del self.dataset_slug + return + if not isinstance(dataset_slug, str): + raise TypeError('dataset_slug must be of type str') + self._dataset_slug = dataset_slug + + @property + def sort_by(self) -> 'TopicListSortBy': + """Sort order for the results.""" + return self._sort_by or TopicListSortBy.TOPIC_LIST_SORT_BY_UNSPECIFIED + + @sort_by.setter + def sort_by(self, sort_by: Optional['TopicListSortBy']): + if sort_by is None: + del self.sort_by + return + if not isinstance(sort_by, TopicListSortBy): + raise TypeError('sort_by must be of type TopicListSortBy') + self._sort_by = sort_by + + @property + def page_size(self) -> int: + """Page size for results.""" + return self._page_size or 0 + + @page_size.setter + def page_size(self, page_size: Optional[int]): + if page_size is None: + del self.page_size + return + if not isinstance(page_size, int): + raise TypeError('page_size must be of type int') + self._page_size = page_size + + @property + def page_token(self) -> str: + """Page token used for pagination.""" + return self._page_token or "" + + @page_token.setter + def page_token(self, page_token: Optional[str]): + if page_token is None: + del self.page_token + return + if not isinstance(page_token, str): + raise TypeError('page_token must be of type str') + self._page_token = page_token + + @property + def search_query(self) -> str: + """Optional search query to filter topics by.""" + return self._search_query or "" + + @search_query.setter + def search_query(self, search_query: Optional[str]): + if search_query is None: + del self.search_query + return + if not isinstance(search_query, str): + raise TypeError('search_query must be of type str') + self._search_query = search_query + + def endpoint(self): + path = '/api/v1/datasets/{owner_slug}/{dataset_slug}/topics/list' + return path.format_map(self.to_field_map(self)) + + @staticmethod + def endpoint_path(): + return '/api/v1/datasets/{owner_slug}/{dataset_slug}/topics/list' + + class ApiListForumsRequest(KaggleObject): r""" """ @@ -592,6 +830,125 @@ def forums(self, forums: Optional[List[Optional['ApiDiscussionForum']]]): self._forums = forums +class ApiListModelTopicsRequest(KaggleObject): + r""" + Attributes: + owner_slug (str) + The owner (user or organization) slug for the model. + model_slug (str) + The model slug. + sort_by (TopicListSortBy) + Sort order for the results. + page_size (int) + Page size for results. + page_token (str) + Page token used for pagination. + search_query (str) + Optional search query to filter topics by. + """ + + def __init__(self): + self._owner_slug = "" + self._model_slug = "" + self._sort_by = None + self._page_size = None + self._page_token = None + self._search_query = None + self._freeze() + + @property + def owner_slug(self) -> str: + """The owner (user or organization) slug for the model.""" + return self._owner_slug + + @owner_slug.setter + def owner_slug(self, owner_slug: str): + if owner_slug is None: + del self.owner_slug + return + if not isinstance(owner_slug, str): + raise TypeError('owner_slug must be of type str') + self._owner_slug = owner_slug + + @property + def model_slug(self) -> str: + """The model slug.""" + return self._model_slug + + @model_slug.setter + def model_slug(self, model_slug: str): + if model_slug is None: + del self.model_slug + return + if not isinstance(model_slug, str): + raise TypeError('model_slug must be of type str') + self._model_slug = model_slug + + @property + def sort_by(self) -> 'TopicListSortBy': + """Sort order for the results.""" + return self._sort_by or TopicListSortBy.TOPIC_LIST_SORT_BY_UNSPECIFIED + + @sort_by.setter + def sort_by(self, sort_by: Optional['TopicListSortBy']): + if sort_by is None: + del self.sort_by + return + if not isinstance(sort_by, TopicListSortBy): + raise TypeError('sort_by must be of type TopicListSortBy') + self._sort_by = sort_by + + @property + def page_size(self) -> int: + """Page size for results.""" + return self._page_size or 0 + + @page_size.setter + def page_size(self, page_size: Optional[int]): + if page_size is None: + del self.page_size + return + if not isinstance(page_size, int): + raise TypeError('page_size must be of type int') + self._page_size = page_size + + @property + def page_token(self) -> str: + """Page token used for pagination.""" + return self._page_token or "" + + @page_token.setter + def page_token(self, page_token: Optional[str]): + if page_token is None: + del self.page_token + return + if not isinstance(page_token, str): + raise TypeError('page_token must be of type str') + self._page_token = page_token + + @property + def search_query(self) -> str: + """Optional search query to filter topics by.""" + return self._search_query or "" + + @search_query.setter + def search_query(self, search_query: Optional[str]): + if search_query is None: + del self.search_query + return + if not isinstance(search_query, str): + raise TypeError('search_query must be of type str') + self._search_query = search_query + + def endpoint(self): + path = '/api/v1/models/{owner_slug}/{model_slug}/topics/list' + return path.format_map(self.to_field_map(self)) + + @staticmethod + def endpoint_path(): + return '/api/v1/models/{owner_slug}/{model_slug}/topics/list' + + class ApiListTopicsRequest(KaggleObject): r""" Attributes: @@ -832,6 +1189,15 @@ def nextPageToken(self): FieldMetadata("topic", "topic", "_topic", ApiDiscussionTopic, None, KaggleObjectSerializer()), ] +ApiListBenchmarkTopicsRequest._fields = [ + FieldMetadata("ownerSlug", "owner_slug", "_owner_slug", str, "", PredefinedSerializer()), + FieldMetadata("benchmarkSlug", "benchmark_slug", "_benchmark_slug", str, "", PredefinedSerializer()), + FieldMetadata("sortBy", "sort_by", "_sort_by", TopicListSortBy, None, EnumSerializer(), optional=True), + FieldMetadata("pageSize", "page_size", "_page_size", int, None, PredefinedSerializer(), optional=True), + FieldMetadata("pageToken", "page_token", "_page_token", str, None, PredefinedSerializer(), optional=True), + FieldMetadata("searchQuery", "search_query", "_search_query", str, None, PredefinedSerializer(), optional=True), +] + ApiListCommentsRequest._fields = [ FieldMetadata("topicId", "topic_id", "_topic_id", int, 0, PredefinedSerializer()), FieldMetadata("pageSize", "page_size", "_page_size", int, None, PredefinedSerializer(), optional=True), @@ -843,12 +1209,30 @@ def nextPageToken(self): FieldMetadata("nextPageToken", "next_page_token", "_next_page_token", str, "", PredefinedSerializer()), ] +ApiListDatasetTopicsRequest._fields = [ + FieldMetadata("ownerSlug", "owner_slug", "_owner_slug", str, "", PredefinedSerializer()), + FieldMetadata("datasetSlug", "dataset_slug", "_dataset_slug", str, "", PredefinedSerializer()), + FieldMetadata("sortBy", "sort_by", "_sort_by", TopicListSortBy, None, EnumSerializer(), optional=True), + FieldMetadata("pageSize", "page_size", "_page_size", int, None, PredefinedSerializer(), optional=True), + FieldMetadata("pageToken", "page_token", "_page_token", str, None, PredefinedSerializer(), optional=True), + FieldMetadata("searchQuery", "search_query", "_search_query", str, None, PredefinedSerializer(), optional=True), +] + ApiListForumsRequest._fields = [] ApiListForumsResponse._fields = [ FieldMetadata("forums", "forums", "_forums", ApiDiscussionForum, [], ListSerializer(KaggleObjectSerializer())), ] +ApiListModelTopicsRequest._fields = [ + FieldMetadata("ownerSlug", "owner_slug", "_owner_slug", str, "", PredefinedSerializer()), + FieldMetadata("modelSlug", "model_slug", "_model_slug", str, "", PredefinedSerializer()), + FieldMetadata("sortBy", "sort_by", "_sort_by", TopicListSortBy, None, EnumSerializer(), optional=True), + FieldMetadata("pageSize", "page_size", "_page_size", int, None, PredefinedSerializer(), optional=True), + FieldMetadata("pageToken", "page_token", "_page_token", str, None, PredefinedSerializer(), optional=True), + FieldMetadata("searchQuery", "search_query", "_search_query", str, None, PredefinedSerializer(), optional=True), +] + ApiListTopicsRequest._fields = [ FieldMetadata("forumSlug", "forum_slug", "_forum_slug", str, None, PredefinedSerializer(), optional=True), FieldMetadata("sortBy", "sort_by", "_sort_by", TopicListSortBy, None, EnumSerializer(), optional=True), diff --git a/kagglesdk/discussions/types/writeup_enums.py b/kagglesdk/discussions/types/writeup_enums.py index d4473c9..c02e68e 100644 --- a/kagglesdk/discussions/types/writeup_enums.py +++ b/kagglesdk/discussions/types/writeup_enums.py @@ -1,5 +1,16 @@ import enum +class ResolvedWriteUpLinkType(enum.Enum): + """Indicates what kind of content a resolved WriteUpLink points to""" + RESOLVED_WRITE_UP_LINK_TYPE_UNSPECIFIED = 0 + RESOLVED_WRITE_UP_LINK_TYPE_DATASET = 1 + RESOLVED_WRITE_UP_LINK_TYPE_NOTEBOOK = 2 + RESOLVED_WRITE_UP_LINK_TYPE_MODEL = 3 + RESOLVED_WRITE_UP_LINK_TYPE_BENCHMARK = 4 + RESOLVED_WRITE_UP_LINK_TYPE_YOUTUBE = 5 + RESOLVED_WRITE_UP_LINK_TYPE_EXTERNAL = 6 + RESOLVED_WRITE_UP_LINK_TYPE_IMAGE = 7 + class WriteUpLinkLocation(enum.Enum): WRITE_UP_LINK_LOCATION_UNSPECIFIED = 0 ADDITIONAL_LINKS = 1 diff --git a/kagglesdk/discussions/types/writeup_types.py b/kagglesdk/discussions/types/writeup_types.py index 48176be..1566eda 100644 --- a/kagglesdk/discussions/types/writeup_types.py +++ b/kagglesdk/discussions/types/writeup_types.py @@ -2,7 +2,7 @@ from kagglesdk.common.types.cropped_image_upload import CroppedImageUpload from kagglesdk.community.types.content_enums import ContentState from kagglesdk.discussions.types.forum_message import CommentForumMessage -from kagglesdk.discussions.types.writeup_enums import WriteUpLinkLocation, WriteUpLinkMediaType, WriteUpType +from kagglesdk.discussions.types.writeup_enums import ResolvedWriteUpLinkType, WriteUpLinkLocation, WriteUpLinkMediaType, WriteUpType from kagglesdk.kaggle_object import * from kagglesdk.licenses.types.licenses_types import License from kagglesdk.security.types.security_types import KaggleResourceType @@ -12,6 +12,232 @@ from kagglesdk.users.types.user_avatar import UserAvatar from typing import Optional, List +class ResolvedFileSummary(KaggleObject): + r""" + Summary of the files contained in a dataset or notebook output. + Used exclusively by the MCP server via ResolvedWriteUpLink. + + Attributes: + total_file_count (int) + Total number of files + total_size (int) + Total size in bytes + file_types (str) + List of file type extensions (e.g. 'csv', 'json') + sample_file_names (str) + A sample of file names from the resource + """ + + def __init__(self): + self._total_file_count = 0 + self._total_size = 0 + self._file_types = [] + self._sample_file_names = [] + self._freeze() + + @property + def total_file_count(self) -> int: + """Total number of files""" + return self._total_file_count + + @total_file_count.setter + def total_file_count(self, total_file_count: int): + if total_file_count is None: + del self.total_file_count + return + if not isinstance(total_file_count, int): + raise TypeError('total_file_count must be of type int') + self._total_file_count = total_file_count + + @property + def total_size(self) -> int: + """Total size in bytes""" + return self._total_size + + @total_size.setter + def total_size(self, total_size: int): + if total_size is None: + del self.total_size + return + if not isinstance(total_size, int): + raise TypeError('total_size must be of type int') + self._total_size = total_size + + @property + def file_types(self) -> Optional[List[str]]: + """List of file type extensions (e.g. 'csv', 'json')""" + return self._file_types + + @file_types.setter + def file_types(self, file_types: Optional[List[str]]): + if file_types is None: + del self.file_types + return + if not isinstance(file_types, list): + raise TypeError('file_types must be of type list') + if not all([isinstance(t, str) for t in file_types]): + raise TypeError('file_types must contain only items of type str') + self._file_types = file_types + + @property + def sample_file_names(self) -> Optional[List[str]]: + """A sample of file names from the resource""" + return self._sample_file_names + + @sample_file_names.setter + def sample_file_names(self, sample_file_names: Optional[List[str]]): + if sample_file_names is None: + del self.sample_file_names + return + if not isinstance(sample_file_names, list): + raise TypeError('sample_file_names must be of type list') + if not all([isinstance(t, str) for t in sample_file_names]): + raise TypeError('sample_file_names must contain only items of type str') + self._sample_file_names = sample_file_names + + +class ResolvedWriteUpLink(KaggleObject): + r""" + A fully-resolved view of a WriteUpLink used exclusively by the MCP server + to provide rich context about each link. Includes download URLs and file + metadata fetched from underlying entities (datasets, notebooks, models, etc.) + as well as thumbnail and metadata for YouTube videos and external links + (GitHub, etc.). + + Attributes: + title (str) + Title of the resolved resource + description (str) + Description of the resolved resource + type (ResolvedWriteUpLinkType) + The type of content this link points to + download_url (str) + Download URL for Kaggle resources (e.g. datasets, notebooks, models, etc.) + file_summary (ResolvedFileSummary) + File summary for dataset links + thumbnail_url (str) + Thumbnail image URL for the resolved resource (e.g. YouTube video + thumbnail, Open Graph image for external links, dataset card image) + original_url (str) + The original URL from the WriteUpLink, preserving the connection to the + resource + """ + + def __init__(self): + self._title = "" + self._description = "" + self._type = ResolvedWriteUpLinkType.RESOLVED_WRITE_UP_LINK_TYPE_UNSPECIFIED + self._download_url = "" + self._file_summary = None + self._thumbnail_url = "" + self._original_url = "" + self._freeze() + + @property + def title(self) -> str: + """Title of the resolved resource""" + return self._title + + @title.setter + def title(self, title: str): + if title is None: + del self.title + return + if not isinstance(title, str): + raise TypeError('title must be of type str') + self._title = title + + @property + def description(self) -> str: + """Description of the resolved resource""" + return self._description + + @description.setter + def description(self, description: str): + if description is None: + del self.description + return + if not isinstance(description, str): + raise TypeError('description must be of type str') + self._description = description + + @property + def type(self) -> 'ResolvedWriteUpLinkType': + """The type of content this link points to""" + return self._type + + @type.setter + def type(self, type: 'ResolvedWriteUpLinkType'): + if type is None: + del self.type + return + if not isinstance(type, ResolvedWriteUpLinkType): + raise TypeError('type must be of type ResolvedWriteUpLinkType') + self._type = type + + @property + def download_url(self) -> str: + """Download URL for Kaggle resources (e.g. datasets, notebooks, models, etc.)""" + return self._download_url + + @download_url.setter + def download_url(self, download_url: str): + if download_url is None: + del self.download_url + return + if not isinstance(download_url, str): + raise TypeError('download_url must be of type str') + self._download_url = download_url + + @property + def file_summary(self) -> Optional['ResolvedFileSummary']: + """File summary for dataset links""" + return self._file_summary or None + + @file_summary.setter + def file_summary(self, file_summary: Optional[Optional['ResolvedFileSummary']]): + if file_summary is None: + del self.file_summary + return + if not isinstance(file_summary, ResolvedFileSummary): + raise TypeError('file_summary must be of type ResolvedFileSummary') + self._file_summary = file_summary + + @property + def original_url(self) -> str: + r""" + The original URL from the WriteUpLink, preserving the connection to the + resource + """ + return self._original_url + + @original_url.setter + def original_url(self, original_url: str): + if original_url is None: + del self.original_url + return + if not isinstance(original_url, str): + raise TypeError('original_url must be of type str') + self._original_url = original_url + + @property + def thumbnail_url(self) -> str: + r""" + Thumbnail image URL for the resolved resource (e.g. YouTube video + thumbnail, Open Graph image for external links, dataset card image) + """ + return self._thumbnail_url + + @thumbnail_url.setter + def thumbnail_url(self, thumbnail_url: str): + if thumbnail_url is None: + del self.thumbnail_url + return + if not isinstance(thumbnail_url, str): + raise TypeError('thumbnail_url must be of type str') + self._thumbnail_url = thumbnail_url + + class WriteUp(KaggleObject): r""" Attributes: @@ -67,6 +293,14 @@ class WriteUp(KaggleObject): hackathon competition publish_time (datetime) Time when WriteUp was last published + saved_cover_image_url (str) + Raw (un-fallback'd) saved cover image URL from the DB. Unlike + `cover_image_url`, this is not substituted with a thumbnail/carousel/ + competition fallback when empty. Consumed by the editor so authors aren't + misled into thinking a borrowed image is their saved cover. + saved_thumbnail_image_url (str) + Raw (un-fallback'd) saved thumbnail image URL from the DB. See + `saved_cover_image_url` for rationale. """ def __init__(self): @@ -95,6 +329,8 @@ def __init__(self): self._can_pin = False self._collaborators = [] self._publish_time = None + self._saved_cover_image_url = "" + self._saved_thumbnail_image_url = "" self._freeze() @property @@ -459,6 +695,42 @@ def publish_time(self, publish_time: datetime): raise TypeError('publish_time must be of type datetime') self._publish_time = publish_time + @property + def saved_cover_image_url(self) -> str: + r""" + Raw (un-fallback'd) saved cover image URL from the DB. Unlike + `cover_image_url`, this is not substituted with a thumbnail/carousel/ + competition fallback when empty. Consumed by the editor so authors aren't + misled into thinking a borrowed image is their saved cover. + """ + return self._saved_cover_image_url + + @saved_cover_image_url.setter + def saved_cover_image_url(self, saved_cover_image_url: str): + if saved_cover_image_url is None: + del self.saved_cover_image_url + return + if not isinstance(saved_cover_image_url, str): + raise TypeError('saved_cover_image_url must be of type str') + self._saved_cover_image_url = saved_cover_image_url + + @property + def saved_thumbnail_image_url(self) -> str: + r""" + Raw (un-fallback'd) saved thumbnail image URL from the DB. See + `saved_cover_image_url` for rationale. + """ + return self._saved_thumbnail_image_url + + @saved_thumbnail_image_url.setter + def saved_thumbnail_image_url(self, saved_thumbnail_image_url: str): + if saved_thumbnail_image_url is None: + del self.saved_thumbnail_image_url + return + if not isinstance(saved_thumbnail_image_url, str): + raise TypeError('saved_thumbnail_image_url must be of type str') + self._saved_thumbnail_image_url = saved_thumbnail_image_url + class WriteUpImageInfo(KaggleObject): r""" @@ -1060,6 +1332,23 @@ def is_phone_verified(self, is_phone_verified: Optional[bool]): self._is_phone_verified = is_phone_verified +ResolvedFileSummary._fields = [ + FieldMetadata("totalFileCount", "total_file_count", "_total_file_count", int, 0, PredefinedSerializer()), + FieldMetadata("totalSize", "total_size", "_total_size", int, 0, PredefinedSerializer()), + FieldMetadata("fileTypes", "file_types", "_file_types", str, [], ListSerializer(PredefinedSerializer())), + FieldMetadata("sampleFileNames", "sample_file_names", "_sample_file_names", str, [], ListSerializer(PredefinedSerializer())), +] + +ResolvedWriteUpLink._fields = [ + FieldMetadata("title", "title", "_title", str, "", PredefinedSerializer()), + FieldMetadata("description", "description", "_description", str, "", PredefinedSerializer()), + FieldMetadata("type", "type", "_type", ResolvedWriteUpLinkType, ResolvedWriteUpLinkType.RESOLVED_WRITE_UP_LINK_TYPE_UNSPECIFIED, EnumSerializer()), + FieldMetadata("downloadUrl", "download_url", "_download_url", str, "", PredefinedSerializer()), + FieldMetadata("fileSummary", "file_summary", "_file_summary", ResolvedFileSummary, None, KaggleObjectSerializer(), optional=True), + FieldMetadata("thumbnailUrl", "thumbnail_url", "_thumbnail_url", str, "", PredefinedSerializer()), + FieldMetadata("originalUrl", "original_url", "_original_url", str, "", PredefinedSerializer()), +] + WriteUp._fields = [ FieldMetadata("id", "id", "_id", int, 0, PredefinedSerializer()), FieldMetadata("topicId", "topic_id", "_topic_id", int, 0, PredefinedSerializer()), @@ -1086,6 +1375,8 @@ def is_phone_verified(self, is_phone_verified: Optional[bool]): FieldMetadata("canPin", "can_pin", "_can_pin", bool, False, PredefinedSerializer()), FieldMetadata("collaborators", "collaborators", "_collaborators", UserAvatar, [], ListSerializer(KaggleObjectSerializer())), FieldMetadata("publishTime", "publish_time", "_publish_time", datetime, None, DateTimeSerializer()), + FieldMetadata("savedCoverImageUrl", "saved_cover_image_url", "_saved_cover_image_url", str, "", PredefinedSerializer()), + FieldMetadata("savedThumbnailImageUrl", "saved_thumbnail_image_url", "_saved_thumbnail_image_url", str, "", PredefinedSerializer()), ] WriteUpImageInfo._fields = [ diff --git a/kagglesdk/discussions/types/writeups_api_service.py b/kagglesdk/discussions/types/writeups_api_service.py new file mode 100644 index 0000000..31b1cd1 --- /dev/null +++ b/kagglesdk/discussions/types/writeups_api_service.py @@ -0,0 +1,74 @@ +from kagglesdk.discussions.types.writeup_types import ResolvedWriteUpLink +from kagglesdk.kaggle_object import * +from typing import List, Optional + +class ApiGetResolvedWriteUpLinksRequest(KaggleObject): + r""" + Attributes: + write_up_id (int) + """ + + def __init__(self): + self._write_up_id = 0 + self._freeze() + + @property + def write_up_id(self) -> int: + return self._write_up_id + + @write_up_id.setter + def write_up_id(self, write_up_id: int): + if write_up_id is None: + del self.write_up_id + return + if not isinstance(write_up_id, int): + raise TypeError('write_up_id must be of type int') + self._write_up_id = write_up_id + + def endpoint(self): + path = '/api/v1/writeups/{write_up_id}/resolved-links' + return path.format_map(self.to_field_map(self)) + + @staticmethod + def endpoint_path(): + return '/api/v1/writeups/{write_up_id}/resolved-links' + + +class ApiGetResolvedWriteUpLinksResponse(KaggleObject): + r""" + Attributes: + resolved_links (ResolvedWriteUpLink) + """ + + def __init__(self): + self._resolved_links = [] + self._freeze() + + @property + def resolved_links(self) -> Optional[List[Optional['ResolvedWriteUpLink']]]: + return self._resolved_links + + @resolved_links.setter + def resolved_links(self, resolved_links: Optional[List[Optional['ResolvedWriteUpLink']]]): + if resolved_links is None: + del self.resolved_links + return + if not isinstance(resolved_links, list): + raise TypeError('resolved_links must be of type list') + if not all([isinstance(t, ResolvedWriteUpLink) for t in resolved_links]): + raise TypeError('resolved_links must contain only items of type ResolvedWriteUpLink') + self._resolved_links = resolved_links + + @property + def resolvedLinks(self): + return self.resolved_links + + +ApiGetResolvedWriteUpLinksRequest._fields = [ + FieldMetadata("writeUpId", "write_up_id", "_write_up_id", int, 0, PredefinedSerializer()), +] + +ApiGetResolvedWriteUpLinksResponse._fields = [ + FieldMetadata("resolvedLinks", "resolved_links", "_resolved_links", ResolvedWriteUpLink, [], ListSerializer(KaggleObjectSerializer())), +] + diff --git a/kagglesdk/kaggle_client.py b/kagglesdk/kaggle_client.py index 37ed541..9b28600 100644 --- a/kagglesdk/kaggle_client.py +++ b/kagglesdk/kaggle_client.py @@ -5,9 +5,10 @@ from kagglesdk.blobs.services.blob_api_service import BlobApiClient from kagglesdk.common.services.operations_service import OperationsClient from kagglesdk.competitions.services.competition_api_service import CompetitionApiClient -from kagglesdk.competitions.services.hackathon_service import HackathonClient +from kagglesdk.competitions.services.hackathon_api_service import HackathonApiClient from kagglesdk.datasets.services.dataset_api_service import DatasetApiClient from kagglesdk.discussions.services.discussions_api_service import DiscussionApiClient +from kagglesdk.discussions.services.writeups_api_service import WriteUpsApiClient from kagglesdk.education.services.education_api_service import EducationApiClient from kagglesdk.kernels.services.kernels_api_service import KernelsApiClient from kagglesdk.models.services.model_api_service import ModelApiClient @@ -47,7 +48,7 @@ def __init__(self, http_client: KaggleHttpClient): class Competitions(object): def __init__(self, http_client: KaggleHttpClient): self.competition_api_client = CompetitionApiClient(http_client) - self.hackathon_client = HackathonClient(http_client) + self.hackathon_api_client = HackathonApiClient(http_client) class Datasets(object): def __init__(self, http_client: KaggleHttpClient): @@ -56,6 +57,7 @@ def __init__(self, http_client: KaggleHttpClient): class Discussions(object): def __init__(self, http_client: KaggleHttpClient): self.discussion_api_client = DiscussionApiClient(http_client) + self.write_ups_api_client = WriteUpsApiClient(http_client) class Education(object): def __init__(self, http_client: KaggleHttpClient): diff --git a/kagglesdk/kaggle_env.py b/kagglesdk/kaggle_env.py index d378388..d2e944f 100644 --- a/kagglesdk/kaggle_env.py +++ b/kagglesdk/kaggle_env.py @@ -35,13 +35,6 @@ def get_endpoint(env: KaggleEnv): return _env_to_endpoint[env] -def get_web_endpoint(env: KaggleEnv): - # In PROD, the `api` subdomain is used which breaks link to detail pages. - if env == KaggleEnv.PROD: - return "https://kaggle.com" - return get_endpoint(env) - - def get_env(): env = os.getenv("KAGGLE_API_ENVIRONMENT") if env is None or env == "PROD": diff --git a/kagglesdk/kernels/services/kernels_api_service.py b/kagglesdk/kernels/services/kernels_api_service.py index 6da2ef0..c6fa0ad 100644 --- a/kagglesdk/kernels/services/kernels_api_service.py +++ b/kagglesdk/kernels/services/kernels_api_service.py @@ -2,7 +2,7 @@ from kagglesdk.common.types.http_redirect import HttpRedirect from kagglesdk.common.types.operations import Operation from kagglesdk.kaggle_http_client import KaggleHttpClient -from kagglesdk.kernels.types.kernels_api_service import ApiCancelKernelSessionRequest, ApiCancelKernelSessionResponse, ApiCreateKernelSessionRequest, ApiDeleteKernelRequest, ApiDeleteKernelResponse, ApiDownloadKernelOutputRequest, ApiDownloadKernelOutputZipRequest, ApiGetKernelRequest, ApiGetKernelResponse, ApiGetKernelSessionStatusRequest, ApiGetKernelSessionStatusResponse, ApiListKernelFilesRequest, ApiListKernelFilesResponse, ApiListKernelSessionOutputRequest, ApiListKernelSessionOutputResponse, ApiListKernelsRequest, ApiListKernelsResponse, ApiSaveKernelRequest, ApiSaveKernelResponse +from kagglesdk.kernels.types.kernels_api_service import ApiCancelKernelSessionRequest, ApiCancelKernelSessionResponse, ApiCreateKernelSessionRequest, ApiDeleteKernelRequest, ApiDeleteKernelResponse, ApiDownloadKernelOutputRequest, ApiDownloadKernelOutputZipRequest, ApiGetKernelRequest, ApiGetKernelResponse, ApiGetKernelSessionLogsStreamRequest, ApiGetKernelSessionStatusRequest, ApiGetKernelSessionStatusResponse, ApiListKernelFilesRequest, ApiListKernelFilesResponse, ApiListKernelSessionOutputRequest, ApiListKernelSessionOutputResponse, ApiListKernelsRequest, ApiListKernelsResponse, ApiSaveKernelRequest, ApiSaveKernelResponse class KernelsApiClient(object): @@ -144,3 +144,27 @@ def create_kernel_session(self, request: ApiCreateKernelSessionRequest = None) - request = ApiCreateKernelSessionRequest() return self._client.call("kernels.KernelsApiService", "CreateKernelSession", request, Operation) + + def get_kernel_session_logs_stream(self, request: ApiGetKernelSessionLogsStreamRequest = None) -> FileDownload: + r""" + Streams the log output of a notebook session. + + While the session is running (or shortly after), the midtier proxies + to the per-session log endpoint published by the session manager and + returns Server-Sent Events (Content-Type: text/event-stream) until the + upstream sends an `END_OF_LOG` sentinel or the client disconnects. + + If the session has already terminated by the time the request arrives, + the live stream URL is no longer available; the midtier instead returns + the persisted log file as written by the worker (Content-Type: + application/json). Callers should branch on the response Content-Type. + + Args: + request (ApiGetKernelSessionLogsStreamRequest): + The request object; initialized to empty instance if not specified. + """ + + if request is None: + request = ApiGetKernelSessionLogsStreamRequest() + + return self._client.call("kernels.KernelsApiService", "GetKernelSessionLogsStream", request, FileDownload) diff --git a/kagglesdk/kernels/types/kernels_api_service.py b/kagglesdk/kernels/types/kernels_api_service.py index 7e922bf..b6c1aab 100644 --- a/kagglesdk/kernels/types/kernels_api_service.py +++ b/kagglesdk/kernels/types/kernels_api_service.py @@ -481,6 +481,67 @@ def blob(self, blob: Optional['ApiKernelBlob']): self._blob = blob +class ApiGetKernelSessionLogsStreamRequest(KaggleObject): + r""" + Attributes: + kernel_session_id (int) + ID of the notebook session whose live logs should be streamed. + wait_for_logs_url_seconds (int) + Maximum number of seconds to wait for the upstream log stream URL to be + published by the session manager before giving up. The session manager + only publishes the URL once the session has been allocated to a worker, + so callers may need to wait while the session is queued. Defaults to 60 + seconds; capped to 300 seconds. + """ + + def __init__(self): + self._kernel_session_id = 0 + self._wait_for_logs_url_seconds = None + self._freeze() + + @property + def kernel_session_id(self) -> int: + """ID of the notebook session whose live logs should be streamed.""" + return self._kernel_session_id + + @kernel_session_id.setter + def kernel_session_id(self, kernel_session_id: int): + if kernel_session_id is None: + del self.kernel_session_id + return + if not isinstance(kernel_session_id, int): + raise TypeError('kernel_session_id must be of type int') + self._kernel_session_id = kernel_session_id + + @property + def wait_for_logs_url_seconds(self) -> int: + r""" + Maximum number of seconds to wait for the upstream log stream URL to be + published by the session manager before giving up. The session manager + only publishes the URL once the session has been allocated to a worker, + so callers may need to wait while the session is queued. Defaults to 60 + seconds; capped to 300 seconds. + """ + return self._wait_for_logs_url_seconds or 0 + + @wait_for_logs_url_seconds.setter + def wait_for_logs_url_seconds(self, wait_for_logs_url_seconds: Optional[int]): + if wait_for_logs_url_seconds is None: + del self.wait_for_logs_url_seconds + return + if not isinstance(wait_for_logs_url_seconds, int): + raise TypeError('wait_for_logs_url_seconds must be of type int') + self._wait_for_logs_url_seconds = wait_for_logs_url_seconds + + def endpoint(self): + path = '/api/v1/kernels/sessions/{kernel_session_id}/logs/stream' + return path.format_map(self.to_field_map(self)) + + @staticmethod + def endpoint_path(): + return '/api/v1/kernels/sessions/{kernel_session_id}/logs/stream' + + class ApiGetKernelSessionStatusRequest(KaggleObject): r""" Attributes: @@ -2314,6 +2375,11 @@ def kernelId(self): FieldMetadata("blob", "blob", "_blob", ApiKernelBlob, None, KaggleObjectSerializer()), ] +ApiGetKernelSessionLogsStreamRequest._fields = [ + FieldMetadata("kernelSessionId", "kernel_session_id", "_kernel_session_id", int, 0, PredefinedSerializer()), + FieldMetadata("waitForLogsUrlSeconds", "wait_for_logs_url_seconds", "_wait_for_logs_url_seconds", int, None, PredefinedSerializer(), optional=True), +] + ApiGetKernelSessionStatusRequest._fields = [ FieldMetadata("userName", "user_name", "_user_name", str, "", PredefinedSerializer()), FieldMetadata("kernelSlug", "kernel_slug", "_kernel_slug", str, "", PredefinedSerializer()), diff --git a/sample.py b/sample.py new file mode 100644 index 0000000..adeb491 --- /dev/null +++ b/sample.py @@ -0,0 +1,131 @@ +import sys +from kagglesdk.competitions.services.competition_service import CompetitionClient +from kagglesdk.competitions.types.competition_service import GetCompetitionRequest +from kagglesdk.competitions.types.competition_service import ListCompetitionsRequest +from kagglesdk.datasets.types.dataset_service import ( + DatasetListItem, + SearchDatasetsRequest, +) +from kagglesdk.datasets.services.dataset_service import DatasetClient +from kagglesdk.kernels.services.kernels_service import KernelsClient +from kagglesdk.kernels.types.kernels_enums import ( + KernelsListSortType, + KernelsListViewType, +) +from kagglesdk.kernels.types.kernels_types import ( + KernelList, + ListKernelIdsRequest, + ListKernelsRequest, + SearchKernelIdsRequest, +) +from kagglesdk.users.types.users_service import ( + GetCurrentUserRequest, + SearchUsersSuggestionsRequest, +) +from kagglesdk.users.services.users_service import UsersClient +from kagglesdk import KaggleClient, KaggleEnv + + +def get_competition(client: CompetitionClient): + competition = client.get_competition(competition_name="titanic") + print("Competition:") + print(f"/competitions/{competition.competition_name} {competition.title} {competition.brief_description}") + print() + + +def list_competitions(client: CompetitionClient): + request = ListCompetitionsRequest() + request.page_size = 5 + request.selector = ListCompetitionsRequest.Selector() + request.selector.list_option = ListCompetitionsRequest.Selector.ListOption.LIST_OPTION_ACTIVE + request.selector.sort_option = ListCompetitionsRequest.Selector.SortOption.SORT_OPTION_NUM_TEAMS + request.selector.visibility_filter = ListCompetitionsRequest.Selector.VisibilityFilter.VISIBILITY_FILTER_PUBLIC + + response = client.list_competitions(request) + print("Competitions:") + for competition in response.competitions: + print(f'/competitions/{competition.competition_name} "{competition.title}" "{competition.brief_description}"') + print() + + +def list_kernels(client: KernelsClient): + request = ListKernelsRequest() + request.kernel_filter_criteria = SearchKernelIdsRequest() + request.kernel_filter_criteria.list_request = ListKernelIdsRequest() + request.kernel_filter_criteria.list_request.competition_id = 3136 # Titanic + request.kernel_filter_criteria.list_request.sort_by = KernelsListSortType.HOTNESS + request.kernel_filter_criteria.list_request.page_size = 5 + request.kernel_filter_criteria.list_request.page = 1 + request.kernel_filter_criteria.list_request.group = KernelsListViewType.EVERYONE + + response = response = client.list_kernels(request) + print("Kernels:") + for kernel in response.kernels: + print(f'/code/{kernel.current_url_slug} "{kernel.title}" by "{kernel.author.display_name}"') + print() + + +def list_datasets(client: DatasetClient): + request = SearchDatasetsRequest() + + response = client.search_datasets(request) + + print("Datasets:") + for dataset in response.dataset_list.items[:5]: + print(f'{dataset.dataset_url} by "{dataset.owner_name}"') + print() + + +def get_current_user(client: UsersClient): + current_user = client.get_current_user() + + print("Current user:") + print(f'{current_user.id}/{current_user.user_name} "{current_user.display_name}"') + print() + + +def search_users_suggestions(client: UsersClient): + topic_id = 293861 # /discussions/getting-started/293861 + + response = client.search_users_suggestions(query="yas", topic_id=topic_id, page_size=5) + + print("User suggestions:") + for user in response.users_suggestions: + print(f'{user.id}/{user.user_name} "{user.display_name}"') + print() + + +def _build_kaggle_client(args): + env = ( + KaggleEnv.STAGING + if "--staging" in args + else (KaggleEnv.ADMIN if "--admin" in args else KaggleEnv.QA if "--qa" in args else KaggleEnv.LOCAL) + ) + verbose = "--verbose" in args or "-v" in args + return KaggleClient(env=env, verbose=verbose) + + +def main(args): + try: + with _build_kaggle_client(args) as kaggle_client: + if "--competitions" in args: + get_competition(kaggle_client.competitions.competition_client) + list_competitions(kaggle_client.competitions.competition_client) + + if "--code" in args: + list_kernels(kaggle_client.kernels.kernels_client) + + if "--datasets" in args: + list_datasets(kaggle_client.datasets.dataset_client) + + if "--users" in args: + get_current_user(kaggle_client.users.users_client) + search_users_suggestions(kaggle_client.users.users_client) + except Exception as e: + if "--verbose" in args or "-v" in args: + raise + print(e) + + +if __name__ == "__main__": + main(sys.argv[1:]) diff --git a/test.py b/test.py new file mode 100644 index 0000000..985772f --- /dev/null +++ b/test.py @@ -0,0 +1,113 @@ +import sys +from json import JSONDecodeError + +from kagglesdk.datasets.services.dataset_api_service import DatasetApiClient +from kagglesdk.datasets.types.dataset_api_service import ApiDownloadDatasetRequest +from kagglesdk.models.services.model_api_service import ModelApiClient +from kagglesdk.models.types.model_api_service import ( + ApiListModelInstanceVersionFilesRequest, + ApiDownloadModelInstanceVersionRequest, +) +from kagglesdk.models.types.model_enums import ModelFramework +from kagglesdk import KaggleClient, KaggleEnv + + +# An example that illustrates how to use the generated API. +# This exercises all the derived, conditional endpoints. +# Requests cannot be reused since the #framework property is deleted. +# That's done to ensure it won't show up in the body of a POST, although +# currently all Request objects that include #framework use GET. + + +def download_dataset(client: DatasetApiClient): + # Download a small dataset that's on localhost. + request = ApiDownloadDatasetRequest() + request.owner_slug = "shreyanshverma27" + request.dataset_slug = "online-sales-dataset-popular-marketplace-data" + try: + response = client.download_dataset(request) + print(response.method()) + except JSONDecodeError as e: + print("Download request succeeded, but no download was performed") + + request = ApiDownloadDatasetRequest() + request.owner_slug = "shreyanshverma27" + request.dataset_slug = "online-sales-dataset-popular-marketplace-data" + request.file_name = "Online Sales Data.csv" + try: + response = client.download_dataset(request) + print(response.method()) + except JSONDecodeError as e: + print("Download request succeeded, but no download was performed") + + +def list_model_version_files(client: ModelApiClient): + request = ApiListModelInstanceVersionFilesRequest() + request.owner_slug = "google" + request.model_slug = "recurrentgemma" + request.instance_slug = "2b" + request.framework = ModelFramework.MODEL_FRAMEWORK_FLAX + request.version_number = 1 + response = client.list_model_instance_version_files(request) + for file in response.files: + print(file.name) + + request = ApiListModelInstanceVersionFilesRequest() + request.owner_slug = "google" + request.model_slug = "recurrentgemma" + request.instance_slug = "2b" + request.framework = ModelFramework.MODEL_FRAMEWORK_FLAX + response = client.list_model_instance_version_files(request) + for file in response.files: + print(file.name) + + +def download_model_instance_version(client: ModelApiClient): + # Download a smallish model that's on localhost. + request = ApiDownloadModelInstanceVersionRequest() + request.owner_slug = "google" + request.model_slug = "yamnet" + request.instance_slug = "classification-tflite" + request.version_number = 1 + request.framework = ModelFramework.MODEL_FRAMEWORK_TF_LITE + try: + response = client.download_model_instance_version(request) + print(response.method()) + except JSONDecodeError as e: + print("Download request succeeded, but no download was performed") + + request = ApiDownloadModelInstanceVersionRequest() + request.owner_slug = "google" + request.model_slug = "yamnet" + request.instance_slug = "classification-tflite" + request.version_number = 1 + request.path = "1.tflite" + request.framework = ModelFramework.MODEL_FRAMEWORK_TF_LITE + try: + response = client.download_model_instance_version(request) + print(response.method()) + except JSONDecodeError as e: + print("Download request succeeded, but no download was performed") + + +def _build_kaggle_client(args): + env = KaggleEnv.STAGING if "--staging" in args else KaggleEnv.ADMIN if "--admin" in args else KaggleEnv.LOCAL + verbose = "--verbose" in args or "-v" in args + return KaggleClient(env=env, verbose=verbose) + + +def main(args): + try: + with _build_kaggle_client(args) as kaggle_client: + download_dataset(kaggle_client.datasets.dataset_api_client) + list_model_version_files(kaggle_client.models.model_api_client) + download_model_instance_version(kaggle_client.models.model_api_client) + + except Exception as e: + if "--verbose" in args or "-v" in args: + raise + print(e) + + +if __name__ == "__main__": + main(sys.argv[1:])