From b6716eb2747c4c2444a196c8d81a187eb741ae8e Mon Sep 17 00:00:00 2001 From: Bovard Doerschuk-Tiberi Date: Tue, 21 Jul 2026 23:03:48 +0000 Subject: [PATCH 1/2] Regenerate SDK and release 0.1.35 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Regenerated with `kapigen --public-api-output-path` (per README) from kaggleazure origin/ci at 75e987852b3. Changes surfaced by the regen: - Add `ExportEnrichedHackathonWriteUpsCsv` to `competitions.HackathonService` and wire `HackathonClient` into `KaggleClient` (replaces old `list_hackathon_tracks` shape). - Add benchmark task run/creation types on `benchmark_tasks_api_service`. - Add new fields to `competition_api_service` types. - Drop obsolete `competitions/types/page_service.py`. - Fix "successfull" → "successful" typo in `ContentState` doc (fixed upstream). Hand-maintained files (`kaggle_object.py`, `kaggle_http_client.py`, `kaggle_object_tests.py`, `search_api_service.py` doc typo fix) preserved from `main`. --- kagglesdk/__init__.py | 2 +- .../types/benchmark_tasks_api_service.py | 160 ++++++++- kagglesdk/community/types/content_enums.py | 2 +- .../services/competition_api_service.py | 14 +- .../services/hackathon_service.py | 35 +- .../types/competition_api_service.py | 130 +++++++ .../competitions/types/hackathon_service.py | 328 +++++++----------- kagglesdk/competitions/types/page_service.py | 39 --- kagglesdk/kaggle_client.py | 2 + 9 files changed, 450 insertions(+), 262 deletions(-) delete mode 100644 kagglesdk/competitions/types/page_service.py diff --git a/kagglesdk/__init__.py b/kagglesdk/__init__.py index 5fcc066..977be6a 100644 --- a/kagglesdk/__init__.py +++ b/kagglesdk/__init__.py @@ -1,4 +1,4 @@ -__version__ = "0.1.34" +__version__ = "0.1.35" from kagglesdk.kaggle_client import KaggleClient from kagglesdk.kaggle_creds import KaggleCredentials diff --git a/kagglesdk/benchmarks/types/benchmark_tasks_api_service.py b/kagglesdk/benchmarks/types/benchmark_tasks_api_service.py index 8d41088..94fea04 100644 --- a/kagglesdk/benchmarks/types/benchmark_tasks_api_service.py +++ b/kagglesdk/benchmarks/types/benchmark_tasks_api_service.py @@ -1269,11 +1269,136 @@ def harbor_git(self, harbor_git: Optional['BenchmarkTaskGitDefinition']): class BenchmarkTaskDockerImageDefinition(KaggleObject): r""" - TODO(bml): Setup for docker images + User-supplied Docker image task definition. + Attributes: + docker_image_url (str) + Required. Full image ref (e.g. gcr.io/project/image:tag). + description (str) + Required on new tasks; ignored on version bumps (existing description + carries over). Task description. + title (str) + Optional. Display name for the task. Defaults to the slug when omitted. + Ignored on version bumps. + env_variables (EnvVariable) + Optional. Environment variables set on the container. + secrets (UserSecret) + Optional. Create/update UserSecrets inline before running. + nested_virtualization_required (bool) + Optional. Run the container with nested virtualization enabled — set + when the image needs to launch VMs (e.g. QEMU/KVM guests) inside its + sandbox. Ignored unless the caller has the + BENCHMARKS_ALLOW_NESTED_VIRTUALIZATION feature flag. """ - pass + def __init__(self): + self._docker_image_url = "" + self._description = None + self._title = None + self._env_variables = [] + self._secrets = [] + self._nested_virtualization_required = False + self._freeze() + + @property + def docker_image_url(self) -> str: + """Required. Full image ref (e.g. gcr.io/project/image:tag).""" + return self._docker_image_url + + @docker_image_url.setter + def docker_image_url(self, docker_image_url: str): + if docker_image_url is None: + del self.docker_image_url + return + if not isinstance(docker_image_url, str): + raise TypeError('docker_image_url must be of type str') + self._docker_image_url = docker_image_url + + @property + def description(self) -> str: + r""" + Required on new tasks; ignored on version bumps (existing description + carries over). Task description. + """ + return self._description or "" + + @description.setter + def description(self, description: Optional[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 title(self) -> str: + r""" + Optional. Display name for the task. Defaults to the slug when omitted. + Ignored on version bumps. + """ + return self._title or "" + + @title.setter + def title(self, title: Optional[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 env_variables(self) -> Optional[List[Optional['EnvVariable']]]: + """Optional. Environment variables set on the container.""" + return self._env_variables + + @env_variables.setter + def env_variables(self, env_variables: Optional[List[Optional['EnvVariable']]]): + if env_variables is None: + del self.env_variables + return + if not isinstance(env_variables, list): + raise TypeError('env_variables must be of type list') + if not all([isinstance(t, EnvVariable) for t in env_variables]): + raise TypeError('env_variables must contain only items of type EnvVariable') + self._env_variables = env_variables + + @property + def secrets(self) -> Optional[List[Optional['UserSecret']]]: + """Optional. Create/update UserSecrets inline before running.""" + return self._secrets + + @secrets.setter + def secrets(self, secrets: Optional[List[Optional['UserSecret']]]): + if secrets is None: + del self.secrets + return + if not isinstance(secrets, list): + raise TypeError('secrets must be of type list') + if not all([isinstance(t, UserSecret) for t in secrets]): + raise TypeError('secrets must contain only items of type UserSecret') + self._secrets = secrets + + @property + def nested_virtualization_required(self) -> bool: + r""" + Optional. Run the container with nested virtualization enabled — set + when the image needs to launch VMs (e.g. QEMU/KVM guests) inside its + sandbox. Ignored unless the caller has the + BENCHMARKS_ALLOW_NESTED_VIRTUALIZATION feature flag. + """ + return self._nested_virtualization_required + + @nested_virtualization_required.setter + def nested_virtualization_required(self, nested_virtualization_required: bool): + if nested_virtualization_required is None: + del self.nested_virtualization_required + return + if not isinstance(nested_virtualization_required, bool): + raise TypeError('nested_virtualization_required must be of type bool') + self._nested_virtualization_required = nested_virtualization_required + class BenchmarkTaskGitDefinition(KaggleObject): r""" @@ -1290,6 +1415,9 @@ class BenchmarkTaskGitDefinition(KaggleObject): Optional. Environment variables to expose to the Harbor session container. secrets (UserSecret) Optional. Create/update UserSecrets inline before running. + use_accelerator (bool) + Optional. Route the Harbor session to an L4-GPU exeunit pool. Ignored + unless the caller has the BENCHMARKS_ALLOW_USE_ACCELERATOR feature flag. """ def __init__(self): @@ -1298,6 +1426,7 @@ def __init__(self): self._subpath = "" self._env_variables = [] self._secrets = [] + self._use_accelerator = False self._freeze() @property @@ -1374,6 +1503,23 @@ def secrets(self, secrets: Optional[List[Optional['UserSecret']]]): raise TypeError('secrets must contain only items of type UserSecret') self._secrets = secrets + @property + def use_accelerator(self) -> bool: + r""" + Optional. Route the Harbor session to an L4-GPU exeunit pool. Ignored + unless the caller has the BENCHMARKS_ALLOW_USE_ACCELERATOR feature flag. + """ + return self._use_accelerator + + @use_accelerator.setter + def use_accelerator(self, use_accelerator: bool): + if use_accelerator is None: + del self.use_accelerator + return + if not isinstance(use_accelerator, bool): + raise TypeError('use_accelerator must be of type bool') + self._use_accelerator = use_accelerator + class BenchmarkTaskNotebookDefinition(KaggleObject): r""" @@ -1488,7 +1634,14 @@ class BenchmarkTaskNotebookDefinition(KaggleObject): FieldMetadata("harborGit", "harbor_git", "_harbor_git", BenchmarkTaskGitDefinition, None, KaggleObjectSerializer(), optional=True), ] -BenchmarkTaskDockerImageDefinition._fields = [] +BenchmarkTaskDockerImageDefinition._fields = [ + FieldMetadata("dockerImageUrl", "docker_image_url", "_docker_image_url", str, "", PredefinedSerializer()), + FieldMetadata("description", "description", "_description", str, None, PredefinedSerializer(), optional=True), + FieldMetadata("title", "title", "_title", str, None, PredefinedSerializer(), optional=True), + FieldMetadata("envVariables", "env_variables", "_env_variables", EnvVariable, [], ListSerializer(KaggleObjectSerializer())), + FieldMetadata("secrets", "secrets", "_secrets", UserSecret, [], ListSerializer(KaggleObjectSerializer())), + FieldMetadata("nestedVirtualizationRequired", "nested_virtualization_required", "_nested_virtualization_required", bool, False, PredefinedSerializer()), +] BenchmarkTaskGitDefinition._fields = [ FieldMetadata("url", "url", "_url", str, "", PredefinedSerializer()), @@ -1496,6 +1649,7 @@ class BenchmarkTaskNotebookDefinition(KaggleObject): FieldMetadata("subpath", "subpath", "_subpath", str, "", PredefinedSerializer()), FieldMetadata("envVariables", "env_variables", "_env_variables", EnvVariable, [], ListSerializer(KaggleObjectSerializer())), FieldMetadata("secrets", "secrets", "_secrets", UserSecret, [], ListSerializer(KaggleObjectSerializer())), + FieldMetadata("useAccelerator", "use_accelerator", "_use_accelerator", bool, False, PredefinedSerializer()), ] BenchmarkTaskNotebookDefinition._fields = [] diff --git a/kagglesdk/community/types/content_enums.py b/kagglesdk/community/types/content_enums.py index 2783799..811ed93 100644 --- a/kagglesdk/community/types/content_enums.py +++ b/kagglesdk/community/types/content_enums.py @@ -32,7 +32,7 @@ class ContentState(enum.Enum): SYSTEM_DELETED = 6 r""" Deleted by an admin or by a system account for moderation purposes. This - data may need to be restored in the event of a successfull user appeal, and + data may need to be restored in the event of a successful user appeal, and should not be wiped out. """ PENDING_PERMANENT_DELETE = 7 diff --git a/kagglesdk/competitions/services/competition_api_service.py b/kagglesdk/competitions/services/competition_api_service.py index 14ee901..75838a9 100644 --- a/kagglesdk/competitions/services/competition_api_service.py +++ b/kagglesdk/competitions/services/competition_api_service.py @@ -1,6 +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, ApiCompetitionPage, ApiCompetitionSolutionStatus, ApiCreateCodeSubmissionRequest, ApiCreateCodeSubmissionResponse, ApiCreateCompetitionDataRequest, ApiCreateCompetitionDataResponse, ApiCreateCompetitionPageRequest, ApiCreateCompetitionRequest, ApiCreateCompetitionResponse, ApiCreateCompetitionSampleSubmissionRequest, ApiCreateCompetitionSolutionRequest, ApiCreateSubmissionRequest, ApiCreateSubmissionResponse, ApiDeleteCompetitionPageRequest, ApiDownloadDataFileRequest, ApiDownloadDataFilesRequest, ApiDownloadLeaderboardRequest, ApiGetCompetitionDataFilesSummaryRequest, ApiGetCompetitionRequest, ApiGetCompetitionSettingsRequest, ApiGetCompetitionSolutionStatusRequest, ApiGetEpisodeAgentLogsRequest, ApiGetEpisodeReplayRequest, ApiGetLeaderboardRequest, ApiGetLeaderboardResponse, ApiGetSubmissionRequest, ApiLaunchCompetitionRequest, ApiListCompetitionHostsRequest, ApiListCompetitionHostsResponse, ApiListCompetitionPagesRequest, ApiListCompetitionPagesResponse, ApiListCompetitionsRequest, ApiListCompetitionsResponse, ApiListCompetitionTopicsRequest, ApiListCompetitionTopicsResponse, ApiListDataFilesRequest, ApiListDataFilesResponse, ApiListDataTreeFilesRequest, ApiListSubmissionEpisodesRequest, ApiListSubmissionEpisodesResponse, ApiListSubmissionsRequest, ApiListSubmissionsResponse, ApiListTeamPublicSubmissionsRequest, ApiListTeamPublicSubmissionsResponse, ApiListTopicMessagesRequest, ApiListTopicMessagesResponse, ApiStartSubmissionUploadRequest, ApiStartSubmissionUploadResponse, ApiSubmission, ApiUpdateCompetitionPageRequest, ApiUpdateCompetitionSettingsRequest +from kagglesdk.competitions.types.competition_api_service import ApiCompetition, ApiCompetitionPage, ApiCompetitionSolutionStatus, ApiCreateCodeSubmissionRequest, ApiCreateCodeSubmissionResponse, ApiCreateCompetitionDataRequest, ApiCreateCompetitionDataResponse, ApiCreateCompetitionPageRequest, ApiCreateCompetitionRequest, ApiCreateCompetitionResponse, ApiCreateCompetitionSampleSubmissionRequest, ApiCreateCompetitionSolutionRequest, ApiCreateSubmissionRequest, ApiCreateSubmissionResponse, ApiDeleteCompetitionPageRequest, ApiDownloadDataFileRequest, ApiDownloadDataFilesRequest, ApiDownloadLeaderboardRequest, ApiGetCompetitionDataFilesSummaryRequest, ApiGetCompetitionRequest, ApiGetCompetitionSettingsRequest, ApiGetCompetitionSolutionStatusRequest, ApiGetEpisodeAgentLogsRequest, ApiGetEpisodeReplayRequest, ApiGetLeaderboardRequest, ApiGetLeaderboardResponse, ApiGetSubmissionLimitsRequest, ApiGetSubmissionRequest, ApiLaunchCompetitionRequest, ApiListCompetitionHostsRequest, ApiListCompetitionHostsResponse, ApiListCompetitionPagesRequest, ApiListCompetitionPagesResponse, ApiListCompetitionsRequest, ApiListCompetitionsResponse, ApiListCompetitionTopicsRequest, ApiListCompetitionTopicsResponse, ApiListDataFilesRequest, ApiListDataFilesResponse, ApiListDataTreeFilesRequest, ApiListSubmissionEpisodesRequest, ApiListSubmissionEpisodesResponse, ApiListSubmissionsRequest, ApiListSubmissionsResponse, ApiListTeamPublicSubmissionsRequest, ApiListTeamPublicSubmissionsResponse, ApiListTopicMessagesRequest, ApiListTopicMessagesResponse, ApiStartSubmissionUploadRequest, ApiStartSubmissionUploadResponse, ApiSubmission, ApiSubmissionLimits, ApiUpdateCompetitionPageRequest, ApiUpdateCompetitionSettingsRequest from kagglesdk.competitions.types.host_service import CompetitionSettings from kagglesdk.datasets.databundles.types.databundle_api_types import ApiDirectoryContent, ApiFilesSummary from kagglesdk.kaggle_http_client import KaggleHttpClient @@ -142,6 +142,18 @@ def get_submission(self, request: ApiGetSubmissionRequest = None) -> ApiSubmissi return self._client.call("competitions.CompetitionApiService", "GetSubmission", request, ApiSubmission) + def get_submission_limits(self, request: ApiGetSubmissionLimitsRequest = None) -> ApiSubmissionLimits: + r""" + Args: + request (ApiGetSubmissionLimitsRequest): + The request object; initialized to empty instance if not specified. + """ + + if request is None: + request = ApiGetSubmissionLimitsRequest() + + return self._client.call("competitions.CompetitionApiService", "GetSubmissionLimits", request, ApiSubmissionLimits) + def start_submission_upload(self, request: ApiStartSubmissionUploadRequest = None) -> ApiStartSubmissionUploadResponse: r""" Args: diff --git a/kagglesdk/competitions/services/hackathon_service.py b/kagglesdk/competitions/services/hackathon_service.py index 9783c5f..31a280c 100644 --- a/kagglesdk/competitions/services/hackathon_service.py +++ b/kagglesdk/competitions/services/hackathon_service.py @@ -1,30 +1,19 @@ -from kagglesdk.competitions.types.hackathon_service import ( - ListHackathonTracksRequest, - ListHackathonTracksResponse, -) +from kagglesdk.competitions.types.hackathon_service import ExportEnrichedHackathonWriteUpsCsvRequest, ExportEnrichedHackathonWriteUpsCsvResponse from kagglesdk.kaggle_http_client import KaggleHttpClient - class HackathonClient(object): - def __init__(self, client: KaggleHttpClient): - self._client = client + def __init__(self, client: KaggleHttpClient): + self._client = client - def list_hackathon_tracks( - self, request: ListHackathonTracksRequest = None - ) -> ListHackathonTracksResponse: - r""" - Args: - request (ListHackathonTracksRequest): - The request object; initialized to empty instance if not specified. - """ + def export_enriched_hackathon_write_ups_csv(self, request: ExportEnrichedHackathonWriteUpsCsvRequest = None) -> ExportEnrichedHackathonWriteUpsCsvResponse: + r""" + Args: + request (ExportEnrichedHackathonWriteUpsCsvRequest): + The request object; initialized to empty instance if not specified. + """ - if request is None: - request = ListHackathonTracksRequest() + if request is None: + request = ExportEnrichedHackathonWriteUpsCsvRequest() - return self._client.call( - "competitions.HackathonService", - "ListHackathonTracks", - request, - ListHackathonTracksResponse, - ) + return self._client.call("competitions.HackathonService", "ExportEnrichedHackathonWriteUpsCsv", request, ExportEnrichedHackathonWriteUpsCsvResponse) diff --git a/kagglesdk/competitions/types/competition_api_service.py b/kagglesdk/competitions/types/competition_api_service.py index 4bcaa88..1075c20 100644 --- a/kagglesdk/competitions/types/competition_api_service.py +++ b/kagglesdk/competitions/types/competition_api_service.py @@ -2887,6 +2887,38 @@ def nextPageToken(self): return self.next_page_token +class ApiGetSubmissionLimitsRequest(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}/submissions/limits' + return path.format_map(self.to_field_map(self)) + + @staticmethod + def endpoint_path(): + return '/api/v1/competitions/{competition_name}/submissions/limits' + + class ApiGetSubmissionRequest(KaggleObject): r""" Attributes: @@ -4609,6 +4641,93 @@ def url(self, url: Optional[str]): self._url = url +class ApiSubmissionLimits(KaggleObject): + r""" + The calling user's submission counts and remaining allowance for a + competition. Counts reflect the team the user is on (empty/zeros if the user + is not on a team yet). + + Attributes: + num_today (int) + Number of valid submissions submitted after the beginning of today UTC. + num_total (int) + Number of valid submissions the team has ever made. + num_allowed_now (int) + Number of additional submissions allowed for the rest of today UTC. + limited_by_total (bool) + Whether num_allowed_now was limited by num_total (the competition's + lifetime submission cap) rather than the daily cap. Rare; occurs mainly + when teams merge and land near the total cap. + """ + + def __init__(self): + self._num_today = 0 + self._num_total = 0 + self._num_allowed_now = 0 + self._limited_by_total = False + self._freeze() + + @property + def num_today(self) -> int: + """Number of valid submissions submitted after the beginning of today UTC.""" + return self._num_today + + @num_today.setter + def num_today(self, num_today: int): + if num_today is None: + del self.num_today + return + if not isinstance(num_today, int): + raise TypeError('num_today must be of type int') + self._num_today = num_today + + @property + def num_total(self) -> int: + """Number of valid submissions the team has ever made.""" + return self._num_total + + @num_total.setter + def num_total(self, num_total: int): + if num_total is None: + del self.num_total + return + if not isinstance(num_total, int): + raise TypeError('num_total must be of type int') + self._num_total = num_total + + @property + def num_allowed_now(self) -> int: + """Number of additional submissions allowed for the rest of today UTC.""" + return self._num_allowed_now + + @num_allowed_now.setter + def num_allowed_now(self, num_allowed_now: int): + if num_allowed_now is None: + del self.num_allowed_now + return + if not isinstance(num_allowed_now, int): + raise TypeError('num_allowed_now must be of type int') + self._num_allowed_now = num_allowed_now + + @property + def limited_by_total(self) -> bool: + r""" + Whether num_allowed_now was limited by num_total (the competition's + lifetime submission cap) rather than the daily cap. Rare; occurs mainly + when teams merge and land near the total cap. + """ + return self._limited_by_total + + @limited_by_total.setter + def limited_by_total(self, limited_by_total: bool): + if limited_by_total is None: + del self.limited_by_total + return + if not isinstance(limited_by_total, bool): + raise TypeError('limited_by_total must be of type bool') + self._limited_by_total = limited_by_total + + class ApiTopicMessage(KaggleObject): r""" Attributes: @@ -5173,6 +5292,10 @@ def body_fields(): FieldMetadata("nextPageToken", "next_page_token", "_next_page_token", str, "", PredefinedSerializer()), ] +ApiGetSubmissionLimitsRequest._fields = [ + FieldMetadata("competitionName", "competition_name", "_competition_name", str, "", PredefinedSerializer()), +] + ApiGetSubmissionRequest._fields = [ FieldMetadata("ref", "ref", "_ref", int, 0, PredefinedSerializer()), ] @@ -5340,6 +5463,13 @@ def body_fields(): FieldMetadata("url", "url", "_url", str, None, PredefinedSerializer(), optional=True), ] +ApiSubmissionLimits._fields = [ + FieldMetadata("numToday", "num_today", "_num_today", int, 0, PredefinedSerializer()), + FieldMetadata("numTotal", "num_total", "_num_total", int, 0, PredefinedSerializer()), + FieldMetadata("numAllowedNow", "num_allowed_now", "_num_allowed_now", int, 0, PredefinedSerializer()), + FieldMetadata("limitedByTotal", "limited_by_total", "_limited_by_total", bool, False, PredefinedSerializer()), +] + ApiTopicMessage._fields = [ FieldMetadata("id", "id", "_id", int, 0, PredefinedSerializer()), FieldMetadata("postDate", "post_date", "_post_date", datetime, None, DateTimeSerializer()), diff --git a/kagglesdk/competitions/types/hackathon_service.py b/kagglesdk/competitions/types/hackathon_service.py index 7a4215e..3cf16da 100644 --- a/kagglesdk/competitions/types/hackathon_service.py +++ b/kagglesdk/competitions/types/hackathon_service.py @@ -1,203 +1,143 @@ -from kagglesdk.competitions.types.hackathons import HackathonTrack, HackathonWriteUp +import enum from kagglesdk.kaggle_object import * -from typing import Optional, List - - -class ListHackathonTracksRequest(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_id}/hackathon-tracks" - return path.format_map(self.to_field_map(self)) - - @staticmethod - def endpoint_path(): - return "/api/v1/competitions/{competition_id}/hackathon-tracks" - - -class ListHackathonTracksResponse(KaggleObject): +from typing import Optional + +class ExportEnrichedHackathonWriteUpsCsvRequest(KaggleObject): + r""" + Attributes: + competition_id (int) + competition_name (str) + cancel (bool) + Cancels current generation job by deleting marker file but does not cancel + an already-running generation job and a corresponding CSV file will still + be generated. + """ + + def __init__(self): + self._competition_id = None + self._competition_name = None + self._cancel = 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 + + @property + def cancel(self) -> bool: r""" - Attributes: - tracks (HackathonTrack) + Cancels current generation job by deleting marker file but does not cancel + an already-running generation job and a corresponding CSV file will still + be generated. """ - - 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 ListHackathonWriteUpsResponse(KaggleObject): + return self._cancel or False + + @cancel.setter + def cancel(self, cancel: Optional[bool]): + if cancel is None: + del self.cancel + return + if not isinstance(cancel, bool): + raise TypeError('cancel must be of type bool') + self._cancel = cancel + + def endpoint(self): + path = '/api/v1/competitions/{competition_name}/enriched-hackathon-write-ups-csv' + return path.format_map(self.to_field_map(self)) + + @staticmethod + def endpoint_path(): + return '/api/v1/competitions/{competition_name}/enriched-hackathon-write-ups-csv' + + +class ExportEnrichedHackathonWriteUpsCsvResponse(KaggleObject): + r""" + Attributes: + status (ExportEnrichedHackathonWriteUpsCsvResponse.Status) + signed_url (str) + TODO(b/533012382): Defer GCS URL signing to the UI + Signed GCS URL. Only set when status=READY. + """ + + class Status(enum.Enum): + STATUS_UNSPECIFIED = 0 + READY = 1 + """CSV is available; signed_url is populated.""" + GENERATING = 2 + """Generation was just enqueued or is in progress. Poll again later.""" + CANCELLED = 3 + """The generation marker was cleared in response to cancel=true.""" + + def __init__(self): + self._status = self.Status.STATUS_UNSPECIFIED + self._signed_url = "" + self._freeze() + + @property + def status(self) -> 'ExportEnrichedHackathonWriteUpsCsvResponse.Status': + return self._status + + @status.setter + def status(self, status: 'ExportEnrichedHackathonWriteUpsCsvResponse.Status'): + if status is None: + del self.status + return + if not isinstance(status, ExportEnrichedHackathonWriteUpsCsvResponse.Status): + raise TypeError('status must be of type ExportEnrichedHackathonWriteUpsCsvResponse.Status') + self._status = status + + @property + def signed_url(self) -> str: r""" - Attributes: - hackathon_write_ups (HackathonWriteUp) - next_page_token (str) - total_count (int) + TODO(b/533012382): Defer GCS URL signing to the UI + Signed GCS URL. Only set when status=READY. """ - - 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 - - -ListHackathonTracksRequest._fields = [ - FieldMetadata( - "competitionId", - "competition_id", - "_competition_id", - int, - None, - PredefinedSerializer(), - optional=True, - ), - FieldMetadata( - "competitionName", - "competition_name", - "_competition_name", - str, - None, - PredefinedSerializer(), - optional=True, - ), + return self._signed_url + + @signed_url.setter + def signed_url(self, signed_url: str): + if signed_url is None: + del self.signed_url + return + if not isinstance(signed_url, str): + raise TypeError('signed_url must be of type str') + self._signed_url = signed_url + + @property + def signedUrl(self): + return self.signed_url + + +ExportEnrichedHackathonWriteUpsCsvRequest._fields = [ + FieldMetadata("competitionId", "competition_id", "_competition_id", int, None, PredefinedSerializer(), optional=True), + FieldMetadata("competitionName", "competition_name", "_competition_name", str, None, PredefinedSerializer(), optional=True), + FieldMetadata("cancel", "cancel", "_cancel", bool, None, PredefinedSerializer(), optional=True), ] -ListHackathonTracksResponse._fields = [ - FieldMetadata( - "tracks", - "tracks", - "_tracks", - HackathonTrack, - [], - ListSerializer(KaggleObjectSerializer()), - ), +ExportEnrichedHackathonWriteUpsCsvResponse._fields = [ + FieldMetadata("status", "status", "_status", ExportEnrichedHackathonWriteUpsCsvResponse.Status, ExportEnrichedHackathonWriteUpsCsvResponse.Status.STATUS_UNSPECIFIED, EnumSerializer()), + FieldMetadata("signedUrl", "signed_url", "_signed_url", str, "", PredefinedSerializer()), ] -ListHackathonWriteUpsResponse._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/page_service.py b/kagglesdk/competitions/types/page_service.py deleted file mode 100644 index d55e608..0000000 --- a/kagglesdk/competitions/types/page_service.py +++ /dev/null @@ -1,39 +0,0 @@ -from kagglesdk.competitions.types.page import Page -from kagglesdk.kaggle_object import * -from typing import List, Optional - - -class ListPagesResponse(KaggleObject): - r""" - TODO(aip.dev/158): (-- api-linter: - core::0158::response-next-page-token-field=disabled --) - - 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 - - -ListPagesResponse._fields = [ - FieldMetadata( - "pages", "pages", "_pages", Page, [], ListSerializer(KaggleObjectSerializer()) - ), -] diff --git a/kagglesdk/kaggle_client.py b/kagglesdk/kaggle_client.py index 9b28600..166b88c 100644 --- a/kagglesdk/kaggle_client.py +++ b/kagglesdk/kaggle_client.py @@ -6,6 +6,7 @@ from kagglesdk.common.services.operations_service import OperationsClient from kagglesdk.competitions.services.competition_api_service import CompetitionApiClient from kagglesdk.competitions.services.hackathon_api_service import HackathonApiClient +from kagglesdk.competitions.services.hackathon_service import HackathonClient 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 @@ -49,6 +50,7 @@ class Competitions(object): def __init__(self, http_client: KaggleHttpClient): self.competition_api_client = CompetitionApiClient(http_client) self.hackathon_api_client = HackathonApiClient(http_client) + self.hackathon_client = HackathonClient(http_client) class Datasets(object): def __init__(self, http_client: KaggleHttpClient): From 37198f39b07b59d6753af6be6c0ade214e7c280d Mon Sep 17 00:00:00 2001 From: Bovard Doerschuk-Tiberi Date: Wed, 22 Jul 2026 20:33:14 +0000 Subject: [PATCH 2/2] Pick up small proto changes from latest ci Regenerated from kaggleazure origin/ci to pick up: - `ApiBlobType.COMPETITION_SOLUTION = 4` on blob API. - `WriteUp.is_user_author` field on writeups. --- kagglesdk/blobs/types/blob_api_service.py | 1 + kagglesdk/discussions/types/writeup_types.py | 24 ++++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/kagglesdk/blobs/types/blob_api_service.py b/kagglesdk/blobs/types/blob_api_service.py index 75acd72..2720e4a 100644 --- a/kagglesdk/blobs/types/blob_api_service.py +++ b/kagglesdk/blobs/types/blob_api_service.py @@ -7,6 +7,7 @@ class ApiBlobType(enum.Enum): DATASET = 1 MODEL = 2 INBOX = 3 + COMPETITION_SOLUTION = 4 class ApiStartBlobUploadRequest(KaggleObject): r""" diff --git a/kagglesdk/discussions/types/writeup_types.py b/kagglesdk/discussions/types/writeup_types.py index 7d2ca74..5261c3c 100644 --- a/kagglesdk/discussions/types/writeup_types.py +++ b/kagglesdk/discussions/types/writeup_types.py @@ -304,6 +304,10 @@ class WriteUp(KaggleObject): doi (str) A DataCite DOI reference identifier, if available. e.g. '12.34567/KAGGLE/W/1234567' + is_user_author (bool) + Indicates whether the requesting user is the author of the WriteUp. + This is equivalent to `message.is_user_author`, but sometimes `message` is + dropped from the mask due to performance considerations. """ def __init__(self): @@ -335,6 +339,7 @@ def __init__(self): self._saved_cover_image_url = "" self._saved_thumbnail_image_url = "" self._doi = None + self._is_user_author = False self._freeze() @property @@ -752,6 +757,24 @@ def doi(self, doi: Optional[str]): raise TypeError('doi must be of type str') self._doi = doi + @property + def is_user_author(self) -> bool: + r""" + Indicates whether the requesting user is the author of the WriteUp. + This is equivalent to `message.is_user_author`, but sometimes `message` is + dropped from the mask due to performance considerations. + """ + return self._is_user_author + + @is_user_author.setter + def is_user_author(self, is_user_author: bool): + if is_user_author is None: + del self.is_user_author + return + if not isinstance(is_user_author, bool): + raise TypeError('is_user_author must be of type bool') + self._is_user_author = is_user_author + class WriteUpImageInfo(KaggleObject): r""" @@ -1399,6 +1422,7 @@ def is_phone_verified(self, is_phone_verified: Optional[bool]): FieldMetadata("savedCoverImageUrl", "saved_cover_image_url", "_saved_cover_image_url", str, "", PredefinedSerializer()), FieldMetadata("savedThumbnailImageUrl", "saved_thumbnail_image_url", "_saved_thumbnail_image_url", str, "", PredefinedSerializer()), FieldMetadata("doi", "doi", "_doi", str, None, PredefinedSerializer(), optional=True), + FieldMetadata("isUserAuthor", "is_user_author", "_is_user_author", bool, False, PredefinedSerializer()), ] WriteUpImageInfo._fields = [