Skip to main content
Skip table of contents

Evaluating Submissions

Synapse collects all submissions via Evaluation Queues, and you can view and monitor them using Submission Views. This tutorial will walk you through how to view the submissions, download them for evaluation, and upload their scores back to Synapse. Looking to automate this process? Check out the last section for some available resources!

Need more help with infrastructure setup? Reach out to the Challenges & Benchmarking Service Desk and a CNB team member will be in touch.

(plus) Learn more about Evaluation Queues.

(plus) Learn more about setting up Submission Views at Creating the Submission View.


Retrieving Submissions

Before you can retrieve submissions from Synapse, ensure you have at least "Can score" permissions for the relevant Evaluation Queue(s), otherwise, an error about permissions will be encountered.

If you had created the Queues, you should already have "Admin" privileges, which grant you the necessary permissions to view and retrieve submissions.

View Submissions Directly from Evaluation Queues

To view submissions programmatically, you can use syn.getSubmissions(EVALUATION_ID) [ref]. For example:

Python

PY
import synapseclient

syn = synapseclient.login()

evaluation_id = "9615516"
submissions = syn.getSubmissions(evaluation_id)  # returns a generator of submissions
for submission in submissions:
  print(submission)

# To view the submissions in a dataframe, use pandas.
import pandas as pd

submissions_df = pd.DataFrame(syn.getSubmissions(evaluation_id))
print(submissions_df)

R

R
library(dplyr)
library(jsonlite)
library(synapser)

synLogin()

submissions <- synGetSubmissions("9615516")$asList()

# To view the submissions in a dataframe
submissions_df <- lapply(submissions, function(s) {
  data.frame(
    id = s$id,
    userId = s$userId,
    evaluationId = s$evaluationId,
    entityId = s$entityId,
    entityBundleJSON = s$entityBundleJSON,
    versionNumber = s$versionNumber,
    name = s$name,
    createdOn = as.character(s$createdOn),
    contributors = as.character(toJSON(s$contributors, auto_unbox = TRUE)),
    stringsAsFactors = FALSE
  )
}) %>% bind_rows()

print(submissions_df)

All new submissions are denoted with a status of RECEIVED. To view only new submissions, add status=”RECEIVED” to the method call:

Python

PY
submissions = syn.getSubmissions(evaluation_id, status="RECEIVED")

R

R
submissions <- synGetSubmissions("9615516", status="RECEIVED")$asList()


If you do not know the evaluation ID, you can first get the Evaluation object by using syn.getEvaluationByName(EVALUATION_NAME)and then pass it into syn.getSubmissions(). For example:

Python

PY
evaluation = syn.getEvaluationByName("MY_EVALUATION_QUEUE_NAME")
submissions = syn.getSubmissions(evaluation)

R

R
evaluation <- synGetEvaluationByName("MY_EVALUATION_QUEUE_NAME")
submissions <- synGetSubmissions(evaluation)

If you also don’t know the name, contact the Synapse user who created the Challenge project. They are likely the current admin of the Evaluation Queue(s) and can provide you with the evaluation ID(s). Otherwise, submit a support ticket to the Challenges & Benchmarking Service Desk for further assistance.

View Submissions via Submission View

If you have already set up a Submission View, you can view submissions by querying that table programmatically using syn.tableQuery(QUERY) [ref]:

Python

PY
import pandas as pd
import synapseclient

syn = synapseclient.login()

# View all submissions.
view_id = "syn53293336"
submissions_df = (
  syn.tableQuery(f"SELECT * FROM {view_id}")
  .asDataFrame()
  .fillna("")
)
print(submissions_df)

R

R
library(synapser)
library(tidyverse)

synLogin()

# View all submissions.
view_id <- "syn53293336"
submissions_df <- 
  synTableQuery(str_glue("SELECT * FROM {view_id}")) %>%
  .$asDataFrame() %>%
  mutate(across(everything(), ~ ifelse(is.na(.) | . == "NaN", "", .)))

(plus) See Using Advanced Search Queries for more examples of SQL-like queries supported by Synapse.

To view only new submissions, add status = 'RECEIVED' as a clause to the query:

Python

PY
import pandas as pd
import synapseclient

syn = synapseclient.login()

# View only new submissions.
view_id = "syn53293336"
submissions_df = (
  syn.tableQuery(f"SELECT * FROM {view_id} WHERE status = 'RECEIVED'")  # add a clause
  .asDataFrame()
  .fillna("")
)
print(submissions_df)

R

R
library(synapser)
library(tidyverse)

synLogin()

