Repository: Sage-Bionetworks/challengeutils

Issue:
createchallenge does not work!
**Describe the bug**
creating a challenge object used to use the `Challenge` class, but restPOST does not like 'None' values.

**To Reproduce**
```
syn.restPOST('/challenge',  str(new_challenge))                         
---------------------------------------------------------------------------
SynapseHTTPError                          Traceback (most recent call last)
<ipython-input-16-3bd029e22510> in <module>
      1 syn.restPOST('/challenge',
----> 2                                       str(new_challenge))

~/anaconda3/envs/challenge/lib/python3.7/site-packages/synapseclient/client.py in restPOST(self, uri, body, endpoint, headers, retryPolicy, **kwargs)
   3321         response = with_retry(lambda: self._requests_session.post(uri, data=body, headers=headers, **kwargs),
   3322                               verbose=self.debug, **retryPolicy)
-> 3323         exceptions._raise_for_status(response, verbose=self.debug)
   3324         return self._return_rest_body(response)
   3325 

~/anaconda3/envs/challenge/lib/python3.7/site-packages/synapseclient/core/exceptions.py in _raise_for_status(response, verbose)
    147                 message += "\nCould not append all response info"
    148 
--> 149         raise SynapseHTTPError(message, response=response)

SynapseHTTPError: 400 Client Error: 
For input string: "None"

In [17]: str(new_challenge)                                                                
Out[17]: "{'etag': None,\n 'id': None,\n 'participantTeamId': 3409986,\n 'projectId': 'syn22053727'}"
```


Discussion:

Patch:
diff --git a/LICENSE b/LICENSE
index 261eeb9..8b9a0c6 100644
--- a/LICENSE
+++ b/LICENSE
@@ -186,7 +186,7 @@
       same "printed page" as the copyright notice for easier
       identification within third-party archives.
 
-   Copyright [yyyy] [name of copyright owner]
+   Copyright 2017 Sage Bionetworks
 
    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
diff --git a/challengeutils/challenge.py b/challengeutils/challenge.py
index bb18b9a..a488a3d 100644
--- a/challengeutils/challenge.py
+++ b/challengeutils/challenge.py
@@ -36,10 +36,10 @@ class ChallengeApi:
             A synapseservices.Challenge
 
         """
-        new_challenge = Challenge(participantTeamId=teamid,
-                                  projectId=projectid)
+        challenge_object = {'participantTeamId': teamid,
+                            'projectId': projectid}
         challenge = self.syn.restPOST('/challenge',
-                                      str(new_challenge))
+                                      json.dumps(challenge_object))
         return Challenge(**challenge)
 
     def get_registered_challenges(self,
@@ -93,11 +93,11 @@ class ChallengeApi:
             A synapseservices.Challenge
 
         """
-        new_challenge = Challenge(id=challengeid,
-                                  participantTeamId=teamid,
-                                  projectId=projectid)
+        challenge_object = {'id': challengeid,
+                            'participantTeamId': teamid,
+                            'projectId': projectid}
         challenge = self.syn.restPUT(f'/challenge/{challengeid}',
-                                     str(new_challenge))
+                                     json.dumps(challenge_object))
         return Challenge(**challenge)
 
     def delete_challenge(self, challengeid: str):
diff --git a/challengeutils/createchallenge.py b/challengeutils/createchallenge.py
index 8595694..83b4ca6 100644
--- a/challengeutils/createchallenge.py
+++ b/challengeutils/createchallenge.py
@@ -281,8 +281,8 @@ def main(syn, challenge_name, live_site=None):
     else:
         project_live = syn.get(live_site)
 
-    challenge = create_challenge_widget(syn, project_live,
-                                        teams['team_part_id'])
+    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)
@@ -304,7 +304,7 @@ def main(syn, challenge_name, live_site=None):
     for page in new_wikiids:
         wikipage = syn.getWiki(project_staging, page['id'])
         wikipage.markdown = _update_wikipage_string(wikipage.markdown,
-                                                    challenge.id,
+                                                    challenge_obj.id,
                                                     teams['team_part_id'],
                                                     challenge_name,
                                                     project_live.id)


Test patch:
diff --git a/tests/test_challenge.py b/tests/test_challenge.py
index 882a569..fcee3d8 100644
--- a/tests/test_challenge.py
+++ b/tests/test_challenge.py
@@ -56,6 +56,8 @@ class TestChallengeApi:
         """Tests that a challenge object can be instantiated"""
         new_challenge = Challenge(projectId=self.projectid,
                                   participantTeamId=self.teamid)
+        challenge_object = {'participantTeamId': self.teamid,
+                            'projectId': self.projectid}
         with patch.object(self.syn, "restPOST",
                           return_value=self.challenge_dict) as patch_restpost:
             challenge_obj = self.challenge_api.create_challenge(
@@ -63,8 +65,9 @@ class TestChallengeApi:
                 projectid=self.projectid
             )
             assert challenge_obj == self.expected
-            patch_restpost.assert_called_once_with('/challenge',
-                                                   str(new_challenge))
+            patch_restpost.assert_called_once_with(
+                '/challenge', json.dumps(challenge_object)
+            )
 
     def test_get_challenge__with_id(self):
         """Tests getting challenge with challenge id"""
@@ -92,6 +95,9 @@ class TestChallengeApi:
 
     def test_update_challenge(self):
         """Tests updating Challenge"""
+        challenge_dict = {"id": self.challengeid,
+                          "participantTeamId": self.teamid,
+                          "projectId": self.projectid}
         with patch.object(self.syn, "restPUT",
                           return_value=self.challenge_dict) as patch_restput:
             challenge_obj = self.challenge_api.update_challenge(
@@ -102,7 +108,7 @@ class TestChallengeApi:
             assert challenge_obj == self.expected
             patch_restput.assert_called_once_with(
                 f'/challenge/{self.challengeid}',
-                str(self.input)
+                json.dumps(challenge_dict)
             )
 
     def test_delete_challenge(self):