|
100 | 100 | ApiListCompetitionPagesResponse, |
101 | 101 | ApiCreateCompetitionPageRequest, |
102 | 102 | ApiCompetitionPage, |
| 103 | + ApiCreateCompetitionRequest, |
| 104 | + ApiCreateCompetitionResponse, |
103 | 105 | ApiLaunchCompetitionRequest, |
104 | 106 | ApiListCompetitionTopicsRequest, |
105 | 107 | ApiListCompetitionTopicsResponse, |
|
131 | 133 | ) |
132 | 134 | from kagglesdk.competitions.types.competition_enums import ( |
133 | 135 | CompetitionListTab, |
| 136 | + CompetitionPrivacy, |
134 | 137 | HostSegment, |
135 | 138 | CompetitionSortBy, |
136 | 139 | SubmissionGroup, |
137 | 140 | SubmissionSortBy, |
138 | 141 | ) |
139 | 142 |
|
| 143 | +from kagglesdk.competitions.types.competition import Reward, RewardTypeId |
| 144 | + |
140 | 145 | from kagglesdk.common.types.cropped_image_upload import CroppedImageUpload, CroppedImageRectangle |
141 | 146 |
|
142 | 147 | from kagglesdk.datasets.types.dataset_api_service import ( |
@@ -764,6 +769,7 @@ class KaggleApi: |
764 | 769 | KERNEL_METADATA_FILE = "kernel-metadata.json" |
765 | 770 | MODEL_METADATA_FILE = "model-metadata.json" |
766 | 771 | MODEL_INSTANCE_METADATA_FILE = "model-instance-metadata.json" |
| 772 | + COMPETITION_METADATA_FILE = "competition-metadata.json" |
767 | 773 | MAX_NUM_INBOX_FILES_TO_UPLOAD = 1000 |
768 | 774 | MAX_UPLOAD_RESUME_ATTEMPTS = 10 |
769 | 775 |
|
@@ -2471,6 +2477,130 @@ def competition_launch_cli( |
2471 | 2477 | else: |
2472 | 2478 | print(f'Competition "{competition_name}" launched.') |
2473 | 2479 |
|
| 2480 | + def competition_initialize(self, folder: str) -> str: |
| 2481 | + """Initialize a folder with a competition-metadata.json template. |
| 2482 | +
|
| 2483 | + Args: |
| 2484 | + folder (str): Folder in which to write the template. |
| 2485 | +
|
| 2486 | + Returns: |
| 2487 | + str: Path to the written metadata file. |
| 2488 | + """ |
| 2489 | + if not os.path.isdir(folder): |
| 2490 | + raise ValueError("Invalid folder: " + folder) |
| 2491 | + |
| 2492 | + meta_data = { |
| 2493 | + "title": "INSERT_TITLE_HERE", |
| 2494 | + "slug": "INSERT_SLUG_HERE", |
| 2495 | + "briefDescription": "INSERT_BRIEF_DESCRIPTION_HERE", |
| 2496 | + "privacy": "PUBLIC", |
| 2497 | + "disableKernels": False, |
| 2498 | + "hackathon": False, |
| 2499 | + "cloneCompetitionId": None, |
| 2500 | + "cloneExcludeCompetitionData": None, |
| 2501 | + "clonePageNames": None, |
| 2502 | + "licenseId": None, |
| 2503 | + "organizationId": None, |
| 2504 | + "numPrizes": None, |
| 2505 | + "restrictLinkToEmailList": None, |
| 2506 | + "reward": None, |
| 2507 | + } |
| 2508 | + meta_file = os.path.join(folder, self.COMPETITION_METADATA_FILE) |
| 2509 | + with open(meta_file, "w") as f: |
| 2510 | + json.dump(meta_data, f, indent=2) |
| 2511 | + |
| 2512 | + print("Competition metadata template written to: " + meta_file) |
| 2513 | + return meta_file |
| 2514 | + |
| 2515 | + def competition_initialize_cli(self, folder=None): |
| 2516 | + folder = folder or os.getcwd() |
| 2517 | + self.competition_initialize(folder) |
| 2518 | + |
| 2519 | + def competition_create_new(self, folder: str) -> ApiCreateCompetitionResponse: |
| 2520 | + """Create a new competition from ``competition-metadata.json`` in ``folder``. |
| 2521 | +
|
| 2522 | + Args: |
| 2523 | + folder (str): Folder containing competition-metadata.json. |
| 2524 | +
|
| 2525 | + Returns: |
| 2526 | + ApiCreateCompetitionResponse: with id, ref, url of the new competition. |
| 2527 | + """ |
| 2528 | + if not os.path.isdir(folder): |
| 2529 | + raise ValueError("Invalid folder: " + folder) |
| 2530 | + |
| 2531 | + meta_file = os.path.join(folder, self.COMPETITION_METADATA_FILE) |
| 2532 | + if not os.path.isfile(meta_file): |
| 2533 | + raise ValueError("Metadata file not found: " + self.COMPETITION_METADATA_FILE) |
| 2534 | + |
| 2535 | + with open(meta_file) as f: |
| 2536 | + meta = json.load(f) |
| 2537 | + |
| 2538 | + title = cast(str, self.get_or_fail(meta, "title")) |
| 2539 | + slug = cast(str, self.get_or_fail(meta, "slug")) |
| 2540 | + brief_description = cast(str, self.get_or_fail(meta, "briefDescription")) |
| 2541 | + privacy_str = cast(str, self.get_or_fail(meta, "privacy")) |
| 2542 | + |
| 2543 | + if title == "INSERT_TITLE_HERE": |
| 2544 | + raise ValueError("Default title detected, please update competition-metadata.json before creating") |
| 2545 | + if slug == "INSERT_SLUG_HERE": |
| 2546 | + raise ValueError("Default slug detected, please update competition-metadata.json before creating") |
| 2547 | + if brief_description == "INSERT_BRIEF_DESCRIPTION_HERE": |
| 2548 | + raise ValueError( |
| 2549 | + "Default briefDescription detected, please update competition-metadata.json before creating" |
| 2550 | + ) |
| 2551 | + |
| 2552 | + try: |
| 2553 | + privacy = CompetitionPrivacy[privacy_str.upper()] |
| 2554 | + except KeyError as exc: |
| 2555 | + valid = [n for n in CompetitionPrivacy.__members__ if n != "COMPETITION_PRIVACY_UNSPECIFIED"] |
| 2556 | + raise ValueError(f"Invalid privacy '{privacy_str}'. Valid: {', '.join(valid)}") from exc |
| 2557 | + |
| 2558 | + request = ApiCreateCompetitionRequest() |
| 2559 | + request.title = title |
| 2560 | + request.slug = slug |
| 2561 | + request.brief_description = brief_description |
| 2562 | + request.privacy = privacy |
| 2563 | + |
| 2564 | + if meta.get("disableKernels") is not None: |
| 2565 | + request.disable_kernels = bool(meta["disableKernels"]) |
| 2566 | + if meta.get("hackathon") is not None: |
| 2567 | + request.hackathon = bool(meta["hackathon"]) |
| 2568 | + if meta.get("restrictLinkToEmailList") is not None: |
| 2569 | + request.restrict_link_to_email_list = bool(meta["restrictLinkToEmailList"]) |
| 2570 | + if meta.get("cloneCompetitionId") is not None: |
| 2571 | + request.clone_competition_id = int(meta["cloneCompetitionId"]) |
| 2572 | + if meta.get("cloneExcludeCompetitionData") is not None: |
| 2573 | + request.clone_exclude_competition_data = bool(meta["cloneExcludeCompetitionData"]) |
| 2574 | + if meta.get("clonePageNames"): |
| 2575 | + request.clone_page_names = list(meta["clonePageNames"]) |
| 2576 | + if meta.get("licenseId") is not None: |
| 2577 | + request.license_id = int(meta["licenseId"]) |
| 2578 | + if meta.get("organizationId") is not None: |
| 2579 | + request.organization_id = int(meta["organizationId"]) |
| 2580 | + if meta.get("numPrizes") is not None: |
| 2581 | + request.num_prizes = int(meta["numPrizes"]) |
| 2582 | + if meta.get("reward") is not None: |
| 2583 | + reward_meta = meta["reward"] |
| 2584 | + reward = Reward() |
| 2585 | + reward_id_str = cast(str, self.get_or_fail(reward_meta, "id")) |
| 2586 | + try: |
| 2587 | + reward.id = RewardTypeId[reward_id_str.upper()] |
| 2588 | + except KeyError as exc: |
| 2589 | + valid = [n for n in RewardTypeId.__members__ if n != "REWARD_TYPE_ID_UNSPECIFIED"] |
| 2590 | + raise ValueError(f"Invalid reward.id '{reward_id_str}'. Valid: {', '.join(valid)}") from exc |
| 2591 | + reward.quantity = int(self.get_or_fail(reward_meta, "quantity")) |
| 2592 | + if reward_meta.get("clarification") is not None: |
| 2593 | + reward.clarification = reward_meta["clarification"] |
| 2594 | + request.reward = reward |
| 2595 | + |
| 2596 | + with self.build_kaggle_client() as kaggle: |
| 2597 | + return kaggle.competitions.competition_api_client.create_competition(request) |
| 2598 | + |
| 2599 | + def competition_create_new_cli(self, folder=None): |
| 2600 | + folder = folder or os.getcwd() |
| 2601 | + response = self.competition_create_new(folder) |
| 2602 | + print(f"Competition created: {response.url}") |
| 2603 | + |
2474 | 2604 | def competition_list_topics(self, competition: str, sort_by: Optional[str] = None, page: Optional[int] = None): |
2475 | 2605 | """List discussion topics for a competition. |
2476 | 2606 |
|
|
0 commit comments