# View only new submissions.
view_id <- "syn53293336"
submissions_df <- 
  synTableQuery(str_glue("SELECT * FROM {view_id} WHERE status = 'RECEIVED'")) %>%  # add a clause
  .$asDataFrame() %>%
  mutate(across(everything(), ~ ifelse(is.na(.) | . == "NaN", "", .)))

Download Submissions

While there is not currently a feature on the Synapse web UI to download submissions, you can do so programmatically. Technically, you will be downloading Submission objects, which are copies of the entities submitted to an Evaluation Queue.

As long as you have at least "Can score" permissions on the queue, you will be able to access and download these Submission objects.

File Submissions

You can directly download file submissions by using the submission ID with syn.getSubmission(SUBMISSION_ID) [ref].

syn.getSubmission() is not the same as syn.getSubmissions()!

syn.getSubmission() (no “s” at the end) retrieves the metadata for a single submission and downloads it if it’s a file. Whereas syn.getSubmissions() only retrieves metadata of multiple submissions submitted to an Evaluation Queue.

Python

PY
import synapseclient

syn = synapseclient.login()
submission_id = 9743445
syn.getSubmission(submission_id)

# By default, all files are downloaded to ~/.synapseCache. To specify
# a different location, use `downloadLocation`.
syn.getSubmission(submission_id, downloadLocation="path/to/download")

R

R
library(synapser)

synLogin()
submission_id <- 9743445
synGetSubmission(submission_id)

# By default, all files are downloaded to ~/.synapseCache. To specify
# a different location, use `downloadLocation`.
synGetSubmission(submission_id, downloadLocation = "path/to/download")

Docker Submissions

For Docker submissions, you will need to use Docker to retrieve the submitted images; using syn.getSubmission() will not pull the image onto your machine. To get the image name associated with a submission, combine the dockerRepositoryName and dockerDigest from the Submission object, separated by an @ symbol, e.g.{dockerRepositoryName}@{dockerDigest}.

CLI

BASH
docker pull DOCKER_REPOSITORY_NAME@DOCKER_DIGEST

Python

PY
import docker
import synapseclient

syn = synapseclient.login()

# Setup Docker client.
client = docker.from_env()

# Get submission metadata with syn.getSubmission(..., downloadFile=False).
submission_id = 9753713
submission = syn.getSubmission(submission_id, downloadFile=False)
repo_name = submission.get("dockerRepositoryName")
digest = submission.get("dockerDigest")

# Attempt to pull Docker submission, otherwise output error message.
try:
  client.images.pull(f"{repo_name}@{digest}")
except docker.errors.APIError:
  print(f"Something went wrong with pulling submission {submission_id}")

This example uses the Docker SDK for Python to programmatically pull the images.

R

R
library(synapser)
library(tidyverse)

synLogin()

# Login to Synapse Docker Registry.
system("docker login docker.synapse.org")

# Get submission metadata with syn.getSubmission(..., downloadFile=False).
submission_id <- 9753713
submission <- synGetSubmission(submission_id)
repo_name <- submission$dockerRepositoryName
digest <- submission$dockerDigest

# Attempt to pull Docker submission, otherwise output error message.
if (nzchar(repo_name) && nzchar(digest)) {
  exit_code <- system(str_glue("docker pull {repo_name}@{digest}"))
  if (exit_code != 0) {
    message(str_glue("Something went wrong with pulling submission {submission_id}"))
  }
} else {
  message(str_glue("Submission {submission_id} is not a Docker submission"))
}

If you are pulling the submission metadata via a Submission View instead of using syn.getSubmission(), the annotation names will be in all lowercase (dockerrepositoryname and dockerdigest) instead of camel case, e.g.

PY
submissions_df = syn.tableQuery(...)

for _, row in submissions_df.iterrows():
  submission_id = row['id']
  repo_name = row["dockerrepositoryname"]
  digest = row["dockerdigest"]

Putting It All Together

You now know how to query for new submissions and download them. By combining these steps, you can create a single script to evaluate all new submissions.

Once a submission is evaluated, we recommend updating its status from RECEIVED so that it does not get picked up again if/when you re-query for new submissions in the future.

Here's an example of an integrated script for downloading file submissions:

Python

PY
"""Evaluating file submissions."""
import os

import pandas as pd
import synapseclient

syn = synapseclient.login()

# Get new submissions.
view_id = "syn53293336"
submissions_df = (
  syn.tableQuery(f"SELECT * FROM {view_id} WHERE status = 'RECEIVED'")
  .asDataFrame()
  .fillna("")
)

