From 8c567438b87d1d6947aeaaee22bd282102a06217 Mon Sep 17 00:00:00 2001 From: verena <9377970+vpchung@users.noreply.github.com> Date: Tue, 21 Nov 2023 17:03:57 -0800 Subject: [PATCH 01/26] add new command for new portal challenge --- challengeutils/__main__.py | 48 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/challengeutils/__main__.py b/challengeutils/__main__.py index e083283a..931b7c07 100644 --- a/challengeutils/__main__.py +++ b/challengeutils/__main__.py @@ -16,6 +16,7 @@ from . import ( annotations, createchallenge, + create_portal_challenge, challenge, cheat_detection, evaluation_queue, @@ -81,6 +82,33 @@ def command_createchallenge(syn, args): return challenge_components +def command_create_portal_challenge(syn, args): + """Creates a challenge on the Sage Challenge Portal. + + >>> challengeutils create-portal-challenge "Challenge Name Here" [-n ] + """ + challenge_components = create_portal_challenge.main( + syn, args.challengename, args.tasks_count, args.livesiteid + ) + # component: project or team + # componentid: project id or teamid + urls = {} + for component, componentid in challenge_components.items(): + if component.endswith("projectid"): + urls[component] = f"https://www.synapse.org/#!Synapse:{componentid}" + elif component.endswith("teamid"): + urls[component] = f"https://www.synapse.org/#!Team:{componentid}" + urls["name"] = args.challengename + text = ( + "{name} (Production site): {live_projectid}", + "{name} (Staging site): {staging_projectid}", + "{name} (Participant team): {organizer_teamid}", + "{name} (Organizer team): {participant_teamid}", + ) + print("\n" + "\n".join(text).format(**urls)) + return challenge_components + + def command_query(syn, args): """Command line convenience function to call evaluation queue query Evaluation queues offer a separate query service from the rest of Synapse. @@ -439,6 +467,26 @@ def build_parser(): ) parser_createchallenge.set_defaults(func=command_createchallenge) + parser_create_portal_challenge = subparsers.add_parser( + "create-portal-challenge", help="Create a Sage Challenge Portal challenge from template" + ) + parser_createchallenge.add_argument("challenge_name", help="Challenge name") + parser_createchallenge.add_argument( + "-n", + "--tasks_count", + default=1, + help=( + "Number of challenge tasks (default: 1)" + ), + ) + parser_createchallenge.add_argument( + "--livesiteid", + help=( + "Option to specify the live site synapse Id" " there is already a live site" + ), + ) + parser_createchallenge.set_defaults(func=command_create_portal_challenge) + parser_mirrorwiki = subparsers.add_parser( "mirror-wiki", help="Mirrors (sync) wiki pages by using the wikipage titles between " From 3a9d782894c0c509ea32efe7d5372b0eb6bad2ca Mon Sep 17 00:00:00 2001 From: verena <9377970+vpchung@users.noreply.github.com> Date: Tue, 21 Nov 2023 17:06:26 -0800 Subject: [PATCH 02/26] make copy of `createchallenge` --- challengeutils/create_portal_challenge.py | 218 ++++++++++++++++++++++ 1 file changed, 218 insertions(+) create mode 100644 challengeutils/create_portal_challenge.py diff --git a/challengeutils/create_portal_challenge.py b/challengeutils/create_portal_challenge.py new file mode 100644 index 00000000..4945e3b5 --- /dev/null +++ b/challengeutils/create_portal_challenge.py @@ -0,0 +1,218 @@ +"""Create challenge on the Sage Challenge Portal. + +Feature to create a new challenge based on a template. The new challenge +will include tw Synapse projects (one for dev, one for prod), two teams +(Participants and Organizers), one table to list organizers, and an +evaluation queue. + +Example:: + + import challengeutils + import synapseclient + syn = synapseclient.login() + challengeutils.create_portal_challenge.main(syn, "Foo Challenge") + +""" +import logging +import sys + +import synapseclient +from synapseclient.core.exceptions import SynapseHTTPError +import synapseutils + +from . import challenge, permissions, utils + +logger = logging.getLogger(__name__) + +CHALLENGE_TEMPLATE_SYNID = "syn52941681" +TASK_WIKI_ID = "624554" +TABLE_TEMPLATE_SYNID = "syn52955244" +CHALLENGE_ROLES = ['organizer', 'contributor', 'sponsor'] + + +def create_project(syn, project_name): + """Creates Synapse Project + + Args: + syn: Synpase object + project_name: Name of project + + Returns: + Project Entity + """ + project = synapseclient.Project(project_name) + project = syn.store(project) + logger.info("Created Project {} ({})".format(project.name, project.id)) + return project + + + +def create_evaluation_queue(syn, name, description, parentid): + """ + Creates Evaluation Queues + + Args: + syn: Synpase object + name: Name of evaluation queue + description: Description of queue + parentid: Synapse project id + + Returns: + Evalation Queue + """ + queue_ent = synapseclient.Evaluation( + name=name, description=description, contentSource=parentid + ) + queue = syn.store(queue_ent) + logger.info("Created Queue {}({})".format(queue.name, queue.id)) + return queue + + +def create_challenge_widget(syn, project_live, team_part_id): + """Creates challenge widget - activates a Synapse project + If challenge object exists, it retrieves existing object + + Args: + syn: Synpase object + project_live: Synapse id of live challenge project + team_part_id: Synapse team id of participant team + """ + try: + chal_obj = challenge.create_challenge(syn, project_live, team_part_id) + logger.info("Created Challenge ({})".format(chal_obj.id)) + except SynapseHTTPError: + chal_obj = challenge.get_challenge(syn, project_live) + logger.info("Fetched existing Challenge ({})".format(chal_obj.id)) + return chal_obj + + +def _update_wikipage_string( + wikipage_string, challengeid, teamid, challenge_name, synid +): + """Updates wikipage strings in the challenge wiki template + with the newly created challengeid, teamid, challenge name and project id + + Args: + wikipage_string: Original wikipage string + challengeid: New challenge id + teamid: Synapse Team id + challenge_name: challenge name + synid: Synapse id of project + + Returns: + fixed wiki page string + """ + wikipage_string = wikipage_string.replace( + "challengeId=0", "challengeId=%s" % challengeid + ) + wikipage_string = wikipage_string.replace("{teamId}", teamid) + wikipage_string = wikipage_string.replace("teamId=0", "teamId=%s" % teamid) + wikipage_string = wikipage_string.replace("#!Map:0", "#!Map:%s" % teamid) + wikipage_string = wikipage_string.replace("{challengeName}", challenge_name) + wikipage_string = wikipage_string.replace("projectId=syn0", "projectId=%s" % synid) + return wikipage_string + + +def check_existing_and_delete_wiki(syn, synid): + """Checks if wiki exists, and if so prompt to delete + + Args: + syn: Synapse connection + synid: Synapse id of an Entity + """ + wiki = None + try: + wiki = syn.getWiki(synid) + except SynapseHTTPError: + pass + + if wiki is not None: + logger.info("The staging project has already a wiki.") + logger.info(wiki) + user_input = ( + input("Do you agree to delete the wiki before continuing? (y/N) ") or "n" + ) + if user_input.lower() not in ("y", "yes"): + logger.info("Exiting") + sys.exit(1) + else: + logger.info("Deleting wiki of the staging project ({})".format(wiki.id)) + syn.delete(wiki) + + +def main(syn, challenge_name, tasks_count, live_site=None): + """Creates two project entity for challenge sites. + 1) live (public) and 2) staging (private until launch) + Allow for users to set up the live site themselves + + Args: + syn: Synapse object + challenge_name: Name of the challenge + live_site: If there is already a live site, specify live site Synapse + id. (Default is None) + tasks_count: number of challenge tasks (default: 1) + + Returns: + dict:: + + { + "live_projectid": projectid, + "staging_projectid": projectid, + "organizer_teamid": teams['team_org_id'], + "participant_teamid": teams['team_part_id'] + } + + """ + # Create teams for challenge sites + teams = _create_teams(syn, challenge_name) + + # Create live Project + if live_site is None: + project_live = create_project(syn, challenge_name) + permissions.set_entity_permissions( + syn, project_live, teams["team_org_id"], permission_level="moderate" + ) + _create_live_wiki(syn, project_live) + else: + project_live = syn.get(live_site) + + challenge_obj = create_challenge_widget(syn, project_live, teams["team_part_id"]) + create_evaluation_queue( + syn, + "%s Project Submission" % challenge_name, + "Project Submission", + project_live.id, + ) + create_organizer_tables(syn, project_live.id) + create_data_folders(syn, project_live.id, tasks_count) + + # Create staging Project + project_staging = create_project(syn, challenge_name + " - staging") + permissions.set_entity_permissions( + syn, project_staging, teams["team_org_id"], permission_level="edit" + ) + # Checks if staging wiki exists, if so delete + check_existing_and_delete_wiki(syn, project_staging.id) + + logger.info("Copying wiki template to {}".format(project_staging.name)) + new_wikiids = synapseutils.copyWiki( + syn, CHALLENGE_TEMPLATE_SYNID, project_staging.id + ) + for page in new_wikiids: + wikipage = syn.getWiki(project_staging, page["id"]) + wikipage.markdown = _update_wikipage_string( + wikipage.markdown, + challenge_obj.id, + teams["team_part_id"], + challenge_name, + project_live.id, + ) + syn.store(wikipage) + return { + "live_projectid": project_live.id, + "staging_projectid": project_staging.id, + "admin_teamid": teams["team_admin_id"], + "organizer_teamid": teams["team_org_id"], + "participant_teamid": teams["team_part_id"], + "preregistrantrant_teamid": teams["team_prereg_id"], + } From cffd2752b680d1c822d0443898314b716c8ba67c Mon Sep 17 00:00:00 2001 From: verena <9377970+vpchung@users.noreply.github.com> Date: Tue, 21 Nov 2023 17:06:57 -0800 Subject: [PATCH 03/26] update live wiki template --- challengeutils/create_portal_challenge.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/challengeutils/create_portal_challenge.py b/challengeutils/create_portal_challenge.py index 4945e3b5..e0b51dfe 100644 --- a/challengeutils/create_portal_challenge.py +++ b/challengeutils/create_portal_challenge.py @@ -67,6 +67,24 @@ def create_evaluation_queue(syn, name, description, parentid): logger.info("Created Queue {}({})".format(queue.name, queue.id)) return queue +def _create_live_wiki(syn, project): + """Creates the wiki of the live challenge page + + Args: + syn: Synpase object + project: Synapse project + teamid: Synapse team id of participant team + """ + markdown = ( + syn + .getWiki(CHALLENGE_TEMPLATE_SYNID) + .get('markdown') + .replace( + "#!Synapse:syn52941681/tables/", + f"#!Synapse:{project.id}/tables/") + ) + syn.store(synapseclient.Wiki(title="", owner=project, markdown=markdown)) + def create_challenge_widget(syn, project_live, team_part_id): """Creates challenge widget - activates a Synapse project From 7d69597763bfd1b520b3fc6302e2774109179e34 Mon Sep 17 00:00:00 2001 From: verena <9377970+vpchung@users.noreply.github.com> Date: Tue, 21 Nov 2023 17:07:25 -0800 Subject: [PATCH 04/26] reduce to two teams created --- challengeutils/create_portal_challenge.py | 58 +++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/challengeutils/create_portal_challenge.py b/challengeutils/create_portal_challenge.py index e0b51dfe..5a26e657 100644 --- a/challengeutils/create_portal_challenge.py +++ b/challengeutils/create_portal_challenge.py @@ -46,6 +46,38 @@ def create_project(syn, project_name): return project +def create_team(syn, team_name, desc, can_public_join=False): + """Creates Synapse Team + + Args: + syn: Synpase object + team_name: Name of team + desc: Description of team + can_public_join: true for teams which members can join without + an invitation or approval. Default to False + + Returns: + Synapse Team id + """ + try: + # raises a ValueError if a team does not exist + team = syn.getTeam(team_name) + logger.info("The team {} already exists.".format(team_name)) + logger.info(team) + # If you press enter, this will default to 'y' + user_input = input("Do you want to use this team? (Y/n) ") or "y" + if user_input.lower() not in ("y", "yes"): + logger.info("Please specify a new challenge name. Exiting.") + sys.exit(1) + except ValueError: + team = synapseclient.Team( + name=team_name, description=desc, canPublicJoin=can_public_join + ) + # raises a ValueError if a team with this name already exists + team = syn.store(team) + logger.info("Created Team {} ({})".format(team.name, team.id)) + return team + def create_evaluation_queue(syn, name, description, parentid): """ @@ -131,6 +163,32 @@ def _update_wikipage_string( return wikipage_string +def _create_teams(syn, challenge_name): + """Create teams needed for a challenge: participants and organizers + + Args: + syn: Synapse connection + challenge_name: Name of the challenge + + Returns: + dict of challenge team ids + """ + team_part = challenge_name + " Participants" + team_org = challenge_name + " Organizers" + team_part_ent = create_team( + syn, team_part, "Challenge Particpant Team", can_public_join=True + ) + team_org_ent = create_team( + syn, team_org, "Challenge Organizing Team", can_public_join=False + ) + + team_map = { + "team_part_id": team_part_ent["id"], + "team_org_id": team_org_ent["id"], + } + return team_map + + def check_existing_and_delete_wiki(syn, synid): """Checks if wiki exists, and if so prompt to delete From 9adb1539c76eca9b5a6720d38a3383c49b0bcceb Mon Sep 17 00:00:00 2001 From: verena <9377970+vpchung@users.noreply.github.com> Date: Tue, 21 Nov 2023 17:07:43 -0800 Subject: [PATCH 05/26] create default table and mat. views --- challengeutils/create_portal_challenge.py | 24 +++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/challengeutils/create_portal_challenge.py b/challengeutils/create_portal_challenge.py index 5a26e657..0961481c 100644 --- a/challengeutils/create_portal_challenge.py +++ b/challengeutils/create_portal_challenge.py @@ -99,6 +99,30 @@ def create_evaluation_queue(syn, name, description, parentid): logger.info("Created Queue {}({})".format(queue.name, queue.id)) return queue + +def create_organizer_tables(syn, parent_id): + """Create main table of organizers and associating views. + + Args: + syn: Synapse object + parent_id: project synID + """ + schema = synapseclient.Schema( + name='Organizing Team', + columns=syn.getColumns(TABLE_TEMPLATE_SYNID), + parent=parent_id) + table = synapseclient.Table(schema, [[]]) + table = syn.store(table) + + for role in CHALLENGE_ROLES: + view = synapseclient.MaterializedViewSchema( + name=role.title() + 's', + parent=parent_id, + definingSQL=f"SELECT * FROM {table.id} WHERE challengeRole HAS ('{role}')" + ) + syn.store(view) + + def _create_live_wiki(syn, project): """Creates the wiki of the live challenge page From bfc1b1c8394c9c87bf4b2058fe2732c3838a0b43 Mon Sep 17 00:00:00 2001 From: verena <9377970+vpchung@users.noreply.github.com> Date: Tue, 21 Nov 2023 17:07:57 -0800 Subject: [PATCH 06/26] create default data folders --- challengeutils/create_portal_challenge.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/challengeutils/create_portal_challenge.py b/challengeutils/create_portal_challenge.py index 0961481c..fee7f856 100644 --- a/challengeutils/create_portal_challenge.py +++ b/challengeutils/create_portal_challenge.py @@ -240,6 +240,22 @@ def check_existing_and_delete_wiki(syn, synid): syn.delete(wiki) +def create_data_folders(syn, parent_id, tasks_count): + """Create folders for challenge data, one for each task. + + Args: + syn: Synapse object + parent_id: project synID + tasks_count: Number of task folders to create + """ + for i in range(0, tasks_count): + folder = synapseclient.Folder( + name=f"Task {i + 1}", + parent=parent_id + ) + syn.store(folder) + + def main(syn, challenge_name, tasks_count, live_site=None): """Creates two project entity for challenge sites. 1) live (public) and 2) staging (private until launch) From 04b32e389799c07a5110690344de6a3202c4bdc3 Mon Sep 17 00:00:00 2001 From: verena <9377970+vpchung@users.noreply.github.com> Date: Tue, 21 Nov 2023 19:47:09 -0800 Subject: [PATCH 07/26] fix typo in fxn name --- challengeutils/__main__.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/challengeutils/__main__.py b/challengeutils/__main__.py index 931b7c07..27613d05 100644 --- a/challengeutils/__main__.py +++ b/challengeutils/__main__.py @@ -470,8 +470,8 @@ def build_parser(): parser_create_portal_challenge = subparsers.add_parser( "create-portal-challenge", help="Create a Sage Challenge Portal challenge from template" ) - parser_createchallenge.add_argument("challenge_name", help="Challenge name") - parser_createchallenge.add_argument( + parser_create_portal_challenge.add_argument("challenge_name", help="Challenge name") + parser_create_portal_challenge.add_argument( "-n", "--tasks_count", default=1, @@ -479,13 +479,13 @@ def build_parser(): "Number of challenge tasks (default: 1)" ), ) - parser_createchallenge.add_argument( + parser_creparser_create_portal_challengeatechallenge.add_argument( "--livesiteid", help=( "Option to specify the live site synapse Id" " there is already a live site" ), ) - parser_createchallenge.set_defaults(func=command_create_portal_challenge) + parser_create_portal_challenge.set_defaults(func=command_create_portal_challenge) parser_mirrorwiki = subparsers.add_parser( "mirror-wiki", From ec2565cc059b33cf883595614ad95cf279b2de4d Mon Sep 17 00:00:00 2001 From: verena <9377970+vpchung@users.noreply.github.com> Date: Tue, 21 Nov 2023 19:48:03 -0800 Subject: [PATCH 08/26] fix --- challengeutils/__main__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/challengeutils/__main__.py b/challengeutils/__main__.py index 27613d05..26049bae 100644 --- a/challengeutils/__main__.py +++ b/challengeutils/__main__.py @@ -479,7 +479,7 @@ def build_parser(): "Number of challenge tasks (default: 1)" ), ) - parser_creparser_create_portal_challengeatechallenge.add_argument( + parser_create_portal_challenge.add_argument( "--livesiteid", help=( "Option to specify the live site synapse Id" " there is already a live site" From 3f780b56e498413c19dc30150de48c76b1935a3c Mon Sep 17 00:00:00 2001 From: verena <9377970+vpchung@users.noreply.github.com> Date: Tue, 21 Nov 2023 22:17:36 -0800 Subject: [PATCH 09/26] fix --- challengeutils/__main__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/challengeutils/__main__.py b/challengeutils/__main__.py index 26049bae..c7c8fb9d 100644 --- a/challengeutils/__main__.py +++ b/challengeutils/__main__.py @@ -88,7 +88,7 @@ def command_create_portal_challenge(syn, args): >>> challengeutils create-portal-challenge "Challenge Name Here" [-n ] """ challenge_components = create_portal_challenge.main( - syn, args.challengename, args.tasks_count, args.livesiteid + syn, args.challenge_name, args.tasks_count, args.livesiteid ) # component: project or team # componentid: project id or teamid @@ -474,7 +474,7 @@ def build_parser(): parser_create_portal_challenge.add_argument( "-n", "--tasks_count", - default=1, + type=int, default=1, help=( "Number of challenge tasks (default: 1)" ), From c2494b0e0edea958c3657a639799d0de07345ea0 Mon Sep 17 00:00:00 2001 From: verena <9377970+vpchung@users.noreply.github.com> Date: Tue, 21 Nov 2023 22:18:13 -0800 Subject: [PATCH 10/26] add logging --- challengeutils/create_portal_challenge.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/challengeutils/create_portal_challenge.py b/challengeutils/create_portal_challenge.py index fee7f856..4c6e1d58 100644 --- a/challengeutils/create_portal_challenge.py +++ b/challengeutils/create_portal_challenge.py @@ -113,14 +113,16 @@ def create_organizer_tables(syn, parent_id): parent=parent_id) table = synapseclient.Table(schema, [[]]) table = syn.store(table) - + logger.info("Created Table {}({})".format(table.name, table.id)) for role in CHALLENGE_ROLES: view = synapseclient.MaterializedViewSchema( name=role.title() + 's', parent=parent_id, definingSQL=f"SELECT * FROM {table.id} WHERE challengeRole HAS ('{role}')" ) - syn.store(view) + view = syn.store(view) + logger.info("Created MaterializedView {}({})".format(view.name, view.id)) + return table def _create_live_wiki(syn, project): @@ -253,7 +255,9 @@ def create_data_folders(syn, parent_id, tasks_count): name=f"Task {i + 1}", parent=parent_id ) - syn.store(folder) + folder = syn.store(folder) + logger.info("Created Folder {}({})".format(folder.name, folder.id)) + return def main(syn, challenge_name, tasks_count, live_site=None): From d632b49f0c4078ee91bb2cec4cb900fd94beb0c7 Mon Sep 17 00:00:00 2001 From: verena <9377970+vpchung@users.noreply.github.com> Date: Tue, 21 Nov 2023 22:18:34 -0800 Subject: [PATCH 11/26] update live markdown --- challengeutils/create_portal_challenge.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/challengeutils/create_portal_challenge.py b/challengeutils/create_portal_challenge.py index 4c6e1d58..2d36d21d 100644 --- a/challengeutils/create_portal_challenge.py +++ b/challengeutils/create_portal_challenge.py @@ -133,14 +133,13 @@ def _create_live_wiki(syn, project): project: Synapse project teamid: Synapse team id of participant team """ - markdown = ( - syn - .getWiki(CHALLENGE_TEMPLATE_SYNID) - .get('markdown') - .replace( - "#!Synapse:syn52941681/tables/", - f"#!Synapse:{project.id}/tables/") - ) + markdown = """ +
+ +###! More information coming soon! + +
+ """ syn.store(synapseclient.Wiki(title="", owner=project, markdown=markdown)) From 00f68eba5c3de38c6a8a373a11b6e478b2650048 Mon Sep 17 00:00:00 2001 From: verena <9377970+vpchung@users.noreply.github.com> Date: Tue, 21 Nov 2023 22:18:58 -0800 Subject: [PATCH 12/26] add new tab for each task --- challengeutils/create_portal_challenge.py | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/challengeutils/create_portal_challenge.py b/challengeutils/create_portal_challenge.py index 2d36d21d..9dc30838 100644 --- a/challengeutils/create_portal_challenge.py +++ b/challengeutils/create_portal_challenge.py @@ -327,11 +327,28 @@ def main(syn, challenge_name, tasks_count, live_site=None): project_live.id, ) syn.store(wikipage) + + # Create another Task tab per task. + for i in range(1, tasks_count): + new_tab = synapseclient.Wiki( + title=f"Task {i + 1}", + owner=project_staging.id, + markdown="", + parentWikiId=syn.getWiki(project_staging.id).id + ) + new_tab = syn.store(new_tab) + synapseutils.copyWiki( + syn, + CHALLENGE_TEMPLATE_SYNID, + project_staging.id, + entitySubPageId=TASK_WIKI_ID, + destinationSubPageId=new_tab.id, + entityMap={CHALLENGE_TEMPLATE_SYNID: project_staging.id} + ) + return { "live_projectid": project_live.id, "staging_projectid": project_staging.id, - "admin_teamid": teams["team_admin_id"], "organizer_teamid": teams["team_org_id"], "participant_teamid": teams["team_part_id"], - "preregistrantrant_teamid": teams["team_prereg_id"], } From fa829d7a1ae44c59a9253de2b7f70f38abfdc5ed Mon Sep 17 00:00:00 2001 From: verena <9377970+vpchung@users.noreply.github.com> Date: Wed, 22 Nov 2023 14:06:05 -0800 Subject: [PATCH 13/26] typo fix --- challengeutils/__main__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/challengeutils/__main__.py b/challengeutils/__main__.py index c7c8fb9d..cc36bad9 100644 --- a/challengeutils/__main__.py +++ b/challengeutils/__main__.py @@ -98,7 +98,7 @@ def command_create_portal_challenge(syn, args): urls[component] = f"https://www.synapse.org/#!Synapse:{componentid}" elif component.endswith("teamid"): urls[component] = f"https://www.synapse.org/#!Team:{componentid}" - urls["name"] = args.challengename + urls["name"] = args.challenge_name text = ( "{name} (Production site): {live_projectid}", "{name} (Staging site): {staging_projectid}", @@ -472,7 +472,7 @@ def build_parser(): ) parser_create_portal_challenge.add_argument("challenge_name", help="Challenge name") parser_create_portal_challenge.add_argument( - "-n", + "-t", "--tasks_count", type=int, default=1, help=( From f97a16c84f82dcad88ecf94a350934bdf0d5c1e7 Mon Sep 17 00:00:00 2001 From: verena <9377970+vpchung@users.noreply.github.com> Date: Wed, 22 Nov 2023 14:06:23 -0800 Subject: [PATCH 14/26] remove unneeded packages --- challengeutils/create_portal_challenge.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/challengeutils/create_portal_challenge.py b/challengeutils/create_portal_challenge.py index 9dc30838..35887255 100644 --- a/challengeutils/create_portal_challenge.py +++ b/challengeutils/create_portal_challenge.py @@ -20,14 +20,14 @@ from synapseclient.core.exceptions import SynapseHTTPError import synapseutils -from . import challenge, permissions, utils +from . import challenge, permissions logger = logging.getLogger(__name__) CHALLENGE_TEMPLATE_SYNID = "syn52941681" -TASK_WIKI_ID = "624554" TABLE_TEMPLATE_SYNID = "syn52955244" CHALLENGE_ROLES = ['organizer', 'contributor', 'sponsor'] +TASK_WIKI_ID = "624554" def create_project(syn, project_name): From 009440106fe534a86dcbe10ff9a707b2d056d5e6 Mon Sep 17 00:00:00 2001 From: verena <9377970+vpchung@users.noreply.github.com> Date: Wed, 22 Nov 2023 14:07:25 -0800 Subject: [PATCH 15/26] add FIXME notes to `create_organizer_tables()` --- challengeutils/create_portal_challenge.py | 59 +++++++++++++---------- 1 file changed, 33 insertions(+), 26 deletions(-) diff --git a/challengeutils/create_portal_challenge.py b/challengeutils/create_portal_challenge.py index 35887255..62e5ab33 100644 --- a/challengeutils/create_portal_challenge.py +++ b/challengeutils/create_portal_challenge.py @@ -100,31 +100,6 @@ def create_evaluation_queue(syn, name, description, parentid): return queue -def create_organizer_tables(syn, parent_id): - """Create main table of organizers and associating views. - - Args: - syn: Synapse object - parent_id: project synID - """ - schema = synapseclient.Schema( - name='Organizing Team', - columns=syn.getColumns(TABLE_TEMPLATE_SYNID), - parent=parent_id) - table = synapseclient.Table(schema, [[]]) - table = syn.store(table) - logger.info("Created Table {}({})".format(table.name, table.id)) - for role in CHALLENGE_ROLES: - view = synapseclient.MaterializedViewSchema( - name=role.title() + 's', - parent=parent_id, - definingSQL=f"SELECT * FROM {table.id} WHERE challengeRole HAS ('{role}')" - ) - view = syn.store(view) - logger.info("Created MaterializedView {}({})".format(view.name, view.id)) - return table - - def _create_live_wiki(syn, project): """Creates the wiki of the live challenge page @@ -241,7 +216,39 @@ def check_existing_and_delete_wiki(syn, synid): syn.delete(wiki) -def create_data_folders(syn, parent_id, tasks_count): +# FIXME: function does not work as expected, see inner comments for details. +def create_organizer_tables(syn, project_id): + """Create main table of organizers and associating views. + + Args: + syn: Synapse object + parent_id: project synID + """ + view_ids = {} + schema = synapseclient.Schema( + name='Organizing Team', + columns=syn.getColumns(TABLE_TEMPLATE_SYNID), + parent=project_id) + table = synapseclient.Table(schema, [[]]) + + # FIXME: for some reason, storing the table will then call on the + # `challengeutils` CLI again + table = syn.store(table) + logger.info(f"Table created: {table.name} ({table.id})") + + # FIXME: due to the issue above, we are not able to create the + # MaterializedViews + for role in CHALLENGE_ROLES: + role_title = role.title() + 's' + view = synapseclient.MaterializedViewSchema( + name=role_title, + parent=project_id, + definingSQL=f"SELECT * FROM {table.id} WHERE challengeRole HAS ('{role}')" + ) + view = syn.store(view) + view_ids[role_title] = view.id + logger.info(f"MaterializedView created: {view.name} ({view.id})") + return view_ids """Create folders for challenge data, one for each task. Args: From 6caab73a00e0124eff4af032a01e961a32e94051 Mon Sep 17 00:00:00 2001 From: verena <9377970+vpchung@users.noreply.github.com> Date: Wed, 22 Nov 2023 14:07:56 -0800 Subject: [PATCH 16/26] add annotations for project --- challengeutils/create_portal_challenge.py | 37 +++++++++++++++++------ 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/challengeutils/create_portal_challenge.py b/challengeutils/create_portal_challenge.py index 62e5ab33..481ef9ab 100644 --- a/challengeutils/create_portal_challenge.py +++ b/challengeutils/create_portal_challenge.py @@ -262,8 +262,30 @@ def create_organizer_tables(syn, project_id): parent=parent_id ) folder = syn.store(folder) - logger.info("Created Folder {}({})".format(folder.name, folder.id)) - return +def create_annotations(syn, project_id, table_ids, folder_ids): + """Create annotations that will power the portal.""" + logger.info("Creating basic annotations...") + project = syn.get(project_id) + + # Set basic annotations to the project. + for name, value in syn.get_annotations(CHALLENGE_TEMPLATE_SYNID).items(): + project[name] = "" + + # For annotations we do currently know, add their values. + main_wiki_id = [ + wiki.get('id') + for wiki in syn.getWikiHeaders(project_id) + if not wiki.get('parentId') + ][0] + project['Overview'] = f"{project_id}/wiki/{main_wiki_id}" + # # for role, synid in table_ids.items(): + # # annots[role] = synid + for i, synid in folder_ids.items(): + project[f'Task_{i + 1}.DataFolder'] = synid + print(project) + project = syn.store(project) + logger.info("Annotations creation complete.") + return project def main(syn, challenge_name, tasks_count, live_site=None): @@ -303,14 +325,9 @@ def main(syn, challenge_name, tasks_count, live_site=None): project_live = syn.get(live_site) challenge_obj = create_challenge_widget(syn, project_live, teams["team_part_id"]) - create_evaluation_queue( - syn, - "%s Project Submission" % challenge_name, - "Project Submission", - project_live.id, - ) - create_organizer_tables(syn, project_live.id) - create_data_folders(syn, project_live.id, tasks_count) + # Add the basic annotations to the live Project - some can be pre-filled but the + # majority will need to filled out on the web UI. + project_live = create_annotations(syn, project_live.id, tables, folders) # Create staging Project project_staging = create_project(syn, challenge_name + " - staging") From db4fe84245d9e77ee252686273fd37f6f4761a1b Mon Sep 17 00:00:00 2001 From: verena <9377970+vpchung@users.noreply.github.com> Date: Wed, 22 Nov 2023 14:08:13 -0800 Subject: [PATCH 17/26] set custom permissions for data folders --- challengeutils/create_portal_challenge.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/challengeutils/create_portal_challenge.py b/challengeutils/create_portal_challenge.py index 481ef9ab..357a6ccf 100644 --- a/challengeutils/create_portal_challenge.py +++ b/challengeutils/create_portal_challenge.py @@ -325,6 +325,22 @@ def main(syn, challenge_name, tasks_count, live_site=None): project_live = syn.get(live_site) challenge_obj = create_challenge_widget(syn, project_live, teams["team_part_id"]) + # TODO: the following function does not work for some reason; see function for details + # tables = create_organizer_tables(syn, project_live.id) + tables = {} + + # Create data folder(s) and set their local sharing settings as: + # - organizers team = download access + # - participants team = download access + folders = create_data_folders(syn, project_live.id, tasks_count) + for _, folder_id in folders.items(): + permissions.set_entity_permissions( + syn, folder_id, teams["team_org_id"], permission_level="download" + ) + permissions.set_entity_permissions( + syn, folder_id, teams["team_part_id"], permission_level="download" + ) + # Add the basic annotations to the live Project - some can be pre-filled but the # majority will need to filled out on the web UI. project_live = create_annotations(syn, project_live.id, tables, folders) From 29211bc16d32dd068355d80c54a0de2332cf7b7e Mon Sep 17 00:00:00 2001 From: verena <9377970+vpchung@users.noreply.github.com> Date: Wed, 22 Nov 2023 14:08:26 -0800 Subject: [PATCH 18/26] create one queue per task --- challengeutils/create_portal_challenge.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/challengeutils/create_portal_challenge.py b/challengeutils/create_portal_challenge.py index 357a6ccf..28abc569 100644 --- a/challengeutils/create_portal_challenge.py +++ b/challengeutils/create_portal_challenge.py @@ -325,6 +325,13 @@ def main(syn, challenge_name, tasks_count, live_site=None): project_live = syn.get(live_site) challenge_obj = create_challenge_widget(syn, project_live, teams["team_part_id"]) + for i in range(0, tasks_count): + create_evaluation_queue( + syn, + f"{challenge_name} Task {i + 1}", + f"Task {i + 1} Submission", + project_live.id, + ) # TODO: the following function does not work for some reason; see function for details # tables = create_organizer_tables(syn, project_live.id) tables = {} From 67befa5996e8e15e115fd5cdd51bf666179d9b39 Mon Sep 17 00:00:00 2001 From: verena <9377970+vpchung@users.noreply.github.com> Date: Wed, 22 Nov 2023 14:08:42 -0800 Subject: [PATCH 19/26] fix task tab creation --- challengeutils/create_portal_challenge.py | 29 +++++++++++++---------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/challengeutils/create_portal_challenge.py b/challengeutils/create_portal_challenge.py index 28abc569..77d47e7e 100644 --- a/challengeutils/create_portal_challenge.py +++ b/challengeutils/create_portal_challenge.py @@ -376,22 +376,27 @@ def main(syn, challenge_name, tasks_count, live_site=None): syn.store(wikipage) # Create another Task tab per task. + task_wikis = [ + wiki.get('title') + for wiki in syn.getWikiHeaders(CHALLENGE_TEMPLATE_SYNID) + if wiki.get('parentId') == TASK_WIKI_ID + ] for i in range(1, tasks_count): - new_tab = synapseclient.Wiki( - title=f"Task {i + 1}", + new_task_tab = synapseclient.Wiki( + title=f"Task {i + 1} (tab)", owner=project_staging.id, - markdown="", + markdown="This page can be left empty - it will not appear on the portal.", parentWikiId=syn.getWiki(project_staging.id).id ) - new_tab = syn.store(new_tab) - synapseutils.copyWiki( - syn, - CHALLENGE_TEMPLATE_SYNID, - project_staging.id, - entitySubPageId=TASK_WIKI_ID, - destinationSubPageId=new_tab.id, - entityMap={CHALLENGE_TEMPLATE_SYNID: project_staging.id} - ) + new_task_tab = syn.store(new_task_tab) + for wiki_title in task_wikis: + task_subwiki = synapseclient.Wiki( + title=wiki_title, + owner=project_staging.id, + markdown="Refer to the Task 1 tab for examples", + parentWikiId=new_task_tab.id + ) + syn.store(task_subwiki) return { "live_projectid": project_live.id, From 2b2e2254aec85e3b526a424f201dedf0be9e8a8d Mon Sep 17 00:00:00 2001 From: verena <9377970+vpchung@users.noreply.github.com> Date: Wed, 22 Nov 2023 14:08:57 -0800 Subject: [PATCH 20/26] replace `format()` with f-strings --- challengeutils/create_portal_challenge.py | 30 +++++++++++++++-------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/challengeutils/create_portal_challenge.py b/challengeutils/create_portal_challenge.py index 77d47e7e..654fc8d5 100644 --- a/challengeutils/create_portal_challenge.py +++ b/challengeutils/create_portal_challenge.py @@ -42,7 +42,7 @@ def create_project(syn, project_name): """ project = synapseclient.Project(project_name) project = syn.store(project) - logger.info("Created Project {} ({})".format(project.name, project.id)) + logger.info(f"Project created: {project.name} ({project.id})") return project @@ -62,7 +62,7 @@ def create_team(syn, team_name, desc, can_public_join=False): try: # raises a ValueError if a team does not exist team = syn.getTeam(team_name) - logger.info("The team {} already exists.".format(team_name)) + logger.info(f"Team {team_name} already exists.") logger.info(team) # If you press enter, this will default to 'y' user_input = input("Do you want to use this team? (Y/n) ") or "y" @@ -75,7 +75,7 @@ def create_team(syn, team_name, desc, can_public_join=False): ) # raises a ValueError if a team with this name already exists team = syn.store(team) - logger.info("Created Team {} ({})".format(team.name, team.id)) + logger.info(f"Team created: {team.name} ({team.id})") return team @@ -96,7 +96,7 @@ def create_evaluation_queue(syn, name, description, parentid): name=name, description=description, contentSource=parentid ) queue = syn.store(queue_ent) - logger.info("Created Queue {}({})".format(queue.name, queue.id)) + logger.info(f"Queue created: {queue.name} ({queue.id})") return queue @@ -129,10 +129,10 @@ def create_challenge_widget(syn, project_live, team_part_id): """ try: chal_obj = challenge.create_challenge(syn, project_live, team_part_id) - logger.info("Created Challenge ({})".format(chal_obj.id)) + logger.info(f"Acivated as Challenge ({chal_obj.id})") except SynapseHTTPError: chal_obj = challenge.get_challenge(syn, project_live) - logger.info("Fetched existing Challenge ({})".format(chal_obj.id)) + logger.info(f"Fetched existing Challenge ({chal_obj.id})") return chal_obj @@ -212,7 +212,7 @@ def check_existing_and_delete_wiki(syn, synid): logger.info("Exiting") sys.exit(1) else: - logger.info("Deleting wiki of the staging project ({})".format(wiki.id)) + logger.info(f"Deleting wiki of the staging project ({wiki.id})") syn.delete(wiki) @@ -249,6 +249,9 @@ def create_organizer_tables(syn, project_id): view_ids[role_title] = view.id logger.info(f"MaterializedView created: {view.name} ({view.id})") return view_ids + + +def create_data_folders(syn, project_id, tasks_count): """Create folders for challenge data, one for each task. Args: @@ -256,12 +259,19 @@ def create_organizer_tables(syn, project_id): parent_id: project synID tasks_count: Number of task folders to create """ + folder_ids = {} for i in range(0, tasks_count): + folder_name = f"Task {i + 1}" folder = synapseclient.Folder( - name=f"Task {i + 1}", - parent=parent_id + name=folder_name, + parent=project_id ) folder = syn.store(folder) + folder_ids[i] = folder.id + logger.info(f"Folder created: {folder.name} ({folder.id})") + return folder_ids + + def create_annotations(syn, project_id, table_ids, folder_ids): """Create annotations that will power the portal.""" logger.info("Creating basic annotations...") @@ -360,7 +370,7 @@ def main(syn, challenge_name, tasks_count, live_site=None): # Checks if staging wiki exists, if so delete check_existing_and_delete_wiki(syn, project_staging.id) - logger.info("Copying wiki template to {}".format(project_staging.name)) + logger.info(f"Copying wiki template to {project_staging.name}") new_wikiids = synapseutils.copyWiki( syn, CHALLENGE_TEMPLATE_SYNID, project_staging.id ) From 04bfe469fb4a291a772843598520fbb55ec0709d Mon Sep 17 00:00:00 2001 From: verena <9377970+vpchung@users.noreply.github.com> Date: Wed, 22 Nov 2023 14:13:21 -0800 Subject: [PATCH 21/26] update logging info --- challengeutils/create_portal_challenge.py | 31 ++++++++++++----------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/challengeutils/create_portal_challenge.py b/challengeutils/create_portal_challenge.py index 654fc8d5..62c55c81 100644 --- a/challengeutils/create_portal_challenge.py +++ b/challengeutils/create_portal_challenge.py @@ -42,7 +42,7 @@ def create_project(syn, project_name): """ project = synapseclient.Project(project_name) project = syn.store(project) - logger.info(f"Project created: {project.name} ({project.id})") + logger.info(f" Project created: {project.name} ({project.id})") return project @@ -62,12 +62,12 @@ def create_team(syn, team_name, desc, can_public_join=False): try: # raises a ValueError if a team does not exist team = syn.getTeam(team_name) - logger.info(f"Team {team_name} already exists.") + logger.info(f" Team {team_name} already exists.") logger.info(team) # If you press enter, this will default to 'y' user_input = input("Do you want to use this team? (Y/n) ") or "y" if user_input.lower() not in ("y", "yes"): - logger.info("Please specify a new challenge name. Exiting.") + logger.info(" Please specify a new challenge name. Exiting.") sys.exit(1) except ValueError: team = synapseclient.Team( @@ -75,7 +75,7 @@ def create_team(syn, team_name, desc, can_public_join=False): ) # raises a ValueError if a team with this name already exists team = syn.store(team) - logger.info(f"Team created: {team.name} ({team.id})") + logger.info(f" Team created: {team.name} ({team.id})") return team @@ -96,7 +96,7 @@ def create_evaluation_queue(syn, name, description, parentid): name=name, description=description, contentSource=parentid ) queue = syn.store(queue_ent) - logger.info(f"Queue created: {queue.name} ({queue.id})") + logger.info(f" Queue created: {queue.name} ({queue.id})") return queue @@ -129,10 +129,10 @@ def create_challenge_widget(syn, project_live, team_part_id): """ try: chal_obj = challenge.create_challenge(syn, project_live, team_part_id) - logger.info(f"Acivated as Challenge ({chal_obj.id})") + logger.info(f" Acivated as Challenge (ID: {chal_obj.id})") except SynapseHTTPError: chal_obj = challenge.get_challenge(syn, project_live) - logger.info(f"Fetched existing Challenge ({chal_obj.id})") + logger.info(f" Fetched existing Challenge (ID: {chal_obj.id})") return chal_obj @@ -203,16 +203,16 @@ def check_existing_and_delete_wiki(syn, synid): pass if wiki is not None: - logger.info("The staging project has already a wiki.") + logger.info(" The staging project has already a wiki.") logger.info(wiki) user_input = ( input("Do you agree to delete the wiki before continuing? (y/N) ") or "n" ) if user_input.lower() not in ("y", "yes"): - logger.info("Exiting") + logger.info(" --- Exiting ---") sys.exit(1) else: - logger.info(f"Deleting wiki of the staging project ({wiki.id})") + logger.info(f" Deleting wiki of the staging project ({wiki.id})") syn.delete(wiki) @@ -234,7 +234,7 @@ def create_organizer_tables(syn, project_id): # FIXME: for some reason, storing the table will then call on the # `challengeutils` CLI again table = syn.store(table) - logger.info(f"Table created: {table.name} ({table.id})") + logger.info(f" Table created: {table.name} ({table.id})") # FIXME: due to the issue above, we are not able to create the # MaterializedViews @@ -247,7 +247,7 @@ def create_organizer_tables(syn, project_id): ) view = syn.store(view) view_ids[role_title] = view.id - logger.info(f"MaterializedView created: {view.name} ({view.id})") + logger.info(f" MaterializedView created: {view.name} ({view.id})") return view_ids @@ -274,7 +274,7 @@ def create_data_folders(syn, project_id, tasks_count): def create_annotations(syn, project_id, table_ids, folder_ids): """Create annotations that will power the portal.""" - logger.info("Creating basic annotations...") + logger.info(" Creating basic annotations...") project = syn.get(project_id) # Set basic annotations to the project. @@ -288,13 +288,14 @@ def create_annotations(syn, project_id, table_ids, folder_ids): if not wiki.get('parentId') ][0] project['Overview'] = f"{project_id}/wiki/{main_wiki_id}" + project['Abstract'] = "More challenge information coming soon!" # # for role, synid in table_ids.items(): # # annots[role] = synid for i, synid in folder_ids.items(): project[f'Task_{i + 1}.DataFolder'] = synid print(project) project = syn.store(project) - logger.info("Annotations creation complete.") + logger.info(" Annotations creation complete.") return project @@ -370,7 +371,7 @@ def main(syn, challenge_name, tasks_count, live_site=None): # Checks if staging wiki exists, if so delete check_existing_and_delete_wiki(syn, project_staging.id) - logger.info(f"Copying wiki template to {project_staging.name}") + logger.info(f" Copying wiki template to {project_staging.name}") new_wikiids = synapseutils.copyWiki( syn, CHALLENGE_TEMPLATE_SYNID, project_staging.id ) From 38689913e7d4b0150540eb6cd13c4a48838a1e4b Mon Sep 17 00:00:00 2001 From: verena <9377970+vpchung@users.noreply.github.com> Date: Wed, 22 Nov 2023 14:17:01 -0800 Subject: [PATCH 22/26] add more logging --- challengeutils/create_portal_challenge.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/challengeutils/create_portal_challenge.py b/challengeutils/create_portal_challenge.py index 62c55c81..9d1d116e 100644 --- a/challengeutils/create_portal_challenge.py +++ b/challengeutils/create_portal_challenge.py @@ -224,6 +224,7 @@ def create_organizer_tables(syn, project_id): syn: Synapse object parent_id: project synID """ + logger.info(" Creating tables...") view_ids = {} schema = synapseclient.Schema( name='Organizing Team', @@ -234,7 +235,7 @@ def create_organizer_tables(syn, project_id): # FIXME: for some reason, storing the table will then call on the # `challengeutils` CLI again table = syn.store(table) - logger.info(f" Table created: {table.name} ({table.id})") + logger.info(f" Table created: {table.name} ({table.id}) ✔") # FIXME: due to the issue above, we are not able to create the # MaterializedViews @@ -247,7 +248,7 @@ def create_organizer_tables(syn, project_id): ) view = syn.store(view) view_ids[role_title] = view.id - logger.info(f" MaterializedView created: {view.name} ({view.id})") + logger.info(f" MaterializedView created: {view.name} ({view.id}) ✔") return view_ids @@ -259,6 +260,7 @@ def create_data_folders(syn, project_id, tasks_count): parent_id: project synID tasks_count: Number of task folders to create """ + logger.info(" Creating folders...") folder_ids = {} for i in range(0, tasks_count): folder_name = f"Task {i + 1}" @@ -268,7 +270,7 @@ def create_data_folders(syn, project_id, tasks_count): ) folder = syn.store(folder) folder_ids[i] = folder.id - logger.info(f"Folder created: {folder.name} ({folder.id})") + logger.info(f" Folder created: {folder.name} ({folder.id}) ✔") return folder_ids @@ -293,7 +295,6 @@ def create_annotations(syn, project_id, table_ids, folder_ids): # # annots[role] = synid for i, synid in folder_ids.items(): project[f'Task_{i + 1}.DataFolder'] = synid - print(project) project = syn.store(project) logger.info(" Annotations creation complete.") return project From 02165f63c470f862e93f03f616cd3ca4d1dc99f2 Mon Sep 17 00:00:00 2001 From: verena <9377970+vpchung@users.noreply.github.com> Date: Wed, 22 Nov 2023 14:22:50 -0800 Subject: [PATCH 23/26] fix --- challengeutils/create_portal_challenge.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/challengeutils/create_portal_challenge.py b/challengeutils/create_portal_challenge.py index 9d1d116e..a8da806a 100644 --- a/challengeutils/create_portal_challenge.py +++ b/challengeutils/create_portal_challenge.py @@ -235,7 +235,7 @@ def create_organizer_tables(syn, project_id): # FIXME: for some reason, storing the table will then call on the # `challengeutils` CLI again table = syn.store(table) - logger.info(f" Table created: {table.name} ({table.id}) ✔") + logger.info(f" Table created: {table.name} ({table.id}) ✔") # FIXME: due to the issue above, we are not able to create the # MaterializedViews @@ -248,7 +248,7 @@ def create_organizer_tables(syn, project_id): ) view = syn.store(view) view_ids[role_title] = view.id - logger.info(f" MaterializedView created: {view.name} ({view.id}) ✔") + logger.info(f" MaterializedView created: {view.name} ({view.id}) ✔") return view_ids @@ -270,7 +270,7 @@ def create_data_folders(syn, project_id, tasks_count): ) folder = syn.store(folder) folder_ids[i] = folder.id - logger.info(f" Folder created: {folder.name} ({folder.id}) ✔") + logger.info(f" Folder created: {folder.name} ({folder.id}) ✔") return folder_ids @@ -296,7 +296,7 @@ def create_annotations(syn, project_id, table_ids, folder_ids): for i, synid in folder_ids.items(): project[f'Task_{i + 1}.DataFolder'] = synid project = syn.store(project) - logger.info(" Annotations creation complete.") + logger.info(" Annotations creation complete ✔") return project From 42734675f2b0a729b0e605c8284456941bfad471 Mon Sep 17 00:00:00 2001 From: verena <9377970+vpchung@users.noreply.github.com> Date: Wed, 22 Nov 2023 14:28:39 -0800 Subject: [PATCH 24/26] add step of adding project to project view --- challengeutils/create_portal_challenge.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/challengeutils/create_portal_challenge.py b/challengeutils/create_portal_challenge.py index a8da806a..94f92694 100644 --- a/challengeutils/create_portal_challenge.py +++ b/challengeutils/create_portal_challenge.py @@ -24,6 +24,7 @@ logger = logging.getLogger(__name__) +PORTAL_TABLE = "syn51476218" CHALLENGE_TEMPLATE_SYNID = "syn52941681" TABLE_TEMPLATE_SYNID = "syn52955244" CHALLENGE_ROLES = ['organizer', 'contributor', 'sponsor'] @@ -410,6 +411,12 @@ def main(syn, challenge_name, tasks_count, live_site=None): ) syn.store(task_subwiki) + # Add project to portal table. + project_view = syn.get(PORTAL_TABLE) + project_view.scopeIds.append(project_live.id) + syn.store(project_view) + logger.info(f" Challenge added to 'Curated Challenge Projects' ({PORTAL_TABLE})") + return { "live_projectid": project_live.id, "staging_projectid": project_staging.id, From 4d902558284d13b50a24f7d262c114d245e9211a Mon Sep 17 00:00:00 2001 From: verena <9377970+vpchung@users.noreply.github.com> Date: Wed, 22 Nov 2023 14:51:22 -0800 Subject: [PATCH 25/26] lint --- challengeutils/__main__.py | 3 ++- challengeutils/create_portal_challenge.py | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/challengeutils/__main__.py b/challengeutils/__main__.py index cc36bad9..4572c5e9 100644 --- a/challengeutils/__main__.py +++ b/challengeutils/__main__.py @@ -468,7 +468,8 @@ def build_parser(): parser_createchallenge.set_defaults(func=command_createchallenge) parser_create_portal_challenge = subparsers.add_parser( - "create-portal-challenge", help="Create a Sage Challenge Portal challenge from template" + "create-portal-challenge", + help="Create a Sage Challenge Portal challenge from template" ) parser_create_portal_challenge.add_argument("challenge_name", help="Challenge name") parser_create_portal_challenge.add_argument( diff --git a/challengeutils/create_portal_challenge.py b/challengeutils/create_portal_challenge.py index 94f92694..4606fd81 100644 --- a/challengeutils/create_portal_challenge.py +++ b/challengeutils/create_portal_challenge.py @@ -255,7 +255,7 @@ def create_organizer_tables(syn, project_id): def create_data_folders(syn, project_id, tasks_count): """Create folders for challenge data, one for each task. - + Args: syn: Synapse object parent_id: project synID @@ -345,7 +345,8 @@ def main(syn, challenge_name, tasks_count, live_site=None): f"Task {i + 1} Submission", project_live.id, ) - # TODO: the following function does not work for some reason; see function for details + # TODO: the following function does not work for some reason; see function + # for details # tables = create_organizer_tables(syn, project_live.id) tables = {} From 1161a72e48886946789330f6bf9667b94fe9225c Mon Sep 17 00:00:00 2001 From: verena <9377970+vpchung@users.noreply.github.com> Date: Wed, 22 Nov 2023 14:53:57 -0800 Subject: [PATCH 26/26] one more lint --- challengeutils/create_portal_challenge.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/challengeutils/create_portal_challenge.py b/challengeutils/create_portal_challenge.py index 4606fd81..b14f762d 100644 --- a/challengeutils/create_portal_challenge.py +++ b/challengeutils/create_portal_challenge.py @@ -259,7 +259,7 @@ def create_data_folders(syn, project_id, tasks_count): Args: syn: Synapse object parent_id: project synID - tasks_count: Number of task folders to create + tasks_count: Number of task folders to create """ logger.info(" Creating folders...") folder_ids = {}