diff --git a/kaggle/api/kaggle_api_extended.py b/kaggle/api/kaggle_api_extended.py index 33f4a5d9..51b258af 100644 --- a/kaggle/api/kaggle_api_extended.py +++ b/kaggle/api/kaggle_api_extended.py @@ -34,7 +34,8 @@ import urllib3.exceptions as urllib3_exceptions from requests import RequestException -from kaggle.models.kaggle_models_extended import ResumableUploadResult, File +from kaggle.models.kaggle_models_extended import ResumableUploadResult, File, \ + Kernel from requests.adapters import HTTPAdapter from slugify import slugify @@ -61,7 +62,8 @@ SettingsLicense, DatasetCollaborator from kagglesdk.kernels.types.kernels_api_service import ApiListKernelsRequest, \ ApiListKernelFilesRequest, ApiSaveKernelRequest, ApiGetKernelRequest, \ - ApiListKernelSessionOutputRequest, ApiGetKernelSessionStatusRequest + ApiListKernelSessionOutputRequest, ApiGetKernelSessionStatusRequest, \ + ApiSaveKernelResponse from kagglesdk.kernels.types.kernels_enums import KernelsListSortType, \ KernelsListViewType from kagglesdk.models.types.model_api_service import ApiListModelsRequest, \ @@ -290,7 +292,7 @@ class KaggleApi: args = {} # DEBUG Add --local to use localhost if os.environ.get('KAGGLE_API_ENVIRONMENT') == 'LOCALHOST': - args = {'--local'} + args = {'--verbose','--local'} # Kernels valid types valid_push_kernel_types = ['script', 'notebook'] @@ -792,6 +794,39 @@ def competitions_list_cli(self, else: print('No competitions found') + def competition_submit_code(self, file_name, message, competition, kernel_slug=None, kernel_version=None, quiet=False): + """ Submit a competition. + + Parameters + ========== + file_name: the name of the output file created by the kernel + message: the submission description + competition: the competition name; if not given use the 'competition' config value + kernel_slug: the / of the notebook to use for a code competition + kernel_version: the version number, returned by 'kaggle kernels push ...' + quiet: suppress verbose output (default is False) + """ + if competition is None: + competition = self.get_config_value(self.CONFIG_NAME_COMPETITION) + if competition is not None and not quiet: + print('Using competition: ' + competition) + + if competition is None: + raise ValueError('No competition specified') + else: + if kernel_version is None: + raise ValueError('Kernel version must be specified') + with self.build_kaggle_client() as kaggle: + submit_request = ApiCreateCodeSubmissionRequest() + submit_request.file_name = file_name + submit_request.competition_name = competition + submit_request.kernel_slug = kernel_slug + submit_request.kernel_version = kernel_version + submit_request.submission_description = message + submit_response = kaggle.competitions.competition_api_client.create_code_submission( + submit_request) + return submit_response + def competition_submit(self, file_name, message, competition, quiet=False): """ Submit a competition. @@ -837,6 +872,8 @@ def competition_submit_cli(self, file_name, message, competition, + kernel=None, + version=None, competition_opt=None, quiet=False): """ Submit a competition using the client. Arguments are same as for @@ -847,12 +884,20 @@ def competition_submit_cli(self, file_name: the competition metadata file message: the submission description competition: the competition name; if not given use the 'competition' config value + kernel: the name of the kernel to submit to a code competition + version: the version of the kernel to submit to a code competition, e.g. '1' quiet: suppress verbose output (default is False) competition_opt: an alternative competition option provided by cli """ + if kernel and not version or version and not kernel: + raise ValueError('Code competition submissions require both the output file name and the version label') competition = competition or competition_opt try: - submit_result = self.competition_submit(file_name, message, competition, + if kernel: + submit_result = self.competition_submit_code(file_name, message, competition, + kernel, version, quiet) + else: + submit_result = self.competition_submit(file_name, message, competition, quiet) except RequestException as e: if e.response and e.response.status_code == 404: @@ -2369,7 +2414,7 @@ def kernels_initialize_cli(self, folder=None): meta_file = self.kernels_initialize(folder) print('Kernel metadata template written to: ' + meta_file) - def kernels_push(self, folder, timeout=None): + def kernels_push(self, folder, timeout=None) -> ApiSaveKernelResponse: """ Read the metadata file and kernel files from a notebook, validate both, and use the Kernel API to push to Kaggle if all is valid. Parameters diff --git a/kaggle/cli.py b/kaggle/cli.py index 50efb7ec..49362e41 100644 --- a/kaggle/cli.py +++ b/kaggle/cli.py @@ -245,13 +245,17 @@ def parse_competitions(subparsers): required=False, help=argparse.SUPPRESS) parser_competitions_submit_required.add_argument( - '-f', '--file', dest='file_name', required=True, help=Help.param_upfile) + '-f', '--file', dest='file_name', help=Help.param_upfile) + parser_competitions_submit_optional.add_argument( + '-k', '--kernel', dest='kernel', help=Help.param_code_kernel) parser_competitions_submit_required.add_argument( '-m', '--message', dest='message', required=True, help=Help.param_competition_message) + parser_competitions_submit_optional.add_argument( + '-v', '--version', dest='version', help=Help.param_code_version) parser_competitions_submit_optional.add_argument( '-q', '--quiet', dest='quiet', action='store_true', help=Help.param_quiet) parser_competitions_submit._action_groups.append( @@ -1420,7 +1424,9 @@ class Help(object): param_delete_old_version = 'Delete old versions of this dataset' param_force = ('Skip check whether local version of file is up to date, force' ' file download') - param_upfile = 'File for upload (full path)' + param_upfile = 'File for upload (full path), or the name of the output file produced by a kernel (for code competitions)' + param_code_kernel = 'Name of kernel (notebook) to submit to a code competition' + param_code_version = 'Version of kernel to submit to a code competition, e.g. "Version 1"' param_csv = 'Print results in CSV format (if not set print in table format)' param_page = 'Page number for results paging. Page size is 20 by default' # NOTE: Default and max page size are set by the mid-tier code. diff --git a/kagglesdk/competitions/services/competition_api_service.py b/kagglesdk/competitions/services/competition_api_service.py index e1d87a25..0243792b 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 ApiCreateSubmissionRequest, ApiCreateSubmissionResponse, ApiDownloadDataFileRequest, ApiDownloadDataFilesRequest, ApiDownloadLeaderboardRequest, ApiGetLeaderboardRequest, ApiGetLeaderboardResponse, ApiGetSubmissionRequest, ApiListCompetitionsRequest, ApiListCompetitionsResponse, ApiListDataFilesRequest, ApiListDataFilesResponse, ApiListSubmissionsRequest, ApiListSubmissionsResponse, ApiStartSubmissionUploadRequest, ApiStartSubmissionUploadResponse, ApiSubmission +from kagglesdk.competitions.types.competition_api_service import ApiCreateCodeSubmissionRequest, ApiCreateSubmissionRequest, ApiCreateSubmissionResponse, ApiDownloadDataFileRequest, ApiDownloadDataFilesRequest, ApiDownloadLeaderboardRequest, ApiGetLeaderboardRequest, ApiGetLeaderboardResponse, ApiGetSubmissionRequest, ApiListCompetitionsRequest, ApiListCompetitionsResponse, ApiListDataFilesRequest, ApiListDataFilesResponse, ApiListSubmissionsRequest, ApiListSubmissionsResponse, ApiStartSubmissionUploadRequest, ApiStartSubmissionUploadResponse, ApiSubmission from kagglesdk.kaggle_http_client import KaggleHttpClient class CompetitionApiClient(object): @@ -80,6 +80,18 @@ def create_submission(self, request: ApiCreateSubmissionRequest = None) -> ApiCr return self._client.call("competitions.CompetitionApiService", "ApiCreateSubmission", request, ApiCreateSubmissionResponse) + def create_code_submission(self, request: ApiCreateCodeSubmissionRequest = None) -> ApiCreateSubmissionResponse: + r""" + Args: + request (ApiCreateCodeSubmissionRequest): + The request object; initialized to empty instance if not specified. + """ + + if request is None: + request = ApiCreateCodeSubmissionRequest() + + return self._client.call("competitions.CompetitionApiService", "ApiCreateCodeSubmission", request, ApiCreateSubmissionResponse) + def get_submission(self, request: ApiGetSubmissionRequest = None) -> ApiSubmission: r""" Args: diff --git a/kagglesdk/competitions/types/competition_api_service.py b/kagglesdk/competitions/types/competition_api_service.py index db9d15e5..36c978b9 100644 --- a/kagglesdk/competitions/types/competition_api_service.py +++ b/kagglesdk/competitions/types/competition_api_service.py @@ -4,6 +4,99 @@ from kagglesdk.kaggle_object import * from typing import Optional, List +class ApiCreateCodeSubmissionRequest(KaggleObject): + r""" + Attributes: + competition_name (str) + kernel_slug (str) + kernel_version (str) + file_name (str) + submission_description (str) + """ + + def __init__(self): + self._competition_name = "" + self._kernel_slug = "" + self._kernel_version = "" + self._file_name = "" + self._submission_description = 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 kernel_slug(self) -> str: + return self._kernel_slug + + @kernel_slug.setter + def kernel_slug(self, kernel_slug: str): + if kernel_slug is None: + del self.kernel_slug + return + if not isinstance(kernel_slug, str): + raise TypeError('kernel_slug must be of type str') + self._kernel_slug = kernel_slug + + @property + def kernel_version(self) -> str: + return self._kernel_version + + @kernel_version.setter + def kernel_version(self, kernel_version: str): + if kernel_version is None: + del self.kernel_version + return + if not isinstance(kernel_version, str): + raise TypeError('kernel_version must be of type str') + self._kernel_version = kernel_version + + @property + def file_name(self) -> str: + return self._file_name + + @file_name.setter + def file_name(self, file_name: str): + if file_name is None: + del self.file_name + return + if not isinstance(file_name, str): + raise TypeError('file_name must be of type str') + self._file_name = file_name + + @property + def submission_description(self) -> str: + return self._submission_description or "" + + @submission_description.setter + def submission_description(self, submission_description: str): + if submission_description is None: + del self.submission_description + return + if not isinstance(submission_description, str): + raise TypeError('submission_description must be of type str') + self._submission_description = submission_description + + def endpoint(self): + path = '/api/v1/competitions/submissions/submit-notebook/{competition_name}' + return path.format_map(self.to_field_map(self)) + + + @staticmethod + def method(): + return 'POST' + + class ApiCreateSubmissionRequest(KaggleObject): r""" Attributes: @@ -1719,6 +1812,14 @@ def total_count(self, total_count: int): self._total_count = total_count +ApiCreateCodeSubmissionRequest._fields = [ + FieldMetadata("competitionName", "competition_name", "_competition_name", str, "", PredefinedSerializer()), + FieldMetadata("kernelSlug", "kernel_slug", "_kernel_slug", str, "", PredefinedSerializer()), + FieldMetadata("kernelVersion", "kernel_version", "_kernel_version", str, "", PredefinedSerializer()), + FieldMetadata("fileName", "file_name", "_file_name", str, "", PredefinedSerializer()), + FieldMetadata("submissionDescription", "submission_description", "_submission_description", str, None, PredefinedSerializer(), optional=True), +] + ApiCreateSubmissionRequest._fields = [ FieldMetadata("competitionName", "competition_name", "_competition_name", str, "", PredefinedSerializer()), FieldMetadata("blobFileTokens", "blob_file_tokens", "_blob_file_tokens", str, "", PredefinedSerializer()), diff --git a/kagglesdk/kaggle_http_client.py b/kagglesdk/kaggle_http_client.py index 8ef38128..d8c87411 100644 --- a/kagglesdk/kaggle_http_client.py +++ b/kagglesdk/kaggle_http_client.py @@ -18,7 +18,7 @@ # currently usable by the CLI. # TODO: Extend kapigen to add a boolean to these requests indicating that they use forms. -REQUESTS_REQUIRING_FORMS = ['ApiUploadDatasetFileRequest', 'ApiCreateSubmissionRequest', 'ApiStartSubmissionUploadRequest', 'ApiUploadModelFileRequest'] +REQUESTS_REQUIRING_FORMS = ['ApiUploadDatasetFileRequest', 'ApiCreateSubmissionRequest', 'ApiCreateCodeSubmissionRequest', 'ApiStartSubmissionUploadRequest', 'ApiUploadModelFileRequest'] def _headers_to_str(headers): return '\n'.join(f'{k}: {v}' for k, v in headers.items()) diff --git a/kagglesdk/models/types/model_api_service.py b/kagglesdk/models/types/model_api_service.py index 02f569be..5c8738ce 100644 --- a/kagglesdk/models/types/model_api_service.py +++ b/kagglesdk/models/types/model_api_service.py @@ -1169,7 +1169,6 @@ class ApiListModelGatingUserConsentsRequest(KaggleObject): model_slug (str) review_status (GatingAgreementRequestsReviewStatus) filters: a null value means the filter is off. - expiry_status (GatingAgreementRequestsExpiryStatus) is_user_request_data_expired (bool) page_size (int) paging @@ -1180,7 +1179,6 @@ def __init__(self): self._owner_slug = "" self._model_slug = "" self._review_status = None - self._expiry_status = None self._is_user_request_data_expired = None self._page_size = None self._page_token = None @@ -1226,19 +1224,6 @@ def review_status(self, review_status: 'GatingAgreementRequestsReviewStatus'): raise TypeError('review_status must be of type GatingAgreementRequestsReviewStatus') self._review_status = review_status - @property - def expiry_status(self) -> 'GatingAgreementRequestsExpiryStatus': - return self._expiry_status or GatingAgreementRequestsExpiryStatus.GATING_AGREEMENT_REQUESTS_EXPIRY_STATUS_UNSPECIFIED - - @expiry_status.setter - def expiry_status(self, expiry_status: 'GatingAgreementRequestsExpiryStatus'): - if expiry_status is None: - del self.expiry_status - return - if not isinstance(expiry_status, GatingAgreementRequestsExpiryStatus): - raise TypeError('expiry_status must be of type GatingAgreementRequestsExpiryStatus') - self._expiry_status = expiry_status - @property def is_user_request_data_expired(self) -> bool: return self._is_user_request_data_expired or False @@ -3531,7 +3516,6 @@ def e(self, e: str): FieldMetadata("ownerSlug", "owner_slug", "_owner_slug", str, "", PredefinedSerializer()), FieldMetadata("modelSlug", "model_slug", "_model_slug", str, "", PredefinedSerializer()), FieldMetadata("reviewStatus", "review_status", "_review_status", GatingAgreementRequestsReviewStatus, None, EnumSerializer(), optional=True), - FieldMetadata("expiryStatus", "expiry_status", "_expiry_status", GatingAgreementRequestsExpiryStatus, None, EnumSerializer(), optional=True), FieldMetadata("isUserRequestDataExpired", "is_user_request_data_expired", "_is_user_request_data_expired", 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), diff --git a/kagglesdk/models/types/model_enums.py b/kagglesdk/models/types/model_enums.py index 4fa14b9f..99d298a1 100644 --- a/kagglesdk/models/types/model_enums.py +++ b/kagglesdk/models/types/model_enums.py @@ -1,10 +1,5 @@ import enum -class GatingAgreementRequestsExpiryStatus(enum.Enum): - GATING_AGREEMENT_REQUESTS_EXPIRY_STATUS_UNSPECIFIED = 0 - GATING_AGREEMENT_REQUESTS_EXPIRY_STATUS_NOT_EXPIRED = 1 - GATING_AGREEMENT_REQUESTS_EXPIRY_STATUS_IS_EXPIRED = 2 - class GatingAgreementRequestsReviewStatus(enum.Enum): GATING_AGREEMENT_REQUESTS_REVIEW_STATUS_UNSPECIFIED = 0 GATING_AGREEMENT_REQUESTS_REVIEW_STATUS_PENDING = 1 @@ -58,3 +53,8 @@ class ModelVersionLinkType(enum.Enum): MODEL_VERSION_LINK_TYPE_VERTEX_OPEN = 1 MODEL_VERSION_LINK_TYPE_VERTEX_DEPLOY = 2 +class GatingAgreementRequestsExpiryStatus(enum.Enum): + GATING_AGREEMENT_REQUESTS_EXPIRY_STATUS_UNSPECIFIED = 0 + GATING_AGREEMENT_REQUESTS_EXPIRY_STATUS_NOT_EXPIRED = 1 + GATING_AGREEMENT_REQUESTS_EXPIRY_STATUS_IS_EXPIRED = 2 + diff --git a/src/kaggle/api/kaggle_api_extended.py b/src/kaggle/api/kaggle_api_extended.py index e6696ce4..51b258af 100644 --- a/src/kaggle/api/kaggle_api_extended.py +++ b/src/kaggle/api/kaggle_api_extended.py @@ -34,7 +34,8 @@ import urllib3.exceptions as urllib3_exceptions from requests import RequestException -from kaggle.models.kaggle_models_extended import ResumableUploadResult, File +from kaggle.models.kaggle_models_extended import ResumableUploadResult, File, \ + Kernel from requests.adapters import HTTPAdapter from slugify import slugify @@ -61,7 +62,8 @@ SettingsLicense, DatasetCollaborator from kagglesdk.kernels.types.kernels_api_service import ApiListKernelsRequest, \ ApiListKernelFilesRequest, ApiSaveKernelRequest, ApiGetKernelRequest, \ - ApiListKernelSessionOutputRequest, ApiGetKernelSessionStatusRequest + ApiListKernelSessionOutputRequest, ApiGetKernelSessionStatusRequest, \ + ApiSaveKernelResponse from kagglesdk.kernels.types.kernels_enums import KernelsListSortType, \ KernelsListViewType from kagglesdk.models.types.model_api_service import ApiListModelsRequest, \ @@ -290,7 +292,7 @@ class KaggleApi: args = {} # DEBUG Add --local to use localhost if os.environ.get('KAGGLE_API_ENVIRONMENT') == 'LOCALHOST': - args = {'--local'} + args = {'--verbose','--local'} # Kernels valid types valid_push_kernel_types = ['script', 'notebook'] @@ -792,6 +794,39 @@ def competitions_list_cli(self, else: print('No competitions found') + def competition_submit_code(self, file_name, message, competition, kernel_slug=None, kernel_version=None, quiet=False): + """ Submit a competition. + + Parameters + ========== + file_name: the name of the output file created by the kernel + message: the submission description + competition: the competition name; if not given use the 'competition' config value + kernel_slug: the / of the notebook to use for a code competition + kernel_version: the version number, returned by 'kaggle kernels push ...' + quiet: suppress verbose output (default is False) + """ + if competition is None: + competition = self.get_config_value(self.CONFIG_NAME_COMPETITION) + if competition is not None and not quiet: + print('Using competition: ' + competition) + + if competition is None: + raise ValueError('No competition specified') + else: + if kernel_version is None: + raise ValueError('Kernel version must be specified') + with self.build_kaggle_client() as kaggle: + submit_request = ApiCreateCodeSubmissionRequest() + submit_request.file_name = file_name + submit_request.competition_name = competition + submit_request.kernel_slug = kernel_slug + submit_request.kernel_version = kernel_version + submit_request.submission_description = message + submit_response = kaggle.competitions.competition_api_client.create_code_submission( + submit_request) + return submit_response + def competition_submit(self, file_name, message, competition, quiet=False): """ Submit a competition. @@ -837,6 +872,8 @@ def competition_submit_cli(self, file_name, message, competition, + kernel=None, + version=None, competition_opt=None, quiet=False): """ Submit a competition using the client. Arguments are same as for @@ -847,12 +884,20 @@ def competition_submit_cli(self, file_name: the competition metadata file message: the submission description competition: the competition name; if not given use the 'competition' config value + kernel: the name of the kernel to submit to a code competition + version: the version of the kernel to submit to a code competition, e.g. '1' quiet: suppress verbose output (default is False) competition_opt: an alternative competition option provided by cli """ + if kernel and not version or version and not kernel: + raise ValueError('Code competition submissions require both the output file name and the version label') competition = competition or competition_opt try: - submit_result = self.competition_submit(file_name, message, competition, + if kernel: + submit_result = self.competition_submit_code(file_name, message, competition, + kernel, version, quiet) + else: + submit_result = self.competition_submit(file_name, message, competition, quiet) except RequestException as e: if e.response and e.response.status_code == 404: @@ -2369,7 +2414,7 @@ def kernels_initialize_cli(self, folder=None): meta_file = self.kernels_initialize(folder) print('Kernel metadata template written to: ' + meta_file) - def kernels_push(self, folder, timeout): + def kernels_push(self, folder, timeout=None) -> ApiSaveKernelResponse: """ Read the metadata file and kernel files from a notebook, validate both, and use the Kernel API to push to Kaggle if all is valid. Parameters @@ -2465,7 +2510,7 @@ def kernels_push(self, folder, timeout): # but the server expects just one if 'source' in cell and isinstance(cell['source'], list): cell['source'] = ''.join(cell['source']) - script_body = json.dumps(json_body).replace("'", "\\'") + script_body = json.dumps(json_body) with self.build_kaggle_client() as kaggle: request = ApiSaveKernelRequest() diff --git a/src/kaggle/cli.py b/src/kaggle/cli.py index 50efb7ec..49362e41 100644 --- a/src/kaggle/cli.py +++ b/src/kaggle/cli.py @@ -245,13 +245,17 @@ def parse_competitions(subparsers): required=False, help=argparse.SUPPRESS) parser_competitions_submit_required.add_argument( - '-f', '--file', dest='file_name', required=True, help=Help.param_upfile) + '-f', '--file', dest='file_name', help=Help.param_upfile) + parser_competitions_submit_optional.add_argument( + '-k', '--kernel', dest='kernel', help=Help.param_code_kernel) parser_competitions_submit_required.add_argument( '-m', '--message', dest='message', required=True, help=Help.param_competition_message) + parser_competitions_submit_optional.add_argument( + '-v', '--version', dest='version', help=Help.param_code_version) parser_competitions_submit_optional.add_argument( '-q', '--quiet', dest='quiet', action='store_true', help=Help.param_quiet) parser_competitions_submit._action_groups.append( @@ -1420,7 +1424,9 @@ class Help(object): param_delete_old_version = 'Delete old versions of this dataset' param_force = ('Skip check whether local version of file is up to date, force' ' file download') - param_upfile = 'File for upload (full path)' + param_upfile = 'File for upload (full path), or the name of the output file produced by a kernel (for code competitions)' + param_code_kernel = 'Name of kernel (notebook) to submit to a code competition' + param_code_version = 'Version of kernel to submit to a code competition, e.g. "Version 1"' param_csv = 'Print results in CSV format (if not set print in table format)' param_page = 'Page number for results paging. Page size is 20 by default' # NOTE: Default and max page size are set by the mid-tier code. diff --git a/src/kagglesdk/competitions/services/competition_api_service.py b/src/kagglesdk/competitions/services/competition_api_service.py index e1d87a25..0243792b 100644 --- a/src/kagglesdk/competitions/services/competition_api_service.py +++ b/src/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 ApiCreateSubmissionRequest, ApiCreateSubmissionResponse, ApiDownloadDataFileRequest, ApiDownloadDataFilesRequest, ApiDownloadLeaderboardRequest, ApiGetLeaderboardRequest, ApiGetLeaderboardResponse, ApiGetSubmissionRequest, ApiListCompetitionsRequest, ApiListCompetitionsResponse, ApiListDataFilesRequest, ApiListDataFilesResponse, ApiListSubmissionsRequest, ApiListSubmissionsResponse, ApiStartSubmissionUploadRequest, ApiStartSubmissionUploadResponse, ApiSubmission +from kagglesdk.competitions.types.competition_api_service import ApiCreateCodeSubmissionRequest, ApiCreateSubmissionRequest, ApiCreateSubmissionResponse, ApiDownloadDataFileRequest, ApiDownloadDataFilesRequest, ApiDownloadLeaderboardRequest, ApiGetLeaderboardRequest, ApiGetLeaderboardResponse, ApiGetSubmissionRequest, ApiListCompetitionsRequest, ApiListCompetitionsResponse, ApiListDataFilesRequest, ApiListDataFilesResponse, ApiListSubmissionsRequest, ApiListSubmissionsResponse, ApiStartSubmissionUploadRequest, ApiStartSubmissionUploadResponse, ApiSubmission from kagglesdk.kaggle_http_client import KaggleHttpClient class CompetitionApiClient(object): @@ -80,6 +80,18 @@ def create_submission(self, request: ApiCreateSubmissionRequest = None) -> ApiCr return self._client.call("competitions.CompetitionApiService", "ApiCreateSubmission", request, ApiCreateSubmissionResponse) + def create_code_submission(self, request: ApiCreateCodeSubmissionRequest = None) -> ApiCreateSubmissionResponse: + r""" + Args: + request (ApiCreateCodeSubmissionRequest): + The request object; initialized to empty instance if not specified. + """ + + if request is None: + request = ApiCreateCodeSubmissionRequest() + + return self._client.call("competitions.CompetitionApiService", "ApiCreateCodeSubmission", request, ApiCreateSubmissionResponse) + def get_submission(self, request: ApiGetSubmissionRequest = None) -> ApiSubmission: r""" Args: diff --git a/src/kagglesdk/competitions/types/competition_api_service.py b/src/kagglesdk/competitions/types/competition_api_service.py index db9d15e5..36c978b9 100644 --- a/src/kagglesdk/competitions/types/competition_api_service.py +++ b/src/kagglesdk/competitions/types/competition_api_service.py @@ -4,6 +4,99 @@ from kagglesdk.kaggle_object import * from typing import Optional, List +class ApiCreateCodeSubmissionRequest(KaggleObject): + r""" + Attributes: + competition_name (str) + kernel_slug (str) + kernel_version (str) + file_name (str) + submission_description (str) + """ + + def __init__(self): + self._competition_name = "" + self._kernel_slug = "" + self._kernel_version = "" + self._file_name = "" + self._submission_description = 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 kernel_slug(self) -> str: + return self._kernel_slug + + @kernel_slug.setter + def kernel_slug(self, kernel_slug: str): + if kernel_slug is None: + del self.kernel_slug + return + if not isinstance(kernel_slug, str): + raise TypeError('kernel_slug must be of type str') + self._kernel_slug = kernel_slug + + @property + def kernel_version(self) -> str: + return self._kernel_version + + @kernel_version.setter + def kernel_version(self, kernel_version: str): + if kernel_version is None: + del self.kernel_version + return + if not isinstance(kernel_version, str): + raise TypeError('kernel_version must be of type str') + self._kernel_version = kernel_version + + @property + def file_name(self) -> str: + return self._file_name + + @file_name.setter + def file_name(self, file_name: str): + if file_name is None: + del self.file_name + return + if not isinstance(file_name, str): + raise TypeError('file_name must be of type str') + self._file_name = file_name + + @property + def submission_description(self) -> str: + return self._submission_description or "" + + @submission_description.setter + def submission_description(self, submission_description: str): + if submission_description is None: + del self.submission_description + return + if not isinstance(submission_description, str): + raise TypeError('submission_description must be of type str') + self._submission_description = submission_description + + def endpoint(self): + path = '/api/v1/competitions/submissions/submit-notebook/{competition_name}' + return path.format_map(self.to_field_map(self)) + + + @staticmethod + def method(): + return 'POST' + + class ApiCreateSubmissionRequest(KaggleObject): r""" Attributes: @@ -1719,6 +1812,14 @@ def total_count(self, total_count: int): self._total_count = total_count +ApiCreateCodeSubmissionRequest._fields = [ + FieldMetadata("competitionName", "competition_name", "_competition_name", str, "", PredefinedSerializer()), + FieldMetadata("kernelSlug", "kernel_slug", "_kernel_slug", str, "", PredefinedSerializer()), + FieldMetadata("kernelVersion", "kernel_version", "_kernel_version", str, "", PredefinedSerializer()), + FieldMetadata("fileName", "file_name", "_file_name", str, "", PredefinedSerializer()), + FieldMetadata("submissionDescription", "submission_description", "_submission_description", str, None, PredefinedSerializer(), optional=True), +] + ApiCreateSubmissionRequest._fields = [ FieldMetadata("competitionName", "competition_name", "_competition_name", str, "", PredefinedSerializer()), FieldMetadata("blobFileTokens", "blob_file_tokens", "_blob_file_tokens", str, "", PredefinedSerializer()), diff --git a/src/kagglesdk/kaggle_http_client.py b/src/kagglesdk/kaggle_http_client.py index 8ef38128..d8c87411 100644 --- a/src/kagglesdk/kaggle_http_client.py +++ b/src/kagglesdk/kaggle_http_client.py @@ -18,7 +18,7 @@ # currently usable by the CLI. # TODO: Extend kapigen to add a boolean to these requests indicating that they use forms. -REQUESTS_REQUIRING_FORMS = ['ApiUploadDatasetFileRequest', 'ApiCreateSubmissionRequest', 'ApiStartSubmissionUploadRequest', 'ApiUploadModelFileRequest'] +REQUESTS_REQUIRING_FORMS = ['ApiUploadDatasetFileRequest', 'ApiCreateSubmissionRequest', 'ApiCreateCodeSubmissionRequest', 'ApiStartSubmissionUploadRequest', 'ApiUploadModelFileRequest'] def _headers_to_str(headers): return '\n'.join(f'{k}: {v}' for k, v in headers.items()) diff --git a/src/kagglesdk/models/types/model_api_service.py b/src/kagglesdk/models/types/model_api_service.py index 02f569be..5c8738ce 100644 --- a/src/kagglesdk/models/types/model_api_service.py +++ b/src/kagglesdk/models/types/model_api_service.py @@ -1169,7 +1169,6 @@ class ApiListModelGatingUserConsentsRequest(KaggleObject): model_slug (str) review_status (GatingAgreementRequestsReviewStatus) filters: a null value means the filter is off. - expiry_status (GatingAgreementRequestsExpiryStatus) is_user_request_data_expired (bool) page_size (int) paging @@ -1180,7 +1179,6 @@ def __init__(self): self._owner_slug = "" self._model_slug = "" self._review_status = None - self._expiry_status = None self._is_user_request_data_expired = None self._page_size = None self._page_token = None @@ -1226,19 +1224,6 @@ def review_status(self, review_status: 'GatingAgreementRequestsReviewStatus'): raise TypeError('review_status must be of type GatingAgreementRequestsReviewStatus') self._review_status = review_status - @property - def expiry_status(self) -> 'GatingAgreementRequestsExpiryStatus': - return self._expiry_status or GatingAgreementRequestsExpiryStatus.GATING_AGREEMENT_REQUESTS_EXPIRY_STATUS_UNSPECIFIED - - @expiry_status.setter - def expiry_status(self, expiry_status: 'GatingAgreementRequestsExpiryStatus'): - if expiry_status is None: - del self.expiry_status - return - if not isinstance(expiry_status, GatingAgreementRequestsExpiryStatus): - raise TypeError('expiry_status must be of type GatingAgreementRequestsExpiryStatus') - self._expiry_status = expiry_status - @property def is_user_request_data_expired(self) -> bool: return self._is_user_request_data_expired or False @@ -3531,7 +3516,6 @@ def e(self, e: str): FieldMetadata("ownerSlug", "owner_slug", "_owner_slug", str, "", PredefinedSerializer()), FieldMetadata("modelSlug", "model_slug", "_model_slug", str, "", PredefinedSerializer()), FieldMetadata("reviewStatus", "review_status", "_review_status", GatingAgreementRequestsReviewStatus, None, EnumSerializer(), optional=True), - FieldMetadata("expiryStatus", "expiry_status", "_expiry_status", GatingAgreementRequestsExpiryStatus, None, EnumSerializer(), optional=True), FieldMetadata("isUserRequestDataExpired", "is_user_request_data_expired", "_is_user_request_data_expired", 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), diff --git a/src/kagglesdk/models/types/model_enums.py b/src/kagglesdk/models/types/model_enums.py index 4fa14b9f..99d298a1 100644 --- a/src/kagglesdk/models/types/model_enums.py +++ b/src/kagglesdk/models/types/model_enums.py @@ -1,10 +1,5 @@ import enum -class GatingAgreementRequestsExpiryStatus(enum.Enum): - GATING_AGREEMENT_REQUESTS_EXPIRY_STATUS_UNSPECIFIED = 0 - GATING_AGREEMENT_REQUESTS_EXPIRY_STATUS_NOT_EXPIRED = 1 - GATING_AGREEMENT_REQUESTS_EXPIRY_STATUS_IS_EXPIRED = 2 - class GatingAgreementRequestsReviewStatus(enum.Enum): GATING_AGREEMENT_REQUESTS_REVIEW_STATUS_UNSPECIFIED = 0 GATING_AGREEMENT_REQUESTS_REVIEW_STATUS_PENDING = 1 @@ -58,3 +53,8 @@ class ModelVersionLinkType(enum.Enum): MODEL_VERSION_LINK_TYPE_VERTEX_OPEN = 1 MODEL_VERSION_LINK_TYPE_VERTEX_DEPLOY = 2 +class GatingAgreementRequestsExpiryStatus(enum.Enum): + GATING_AGREEMENT_REQUESTS_EXPIRY_STATUS_UNSPECIFIED = 0 + GATING_AGREEMENT_REQUESTS_EXPIRY_STATUS_NOT_EXPIRED = 1 + GATING_AGREEMENT_REQUESTS_EXPIRY_STATUS_IS_EXPIRED = 2 +