# Evaluate each new submission.
for submission_id in submissions_df["id"]:

  # Download predictions file to /path/to/download/.
  submission = syn.getSubmission(submission_id, downloadLocation="/path/to/download")
  with open(submission.filePath) as f:
    scores = ...  # evaluate the predictions file
  
  # Update submission status to 'SCORED' if evaluation is successful, else 'INVALID'.
  submission_status_obj = syn.getSubmissionStatus(submission_id)
  submission_status_obj.status = "SCORED" if scores else "INVALID"
  syn.store(submission_status_obj)
  
  # File cleanup.
  try:
    os.remove(submission.filePath)
  except OSError as e:
    print(f"Could not delete predictions file for submission {submission_id}: {e}")

R

R
library(synapser)
library(tidyverse)

synLogin()

# Get new submissions.
view_id <- "syn53293336"
submissions_df <- 
  synTableQuery(str_glue("SELECT * FROM {view_id} WHERE status = 'RECEIVED'")) %>%
  .$asDataFrame() %>%
  mutate(across(everything(), ~ ifelse(is.na(.) | . == "NaN", "", .)))

# Evaluate each new submission.
for (submission_id in submissions_df$id) {
  
  # Download predictions file to /path/to/download/.
  submission <- synGetSubmission(submission_id, downloadLocation = "/path/to/download")
  pred_file <- submission$filePath
  scores <- ...  # evaluate the predictions file

  # Update submission status to 'SCORED' if evaluation is successful, else 'INVALID'.
  submission_status_obj = synGetSubmissionStatus(submission_id)
  submission_status_obj$status <- ifelse(!is.null(scores), "SCORED", "INVALID")
  synStore(submission_status_obj)
  
  # File cleanup.
  tryCatch({
    file.remove(submission$filePath)
  }, error = function(e) {
    message(str_glue("Could not delete predictions file for submission {submission_id}: {e}"))
  })
}

(plus) Submission status can only be set to specific values; refer to SubmissionStatusEnum for a list of acceptable values.


Assigning and Displaying Scores

Scores are assigned to submissions by adding them as annotations. You can then display these scores in a Submission View by updating the view's schema to include these new annotations.

Annotate Submissions

You can only add annotations to submissions programmatically; the Synapse web UI does not currently support this feature.

Python

PY
import synapseclient

syn = synapseclient.login()

# Get submission status object.
submission_id = 123
submission_status_obj = syn.getSubmissionStatus(submission_id)

# Add scores to the annotations metadata.

## adding one score
submission_status_obj.submissionAnnotations["auc_roc"] = 0.0

## adding multiple scores
score_annots = {
  "auprc": 0.0,
  "pearson": 0.0
}
submission_status_obj.submissionAnnotations.update(score_annots)

# Save the new annotations.
syn.store(submission_status_obj)

R

R
library(synapser)

synLogin()

# Get submission status object.
submission_id <- 123
submission_status_obj <- synGetSubmissionStatus(submission_id)

# Add scores to the annotations metadata.

## adding one score
submission_status_obj$submissionAnnotations$auc_roc <- 0.0

## adding multiple scores
score_annots <- list(
  auprc = 0.0,
  pearson = 0.0
)
submission_status_obj$submissionAnnotations$update(score_annots)

# Save the new annotations.
synStore(submission_status_obj)

Display Scores

To display the scores on Synapse:

  1. Navigate to the Submission View containing the Evaluation Queue(s), and click on 3-bar icon (next to Submission View Tools) to Show Submission View Schema.

  2. The schema will now appear above the table. Click on Edit Schema and a new window will pop up.

  3. Click + Add Column. For “Column Name”, enter the exact annotation key name you used (e.g. auc_roc from the code examples above). Update the “Column Type” appropriately.

  4. Repeat Step 3 for each scoring metric you want to display.

  5. Click Save to apply the changes.

If done correctly, your Submission View should now include the new metric columns with scores displayed for each submission.

Troubleshooting: If scores are not appearing, double-check that the column names in your schema exactly match the annotation keys on your submissions, including casing. For example, AUC_ROC is not considered the same as auc_roc.

Another potential issue is a mismatch in the “Column Type”. For instance, if you specify "Integer" but your values are strings, the scores will not display.


Tools for Automation

If you're looking to automate the process of evaluating submissions as they come in, Sage offers several tools and services:

Orchestrators

  • ORCA (Paid Service): This tool uses NextFlow to run workflows. Your main job is to provide the evaluation code (template available below) which the Data Processing & Engineering (DPE) team then integrates into a workflow. For cost estimates, please contact the DPE Service Desk.

  • SynapseWorkflowOrchestrator: This tool executes Common Workflow Language (CWL) workflows, which you will design yourself (templates available below). This tool also requires manual setup and configuration to link it with your Challenge project. If you need help, contact the Challenges & Benchmarking Service Desk.

Workflow templates

JavaScript errors detected

Please note, these errors can depend on your browser setup.

If this problem persists, please contact our support.