From 7c0d969c027435fdb0d0087f569ee66064233570 Mon Sep 17 00:00:00 2001 From: Bovard Doerschuk-Tiberi Date: Fri, 17 Apr 2026 18:30:46 +0000 Subject: [PATCH] Update protos and release 0.1.19 Adds new competition endpoints (episodes, pages, hackathon overview) and a new HackathonService client. --- kagglesdk/__init__.py | 2 +- kagglesdk/benchmarks/types/benchmark_enums.py | 9 + kagglesdk/benchmarks/types/benchmark_types.py | 58 +- .../services/competition_api_service.py | 63 +- .../services/hackathon_service.py | 19 + .../types/competition_api_service.py | 549 ++++++++++++++++++ .../competitions/types/competition_enums.py | 6 + kagglesdk/competitions/types/episode.py | 33 ++ .../competitions/types/hackathon_service.py | 68 ++- kagglesdk/competitions/types/hackathons.py | 203 +++++++ kagglesdk/competitions/types/page.py | 203 +++++++ kagglesdk/competitions/types/page_service.py | 37 ++ kagglesdk/kaggle_client.py | 2 + 13 files changed, 1216 insertions(+), 36 deletions(-) create mode 100644 kagglesdk/competitions/services/hackathon_service.py create mode 100644 kagglesdk/competitions/types/episode.py create mode 100644 kagglesdk/competitions/types/page.py create mode 100644 kagglesdk/competitions/types/page_service.py diff --git a/kagglesdk/__init__.py b/kagglesdk/__init__.py index 2a77bd1..49899af 100644 --- a/kagglesdk/__init__.py +++ b/kagglesdk/__init__.py @@ -1,4 +1,4 @@ -__version__ = "0.1.18" +__version__ = "0.1.19" from kagglesdk.kaggle_client import KaggleClient from kagglesdk.kaggle_creds import KaggleCredentials diff --git a/kagglesdk/benchmarks/types/benchmark_enums.py b/kagglesdk/benchmarks/types/benchmark_enums.py index b7afde2..0dde781 100644 --- a/kagglesdk/benchmarks/types/benchmark_enums.py +++ b/kagglesdk/benchmarks/types/benchmark_enums.py @@ -20,6 +20,15 @@ class BenchmarkTaskVersionCreationState(enum.Enum): BENCHMARK_TASK_VERSION_CREATION_STATE_RUNNING = 2 BENCHMARK_TASK_VERSION_CREATION_STATE_COMPLETED = 3 BENCHMARK_TASK_VERSION_CREATION_STATE_ERRORED = 4 + BENCHMARK_TASK_VERSION_CREATION_STATE_KERNEL_WITHOUT_RUN = 5 + +class Modality(enum.Enum): + """Modality types supported by a benchmark model version.""" + MODALITY_UNSPECIFIED = 0 + MODALITY_TEXT = 1 + MODALITY_IMAGE = 2 + MODALITY_VIDEO = 3 + MODALITY_AUDIO = 4 class BenchmarkTaskRunState(enum.Enum): BENCHMARK_TASK_RUN_STATE_UNSPECIFIED = 0 diff --git a/kagglesdk/benchmarks/types/benchmark_types.py b/kagglesdk/benchmarks/types/benchmark_types.py index 6579861..5fae7fd 100644 --- a/kagglesdk/benchmarks/types/benchmark_types.py +++ b/kagglesdk/benchmarks/types/benchmark_types.py @@ -1,5 +1,5 @@ from datetime import datetime -from kagglesdk.benchmarks.types.benchmark_enums import BenchmarkModelImportanceLevel +from kagglesdk.benchmarks.types.benchmark_enums import BenchmarkModelImportanceLevel, Modality from kagglesdk.kaggle_object import * from kagglesdk.licenses.types.licenses_types import License from kagglesdk.users.types.legacy_organizations_service import OrganizationCard @@ -251,14 +251,10 @@ class BenchmarkModelVersion(KaggleObject): license (License) importance_level (BenchmarkModelImportanceLevel) Whether this model version is run on Kaggle-maintained benchmarks - input_modalities (str) - Comma-separated input modalities supported by this model version. - Valid values: 'text', 'image', 'video', 'audio'. - Example: 'text,image' - output_modalities (str) - Comma-separated output modalities supported by this model version. - Valid values: 'text', 'image', 'video', 'audio'. - Example: 'text,image' + input_modalities (Modality) + Input modalities supported by this model version. + output_modalities (Modality) + Output modalities supported by this model version. """ def __init__(self): @@ -277,8 +273,8 @@ def __init__(self): self._name = None self._license = None self._importance_level = None - self._input_modalities = None - self._output_modalities = None + self._input_modalities = [] + self._output_modalities = [] self._freeze() @property @@ -503,39 +499,35 @@ def importance_level(self, importance_level: Optional['BenchmarkModelImportanceL self._importance_level = importance_level @property - def input_modalities(self) -> str: - r""" - Comma-separated input modalities supported by this model version. - Valid values: 'text', 'image', 'video', 'audio'. - Example: 'text,image' - """ - return self._input_modalities or "" + def input_modalities(self) -> Optional[List['Modality']]: + """Input modalities supported by this model version.""" + return self._input_modalities @input_modalities.setter - def input_modalities(self, input_modalities: Optional[str]): + def input_modalities(self, input_modalities: Optional[List['Modality']]): if input_modalities is None: del self.input_modalities return - if not isinstance(input_modalities, str): - raise TypeError('input_modalities must be of type str') + if not isinstance(input_modalities, list): + raise TypeError('input_modalities must be of type list') + if not all([isinstance(t, Modality) for t in input_modalities]): + raise TypeError('input_modalities must contain only items of type Modality') self._input_modalities = input_modalities @property - def output_modalities(self) -> str: - r""" - Comma-separated output modalities supported by this model version. - Valid values: 'text', 'image', 'video', 'audio'. - Example: 'text,image' - """ - return self._output_modalities or "" + def output_modalities(self) -> Optional[List['Modality']]: + """Output modalities supported by this model version.""" + return self._output_modalities @output_modalities.setter - def output_modalities(self, output_modalities: Optional[str]): + def output_modalities(self, output_modalities: Optional[List['Modality']]): if output_modalities is None: del self.output_modalities return - if not isinstance(output_modalities, str): - raise TypeError('output_modalities must be of type str') + if not isinstance(output_modalities, list): + raise TypeError('output_modalities must be of type list') + if not all([isinstance(t, Modality) for t in output_modalities]): + raise TypeError('output_modalities must contain only items of type Modality') self._output_modalities = output_modalities @@ -846,8 +838,8 @@ def minus(self, minus: float): FieldMetadata("name", "name", "_name", str, None, PredefinedSerializer(), optional=True), FieldMetadata("license", "license", "_license", License, None, KaggleObjectSerializer()), FieldMetadata("importanceLevel", "importance_level", "_importance_level", BenchmarkModelImportanceLevel, None, EnumSerializer(), optional=True), - FieldMetadata("inputModalities", "input_modalities", "_input_modalities", str, None, PredefinedSerializer(), optional=True), - FieldMetadata("outputModalities", "output_modalities", "_output_modalities", str, None, PredefinedSerializer(), optional=True), + FieldMetadata("inputModalities", "input_modalities", "_input_modalities", Modality, [], ListSerializer(EnumSerializer())), + FieldMetadata("outputModalities", "output_modalities", "_output_modalities", Modality, [], ListSerializer(EnumSerializer())), ] BenchmarkResult._fields = [ diff --git a/kagglesdk/competitions/services/competition_api_service.py b/kagglesdk/competitions/services/competition_api_service.py index aa5c1a6..fcf9acd 100644 --- a/kagglesdk/competitions/services/competition_api_service.py +++ b/kagglesdk/competitions/services/competition_api_service.py @@ -1,8 +1,9 @@ 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, ApiGetHackathonWriteUpRequest, ApiGetLeaderboardRequest, ApiGetLeaderboardResponse, ApiGetSubmissionRequest, ApiListCompetitionsRequest, ApiListCompetitionsResponse, ApiListDataFilesRequest, ApiListDataFilesResponse, ApiListDataTreeFilesRequest, ApiListHackathonWriteUpsRequest, ApiListSubmissionsRequest, ApiListSubmissionsResponse, ApiStartSubmissionUploadRequest, ApiStartSubmissionUploadResponse, ApiSubmission +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, ApiListDataFilesRequest, ApiListDataFilesResponse, ApiListDataTreeFilesRequest, ApiListHackathonWriteUpsRequest, ApiListSubmissionEpisodesRequest, ApiListSubmissionEpisodesResponse, ApiListSubmissionsRequest, ApiListSubmissionsResponse, 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.datasets.databundles.types.databundle_api_types import ApiDirectoryContent, ApiFilesSummary from kagglesdk.kaggle_http_client import KaggleHttpClient @@ -202,3 +203,63 @@ def get_hackathon_write_up(self, request: ApiGetHackathonWriteUpRequest = None) request = ApiGetHackathonWriteUpRequest() return self._client.call("competitions.CompetitionApiService", "GetHackathonWriteUp", request, HackathonWriteUp) + + def list_submission_episodes(self, request: ApiListSubmissionEpisodesRequest = None) -> ApiListSubmissionEpisodesResponse: + r""" + Args: + request (ApiListSubmissionEpisodesRequest): + The request object; initialized to empty instance if not specified. + """ + + if request is None: + request = ApiListSubmissionEpisodesRequest() + + return self._client.call("competitions.CompetitionApiService", "ListSubmissionEpisodes", request, ApiListSubmissionEpisodesResponse) + + def get_episode_replay(self, request: ApiGetEpisodeReplayRequest = None) -> FileDownload: + r""" + Args: + request (ApiGetEpisodeReplayRequest): + The request object; initialized to empty instance if not specified. + """ + + if request is None: + request = ApiGetEpisodeReplayRequest() + + return self._client.call("competitions.CompetitionApiService", "GetEpisodeReplay", request, FileDownload) + + def get_episode_agent_logs(self, request: ApiGetEpisodeAgentLogsRequest = None) -> FileDownload: + r""" + Args: + request (ApiGetEpisodeAgentLogsRequest): + The request object; initialized to empty instance if not specified. + """ + + if request is None: + request = ApiGetEpisodeAgentLogsRequest() + + return self._client.call("competitions.CompetitionApiService", "GetEpisodeAgentLogs", request, FileDownload) + + def list_competition_pages(self, request: ApiListCompetitionPagesRequest = None) -> ApiListCompetitionPagesResponse: + r""" + Args: + request (ApiListCompetitionPagesRequest): + The request object; initialized to empty instance if not specified. + """ + + if request is None: + request = ApiListCompetitionPagesRequest() + + 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) diff --git a/kagglesdk/competitions/services/hackathon_service.py b/kagglesdk/competitions/services/hackathon_service.py new file mode 100644 index 0000000..837e800 --- /dev/null +++ b/kagglesdk/competitions/services/hackathon_service.py @@ -0,0 +1,19 @@ +from kagglesdk.competitions.types.hackathon_service import ListHackathonTracksRequest, ListHackathonTracksResponse +from kagglesdk.kaggle_http_client import KaggleHttpClient + +class HackathonClient(object): + + 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. + """ + + if request is None: + request = ListHackathonTracksRequest() + + return self._client.call("competitions.HackathonService", "ListHackathonTracks", request, ListHackathonTracksResponse) diff --git a/kagglesdk/competitions/types/competition_api_service.py b/kagglesdk/competitions/types/competition_api_service.py index bac0354..0233638 100644 --- a/kagglesdk/competitions/types/competition_api_service.py +++ b/kagglesdk/competitions/types/competition_api_service.py @@ -1,5 +1,6 @@ from datetime import datetime from kagglesdk.competitions.types.competition_enums import CompetitionListTab, CompetitionSortBy, HostSegment, SubmissionGroup, SubmissionSortBy +from kagglesdk.competitions.types.episode import EpisodeAgentState, EpisodeState, EpisodeType from kagglesdk.competitions.types.submission_status import SubmissionStatus from kagglesdk.kaggle_object import * from typing import Optional, List @@ -551,6 +552,53 @@ def date_created(self, date_created: datetime): self._date_created = date_created +class ApiCompetitionPage(KaggleObject): + r""" + Attributes: + name (str) + Page name (e.g. 'description', 'rules', 'evaluation', 'data-description', + 'prizes'). + content (str) + The rendered content of the page. + """ + + def __init__(self): + self._name = "" + self._content = "" + self._freeze() + + @property + def name(self) -> str: + r""" + Page name (e.g. 'description', 'rules', 'evaluation', 'data-description', + 'prizes'). + """ + return self._name + + @name.setter + def name(self, name: str): + if name is None: + del self.name + return + if not isinstance(name, str): + raise TypeError('name must be of type str') + self._name = name + + @property + def content(self) -> str: + """The rendered content of the page.""" + return self._content + + @content.setter + def content(self, content: str): + if content is None: + del self.content + return + if not isinstance(content, str): + raise TypeError('content must be of type str') + self._content = content + + class ApiCreateCodeSubmissionRequest(KaggleObject): r""" Attributes: @@ -1072,6 +1120,206 @@ def endpoint_path(): return '/api/v1/competitions/{competition_name}/leaderboard/download' +class ApiEpisode(KaggleObject): + r""" + Attributes: + id (int) + create_time (datetime) + end_time (datetime) + state (EpisodeState) + type (EpisodeType) + agents (ApiEpisodeAgent) + """ + + def __init__(self): + self._id = 0 + self._create_time = None + self._end_time = None + self._state = EpisodeState.EPISODE_STATE_UNSPECIFIED + self._type = EpisodeType.EPISODE_TYPE_UNSPECIFIED + self._agents = [] + self._freeze() + + @property + def id(self) -> int: + return self._id + + @id.setter + def id(self, id: int): + if id is None: + del self.id + return + if not isinstance(id, int): + raise TypeError('id must be of type int') + self._id = id + + @property + def create_time(self) -> datetime: + return self._create_time + + @create_time.setter + def create_time(self, create_time: datetime): + if create_time is None: + del self.create_time + return + if not isinstance(create_time, datetime): + raise TypeError('create_time must be of type datetime') + self._create_time = create_time + + @property + def end_time(self) -> datetime: + return self._end_time + + @end_time.setter + def end_time(self, end_time: datetime): + if end_time is None: + del self.end_time + return + if not isinstance(end_time, datetime): + raise TypeError('end_time must be of type datetime') + self._end_time = end_time + + @property + def state(self) -> 'EpisodeState': + return self._state + + @state.setter + def state(self, state: 'EpisodeState'): + if state is None: + del self.state + return + if not isinstance(state, EpisodeState): + raise TypeError('state must be of type EpisodeState') + self._state = state + + @property + def type(self) -> 'EpisodeType': + return self._type + + @type.setter + def type(self, type: 'EpisodeType'): + if type is None: + del self.type + return + if not isinstance(type, EpisodeType): + raise TypeError('type must be of type EpisodeType') + self._type = type + + @property + def agents(self) -> Optional[List[Optional['ApiEpisodeAgent']]]: + return self._agents + + @agents.setter + def agents(self, agents: Optional[List[Optional['ApiEpisodeAgent']]]): + if agents is None: + del self.agents + return + if not isinstance(agents, list): + raise TypeError('agents must be of type list') + if not all([isinstance(t, ApiEpisodeAgent) for t in agents]): + raise TypeError('agents must contain only items of type ApiEpisodeAgent') + self._agents = agents + + +class ApiEpisodeAgent(KaggleObject): + r""" + Attributes: + submission_id (int) + index (int) + reward (float) + state (EpisodeAgentState) + team_name (str) + team_id (int) + """ + + def __init__(self): + self._submission_id = 0 + self._index = 0 + self._reward = None + self._state = EpisodeAgentState.EPISODE_AGENT_STATE_UNSPECIFIED + self._team_name = None + self._team_id = 0 + self._freeze() + + @property + def submission_id(self) -> int: + return self._submission_id + + @submission_id.setter + def submission_id(self, submission_id: int): + if submission_id is None: + del self.submission_id + return + if not isinstance(submission_id, int): + raise TypeError('submission_id must be of type int') + self._submission_id = submission_id + + @property + def index(self) -> int: + return self._index + + @index.setter + def index(self, index: int): + if index is None: + del self.index + return + if not isinstance(index, int): + raise TypeError('index must be of type int') + self._index = index + + @property + def reward(self) -> float: + return self._reward or 0.0 + + @reward.setter + def reward(self, reward: Optional[float]): + if reward is None: + del self.reward + return + if not isinstance(reward, float): + raise TypeError('reward must be of type float') + self._reward = reward + + @property + def state(self) -> 'EpisodeAgentState': + return self._state + + @state.setter + def state(self, state: 'EpisodeAgentState'): + if state is None: + del self.state + return + if not isinstance(state, EpisodeAgentState): + raise TypeError('state must be of type EpisodeAgentState') + self._state = state + + @property + def team_name(self) -> str: + return self._team_name or "" + + @team_name.setter + def team_name(self, team_name: Optional[str]): + if team_name is None: + del self.team_name + return + if not isinstance(team_name, str): + raise TypeError('team_name must be of type str') + self._team_name = team_name + + @property + def team_id(self) -> int: + return self._team_id + + @team_id.setter + def team_id(self, team_id: 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 + + class ApiGetCompetitionDataFilesSummaryRequest(KaggleObject): r""" Attributes: @@ -1136,6 +1384,117 @@ def endpoint_path(): return '/api/v1/competitions/get/{competition_name}' +class ApiGetEpisodeAgentLogsRequest(KaggleObject): + r""" + Attributes: + episode_id (int) + agent_index (int) + """ + + def __init__(self): + self._episode_id = 0 + self._agent_index = 0 + self._freeze() + + @property + def episode_id(self) -> int: + return self._episode_id + + @episode_id.setter + def episode_id(self, episode_id: int): + if episode_id is None: + del self.episode_id + return + if not isinstance(episode_id, int): + raise TypeError('episode_id must be of type int') + self._episode_id = episode_id + + @property + def agent_index(self) -> int: + return self._agent_index + + @agent_index.setter + def agent_index(self, agent_index: int): + if agent_index is None: + del self.agent_index + return + if not isinstance(agent_index, int): + raise TypeError('agent_index must be of type int') + self._agent_index = agent_index + + def endpoint(self): + path = '/api/v1/competitions/episodes/{episode_id}/agents/{agent_index}/logs' + return path.format_map(self.to_field_map(self)) + + @staticmethod + def endpoint_path(): + return '/api/v1/competitions/episodes/{episode_id}/agents/{agent_index}/logs' + + +class ApiGetEpisodeReplayRequest(KaggleObject): + r""" + Attributes: + episode_id (int) + """ + + def __init__(self): + self._episode_id = 0 + self._freeze() + + @property + def episode_id(self) -> int: + return self._episode_id + + @episode_id.setter + def episode_id(self, episode_id: int): + if episode_id is None: + del self.episode_id + return + if not isinstance(episode_id, int): + raise TypeError('episode_id must be of type int') + self._episode_id = episode_id + + def endpoint(self): + path = '/api/v1/competitions/episodes/{episode_id}/replay' + return path.format_map(self.to_field_map(self)) + + @staticmethod + 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: @@ -1419,6 +1778,85 @@ def score(self, score: Optional[str]): self._score = score +class ApiListCompetitionPagesRequest(KaggleObject): + r""" + Attributes: + competition_name (str) + page_name (str) + Optional filter to return only the page with this name (e.g. 'description', + 'rules', 'evaluation'). If empty, all published pages are returned. + """ + + def __init__(self): + self._competition_name = "" + self._page_name = 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 page_name(self) -> str: + r""" + Optional filter to return only the page with this name (e.g. 'description', + 'rules', 'evaluation'). If empty, all published pages are returned. + """ + return self._page_name or "" + + @page_name.setter + def page_name(self, page_name: Optional[str]): + if page_name is None: + del self.page_name + return + if not isinstance(page_name, str): + raise TypeError('page_name must be of type str') + self._page_name = page_name + + def endpoint(self): + path = '/api/v1/competitions/{competition_name}/pages' + return path.format_map(self.to_field_map(self)) + + @staticmethod + def endpoint_path(): + return '/api/v1/competitions/{competition_name}/pages' + + +class ApiListCompetitionPagesResponse(KaggleObject): + r""" + Attributes: + pages (ApiCompetitionPage) + """ + + def __init__(self): + self._pages = [] + self._freeze() + + @property + def pages(self) -> Optional[List[Optional['ApiCompetitionPage']]]: + return self._pages + + @pages.setter + def pages(self, pages: Optional[List[Optional['ApiCompetitionPage']]]): + 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, ApiCompetitionPage) for t in pages]): + raise TypeError('pages must contain only items of type ApiCompetitionPage') + self._pages = pages + + class ApiListCompetitionsRequest(KaggleObject): r""" Attributes: @@ -1915,6 +2353,64 @@ def endpoint_path(): return '/api/v1/competitions/{competition_name}/hackathon-write-ups' +class ApiListSubmissionEpisodesRequest(KaggleObject): + r""" + Attributes: + submission_id (int) + """ + + def __init__(self): + self._submission_id = 0 + self._freeze() + + @property + def submission_id(self) -> int: + return self._submission_id + + @submission_id.setter + def submission_id(self, submission_id: int): + if submission_id is None: + del self.submission_id + return + if not isinstance(submission_id, int): + raise TypeError('submission_id must be of type int') + self._submission_id = submission_id + + def endpoint(self): + path = '/api/v1/competitions/submissions/{submission_id}/episodes' + return path.format_map(self.to_field_map(self)) + + @staticmethod + def endpoint_path(): + return '/api/v1/competitions/submissions/{submission_id}/episodes' + + +class ApiListSubmissionEpisodesResponse(KaggleObject): + r""" + Attributes: + episodes (ApiEpisode) + """ + + def __init__(self): + self._episodes = [] + self._freeze() + + @property + def episodes(self) -> Optional[List[Optional['ApiEpisode']]]: + return self._episodes + + @episodes.setter + def episodes(self, episodes: Optional[List[Optional['ApiEpisode']]]): + if episodes is None: + del self.episodes + return + if not isinstance(episodes, list): + raise TypeError('episodes must be of type list') + if not all([isinstance(t, ApiEpisode) for t in episodes]): + raise TypeError('episodes must contain only items of type ApiEpisode') + self._episodes = episodes + + class ApiListSubmissionsRequest(KaggleObject): r""" Attributes: @@ -2444,6 +2940,11 @@ def url(self, url: Optional[str]): FieldMetadata("dateCreated", "date_created", "_date_created", datetime, None, DateTimeSerializer()), ] +ApiCompetitionPage._fields = [ + FieldMetadata("name", "name", "_name", str, "", PredefinedSerializer()), + FieldMetadata("content", "content", "_content", str, "", PredefinedSerializer()), +] + ApiCreateCodeSubmissionRequest._fields = [ FieldMetadata("competitionName", "competition_name", "_competition_name", str, "", PredefinedSerializer()), FieldMetadata("kernelOwner", "kernel_owner", "_kernel_owner", str, "", PredefinedSerializer()), @@ -2493,6 +2994,24 @@ def url(self, url: Optional[str]): FieldMetadata("competitionName", "competition_name", "_competition_name", str, "", PredefinedSerializer()), ] +ApiEpisode._fields = [ + FieldMetadata("id", "id", "_id", int, 0, PredefinedSerializer()), + FieldMetadata("createTime", "create_time", "_create_time", datetime, None, DateTimeSerializer()), + FieldMetadata("endTime", "end_time", "_end_time", datetime, None, DateTimeSerializer()), + FieldMetadata("state", "state", "_state", EpisodeState, EpisodeState.EPISODE_STATE_UNSPECIFIED, EnumSerializer()), + FieldMetadata("type", "type", "_type", EpisodeType, EpisodeType.EPISODE_TYPE_UNSPECIFIED, EnumSerializer()), + FieldMetadata("agents", "agents", "_agents", ApiEpisodeAgent, [], ListSerializer(KaggleObjectSerializer())), +] + +ApiEpisodeAgent._fields = [ + FieldMetadata("submissionId", "submission_id", "_submission_id", int, 0, PredefinedSerializer()), + FieldMetadata("index", "index", "_index", int, 0, PredefinedSerializer()), + FieldMetadata("reward", "reward", "_reward", float, None, PredefinedSerializer(), optional=True), + FieldMetadata("state", "state", "_state", EpisodeAgentState, EpisodeAgentState.EPISODE_AGENT_STATE_UNSPECIFIED, EnumSerializer()), + FieldMetadata("teamName", "team_name", "_team_name", str, None, PredefinedSerializer(), optional=True), + FieldMetadata("teamId", "team_id", "_team_id", int, 0, PredefinedSerializer()), +] + ApiGetCompetitionDataFilesSummaryRequest._fields = [ FieldMetadata("competitionName", "competition_name", "_competition_name", str, "", PredefinedSerializer()), ] @@ -2501,6 +3020,19 @@ def url(self, url: Optional[str]): FieldMetadata("competitionName", "competition_name", "_competition_name", str, "", PredefinedSerializer()), ] +ApiGetEpisodeAgentLogsRequest._fields = [ + FieldMetadata("episodeId", "episode_id", "_episode_id", int, 0, PredefinedSerializer()), + FieldMetadata("agentIndex", "agent_index", "_agent_index", int, 0, PredefinedSerializer()), +] + +ApiGetEpisodeReplayRequest._fields = [ + 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()), @@ -2529,6 +3061,15 @@ def url(self, url: Optional[str]): FieldMetadata("score", "score", "_score", str, None, PredefinedSerializer(), optional=True), ] +ApiListCompetitionPagesRequest._fields = [ + FieldMetadata("competitionName", "competition_name", "_competition_name", str, "", PredefinedSerializer()), + FieldMetadata("pageName", "page_name", "_page_name", str, None, PredefinedSerializer(), optional=True), +] + +ApiListCompetitionPagesResponse._fields = [ + FieldMetadata("pages", "pages", "_pages", ApiCompetitionPage, [], ListSerializer(KaggleObjectSerializer())), +] + ApiListCompetitionsRequest._fields = [ FieldMetadata("group", "group", "_group", CompetitionListTab, None, EnumSerializer(), optional=True), FieldMetadata("category", "category", "_category", HostSegment, None, EnumSerializer(), optional=True), @@ -2571,6 +3112,14 @@ def url(self, url: Optional[str]): FieldMetadata("pageToken", "page_token", "_page_token", str, None, PredefinedSerializer(), optional=True), ] +ApiListSubmissionEpisodesRequest._fields = [ + FieldMetadata("submissionId", "submission_id", "_submission_id", int, 0, PredefinedSerializer()), +] + +ApiListSubmissionEpisodesResponse._fields = [ + FieldMetadata("episodes", "episodes", "_episodes", ApiEpisode, [], ListSerializer(KaggleObjectSerializer())), +] + ApiListSubmissionsRequest._fields = [ FieldMetadata("competitionName", "competition_name", "_competition_name", str, "", PredefinedSerializer()), FieldMetadata("sortBy", "sort_by", "_sort_by", SubmissionSortBy, SubmissionSortBy.SUBMISSION_SORT_BY_DATE, EnumSerializer()), diff --git a/kagglesdk/competitions/types/competition_enums.py b/kagglesdk/competitions/types/competition_enums.py index f315cd6..704f573 100644 --- a/kagglesdk/competitions/types/competition_enums.py +++ b/kagglesdk/competitions/types/competition_enums.py @@ -51,3 +51,9 @@ 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 new file mode 100644 index 0000000..f18a378 --- /dev/null +++ b/kagglesdk/competitions/types/episode.py @@ -0,0 +1,33 @@ +import enum + +class EpisodeAgentState(enum.Enum): + EPISODE_AGENT_STATE_UNSPECIFIED = 0 + EPISODE_AGENT_STATE_PENDING = 1 + """The Episode has not yet completed.""" + EPISODE_AGENT_STATE_COMPLETE = 2 + """The Agent completed the Episode.""" + EPISODE_AGENT_STATE_ERROR_EVALUATION = 3 + """The Agent threw an Exception during evaluation, or similar.""" + EPISODE_AGENT_STATE_ERROR_INVALID_ACTION = 4 + """The Agent returned an invalid action and was disqualified.""" + EPISODE_AGENT_STATE_ERROR_TIMEOUT = 5 + """The Agent did not return its action in the required time.""" + EPISODE_AGENT_STATE_ERROR_ON_EPISODE = 6 + """The Episode failed for unexpected reasons.""" + +class EpisodeState(enum.Enum): + EPISODE_STATE_UNSPECIFIED = 0 + CREATED = 1 + COMPLETED = 2 + ERRORED = 3 + NEVER_STARTED = 4 + +class EpisodeType(enum.Enum): + """Controls whether score is written and where""" + EPISODE_TYPE_UNSPECIFIED = 0 + EPISODE_TYPE_PUBLIC = 1 + EPISODE_TYPE_PRIVATE = 2 + """This is deprecated / was never supported.""" + EPISODE_TYPE_EXHIBITION = 3 + EPISODE_TYPE_VALIDATION = 4 + diff --git a/kagglesdk/competitions/types/hackathon_service.py b/kagglesdk/competitions/types/hackathon_service.py index 960ed85..a15fe8f 100644 --- a/kagglesdk/competitions/types/hackathon_service.py +++ b/kagglesdk/competitions/types/hackathon_service.py @@ -1,7 +1,65 @@ -from kagglesdk.competitions.types.hackathons import HackathonWriteUp +from kagglesdk.competitions.types.hackathons import HackathonTrack, HackathonWriteUp from kagglesdk.kaggle_object import * from typing import List, Optional +class ListHackathonTracksRequest(KaggleObject): + r""" + Attributes: + competition_id (int) + """ + + def __init__(self): + self._competition_id = 0 + self._freeze() + + @property + def competition_id(self) -> int: + return self._competition_id + + @competition_id.setter + def competition_id(self, competition_id: 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 + + 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): + 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 ListHackathonWriteUpsResponse(KaggleObject): r""" Attributes: @@ -70,6 +128,14 @@ def totalCount(self): return self.total_count +ListHackathonTracksRequest._fields = [ + FieldMetadata("competitionId", "competition_id", "_competition_id", int, 0, PredefinedSerializer()), +] + +ListHackathonTracksResponse._fields = [ + FieldMetadata("tracks", "tracks", "_tracks", HackathonTrack, [], ListSerializer(KaggleObjectSerializer())), +] + ListHackathonWriteUpsResponse._fields = [ FieldMetadata("hackathonWriteUps", "hackathon_write_ups", "_hackathon_write_ups", HackathonWriteUp, [], ListSerializer(KaggleObjectSerializer())), FieldMetadata("nextPageToken", "next_page_token", "_next_page_token", str, "", PredefinedSerializer()), diff --git a/kagglesdk/competitions/types/hackathons.py b/kagglesdk/competitions/types/hackathons.py index 5a3784e..9055230 100644 --- a/kagglesdk/competitions/types/hackathons.py +++ b/kagglesdk/competitions/types/hackathons.py @@ -1,8 +1,194 @@ +from kagglesdk.competitions.types.competition_enums import HackathonTrackPrizeType 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 +class HackathonTrack(KaggleObject): + r""" + Attributes: + id (int) + title (str) + description (str) + prizes (HackathonTrackPrize) + order_index (float) + """ + + def __init__(self): + self._id = 0 + self._title = "" + self._description = "" + self._prizes = [] + self._order_index = 0.0 + self._freeze() + + @property + def id(self) -> int: + return self._id + + @id.setter + def id(self, id: int): + if id is None: + del self.id + return + if not isinstance(id, int): + raise TypeError('id must be of type int') + self._id = id + + @property + def title(self) -> str: + 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: + 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 prizes(self) -> Optional[List[Optional['HackathonTrackPrize']]]: + return self._prizes + + @prizes.setter + def prizes(self, prizes: Optional[List[Optional['HackathonTrackPrize']]]): + if prizes is None: + del self.prizes + return + if not isinstance(prizes, list): + raise TypeError('prizes must be of type list') + if not all([isinstance(t, HackathonTrackPrize) for t in prizes]): + raise TypeError('prizes must contain only items of type HackathonTrackPrize') + self._prizes = prizes + + @property + def order_index(self) -> float: + return self._order_index + + @order_index.setter + def order_index(self, order_index: float): + if order_index is None: + del self.order_index + return + if not isinstance(order_index, float): + raise TypeError('order_index must be of type float') + self._order_index = order_index + + +class HackathonTrackPrize(KaggleObject): + r""" + Attributes: + id (int) + title (str) + type (HackathonTrackPrizeType) + amount_usd (int) + description (str) + order_index (float) + """ + + def __init__(self): + self._id = 0 + self._title = "" + self._type = HackathonTrackPrizeType.HACKATHON_TRACK_PRIZE_TYPE_UNSPECIFIED + self._amount_usd = 0 + self._description = "" + self._order_index = 0.0 + self._freeze() + + @property + def id(self) -> int: + return self._id + + @id.setter + def id(self, id: int): + if id is None: + del self.id + return + if not isinstance(id, int): + raise TypeError('id must be of type int') + self._id = id + + @property + def title(self) -> str: + 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 type(self) -> 'HackathonTrackPrizeType': + return self._type + + @type.setter + def type(self, type: 'HackathonTrackPrizeType'): + if type is None: + del self.type + return + if not isinstance(type, HackathonTrackPrizeType): + raise TypeError('type must be of type HackathonTrackPrizeType') + self._type = type + + @property + def amount_usd(self) -> int: + return self._amount_usd + + @amount_usd.setter + def amount_usd(self, amount_usd: int): + if amount_usd is None: + del self.amount_usd + return + if not isinstance(amount_usd, int): + raise TypeError('amount_usd must be of type int') + self._amount_usd = amount_usd + + @property + def description(self) -> str: + 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 order_index(self) -> float: + return self._order_index + + @order_index.setter + def order_index(self, order_index: float): + if order_index is None: + del self.order_index + return + if not isinstance(order_index, float): + raise TypeError('order_index must be of type float') + self._order_index = order_index + + class HackathonWriteUp(KaggleObject): r""" Attributes: @@ -151,6 +337,23 @@ def owner_judge_user_id(self, owner_judge_user_id: Optional[int]): self._owner_judge_user_id = owner_judge_user_id +HackathonTrack._fields = [ + FieldMetadata("id", "id", "_id", int, 0, PredefinedSerializer()), + FieldMetadata("title", "title", "_title", str, "", PredefinedSerializer()), + FieldMetadata("description", "description", "_description", str, "", PredefinedSerializer()), + FieldMetadata("prizes", "prizes", "_prizes", HackathonTrackPrize, [], ListSerializer(KaggleObjectSerializer())), + FieldMetadata("orderIndex", "order_index", "_order_index", float, 0.0, PredefinedSerializer()), +] + +HackathonTrackPrize._fields = [ + FieldMetadata("id", "id", "_id", int, 0, PredefinedSerializer()), + FieldMetadata("title", "title", "_title", str, "", PredefinedSerializer()), + FieldMetadata("type", "type", "_type", HackathonTrackPrizeType, HackathonTrackPrizeType.HACKATHON_TRACK_PRIZE_TYPE_UNSPECIFIED, EnumSerializer()), + FieldMetadata("amountUsd", "amount_usd", "_amount_usd", int, 0, PredefinedSerializer()), + FieldMetadata("description", "description", "_description", str, "", PredefinedSerializer()), + FieldMetadata("orderIndex", "order_index", "_order_index", float, 0.0, PredefinedSerializer()), +] + HackathonWriteUp._fields = [ FieldMetadata("id", "id", "_id", int, 0, PredefinedSerializer()), FieldMetadata("team", "team", "_team", Team, None, KaggleObjectSerializer(), optional=True), diff --git a/kagglesdk/competitions/types/page.py b/kagglesdk/competitions/types/page.py new file mode 100644 index 0000000..92b5f75 --- /dev/null +++ b/kagglesdk/competitions/types/page.py @@ -0,0 +1,203 @@ +from kagglesdk.kaggle_object import * +from typing import Optional + +class Page(KaggleObject): + r""" + Representation of a top-level Competition or Benchmark page. + Note that this does not map directly to the Page database entity, + as it also includes elements of the Post entity (contents, mime_type). + + Attributes: + id (int) + Unique identifier of the Page + competition_id (int) + If set, identifier of the containing Competition + benchmark_version_id (int) + If set, identifier of the containing Benchmark Version + name (str) + The Page name/title + content (str) + Contents of the page (to be rendered according to 'mime_type') + mime_type (str) + The MIME type of the 'content' field + is_published (bool) + Whether the page is publicly visible + order (int) + The order of the page within the competition + post_title (str) + The title of the post associated with the page + post_id (int) + The id of the post associated with the page - used to warn about concurrent + edits + """ + + def __init__(self): + self._id = 0 + self._competition_id = None + self._benchmark_version_id = None + self._name = "" + self._content = "" + self._mime_type = "" + self._is_published = False + self._order = None + self._post_title = None + self._post_id = 0 + self._freeze() + + @property + def id(self) -> int: + """Unique identifier of the Page""" + return self._id + + @id.setter + def id(self, id: int): + if id is None: + del self.id + return + if not isinstance(id, int): + raise TypeError('id must be of type int') + self._id = id + + @property + def competition_id(self) -> int: + """If set, identifier of the containing Competition""" + 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 benchmark_version_id(self) -> int: + """If set, identifier of the containing Benchmark Version""" + return self._benchmark_version_id or 0 + + @benchmark_version_id.setter + def benchmark_version_id(self, benchmark_version_id: Optional[int]): + if benchmark_version_id is None: + del self.benchmark_version_id + return + if not isinstance(benchmark_version_id, int): + raise TypeError('benchmark_version_id must be of type int') + self._benchmark_version_id = benchmark_version_id + + @property + def name(self) -> str: + """The Page name/title""" + return self._name + + @name.setter + def name(self, name: str): + if name is None: + del self.name + return + if not isinstance(name, str): + raise TypeError('name must be of type str') + self._name = name + + @property + def content(self) -> str: + """Contents of the page (to be rendered according to 'mime_type')""" + return self._content + + @content.setter + def content(self, content: str): + if content is None: + del self.content + return + if not isinstance(content, str): + raise TypeError('content must be of type str') + self._content = content + + @property + def mime_type(self) -> str: + """The MIME type of the 'content' field""" + return self._mime_type + + @mime_type.setter + def mime_type(self, mime_type: str): + if mime_type is None: + del self.mime_type + return + if not isinstance(mime_type, str): + raise TypeError('mime_type must be of type str') + self._mime_type = mime_type + + @property + def is_published(self) -> bool: + """Whether the page is publicly visible""" + return self._is_published + + @is_published.setter + def is_published(self, is_published: bool): + if is_published is None: + del self.is_published + return + if not isinstance(is_published, bool): + raise TypeError('is_published must be of type bool') + self._is_published = is_published + + @property + def order(self) -> int: + """The order of the page within the competition""" + return self._order or 0 + + @order.setter + def order(self, order: Optional[int]): + if order is None: + del self.order + return + if not isinstance(order, int): + raise TypeError('order must be of type int') + self._order = order + + @property + def post_title(self) -> str: + """The title of the post associated with the page""" + return self._post_title or "" + + @post_title.setter + def post_title(self, post_title: Optional[str]): + if post_title is None: + del self.post_title + return + if not isinstance(post_title, str): + raise TypeError('post_title must be of type str') + self._post_title = post_title + + @property + def post_id(self) -> int: + r""" + The id of the post associated with the page - used to warn about concurrent + edits + """ + return self._post_id + + @post_id.setter + def post_id(self, post_id: int): + if post_id is None: + del self.post_id + return + if not isinstance(post_id, int): + raise TypeError('post_id must be of type int') + self._post_id = post_id + + +Page._fields = [ + FieldMetadata("id", "id", "_id", int, 0, PredefinedSerializer()), + FieldMetadata("competitionId", "competition_id", "_competition_id", int, None, PredefinedSerializer(), optional=True), + FieldMetadata("benchmarkVersionId", "benchmark_version_id", "_benchmark_version_id", int, None, PredefinedSerializer(), optional=True), + FieldMetadata("name", "name", "_name", str, "", PredefinedSerializer()), + FieldMetadata("content", "content", "_content", str, "", PredefinedSerializer()), + FieldMetadata("mimeType", "mime_type", "_mime_type", str, "", PredefinedSerializer()), + FieldMetadata("isPublished", "is_published", "_is_published", bool, False, PredefinedSerializer()), + FieldMetadata("order", "order", "_order", int, None, PredefinedSerializer(), optional=True), + FieldMetadata("postTitle", "post_title", "_post_title", str, None, PredefinedSerializer(), optional=True), + FieldMetadata("postId", "post_id", "_post_id", int, 0, PredefinedSerializer()), +] + diff --git a/kagglesdk/competitions/types/page_service.py b/kagglesdk/competitions/types/page_service.py new file mode 100644 index 0000000..6788f26 --- /dev/null +++ b/kagglesdk/competitions/types/page_service.py @@ -0,0 +1,37 @@ +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 c0910dd..bdf52d3 100644 --- a/kagglesdk/kaggle_client.py +++ b/kagglesdk/kaggle_client.py @@ -5,6 +5,7 @@ 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.datasets.services.dataset_api_service import DatasetApiClient from kagglesdk.education.services.education_api_service import EducationApiClient from kagglesdk.kernels.services.kernels_api_service import KernelsApiClient @@ -44,6 +45,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) class Datasets(object): def __init__(self, http_client: KaggleHttpClient):