Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Jobs: Added API endpoint for creating submission to a Challenge Phase #396

Merged
merged 21 commits into from Jan 13, 2017

Conversation

deshraj
Copy link
Member

@deshraj deshraj commented Jan 10, 2017

No description provided.

@deshraj deshraj changed the title Jobs: Added API endpoint for making submission to a Challenge Phase [In-Progress] Jobs: Added API endpoint for making submission to a Challenge Phase Jan 10, 2017
@@ -36,8 +40,10 @@ class Submission(TimeStampedModel):
(FINISHED, FINISHED),
)

participant_team = models.ForeignKey(ParticipantTeam, related_name='submissions')
challenge_phase = models.ForeignKey(ChallengePhase, related_name='submissions')
participant_team = models.ForeignKey(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we make this user? That will store the participant team information as well, right?
It will require one extra join though to query submission per team. Might be good to add both then?

Saying this because then we will loose out information regarding which user submitted. Not super relevant info but still.


urlpatterns = [
url(r'challenge/(?P<challange_id>[0-9]+)/'
r'challenge_phase/(?P<challange_phase_id>[0-9]+)/',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be appended with a submission end_point?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How do you update / delete / get the submission given submission id

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, we will add that functionality soon. This PR mainly focuses on creating the Submission only since this is a major feature.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, but it still feels odd to not have a submission end_point appended after challenge_phase_id. The post request should be sent to /challenge/<challenge_id>/challenge_phase<phase_id>/submission right?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, since it is in the jobs app, so the url would be /api/jobs/challenge/<challenge_id>/challenge_phase<phase_id>/submission. Does that sound good?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sg


from challenges.models import (
ChallengePhase,
Challenge,)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

comma at the end of a tuple seems inconsistent in the whole file

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the convention that we should ideally follow and we have been maintaining this throughout the project.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not in this file though. There are a couple of places where there is no comma. Right?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, will look into it.

def get_participant_team_id_of_a_user_for_a_challenge(user, challenge_id):
"""Returns the participant team object for a particular user for a particular challenge"""
participant_teams = Participant.object.filter(user=user).values_list('teams', flat=True)
for participant_team in participant_teams:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There should be a better way to do this using joins.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, I am figuring that out.

def check_user_participated_in_challenge(user, challenge_id):
"""Returns boolean if the user has participated in a particular challenge"""
participant_teams = Participant.object.filter(user=user).values_list('teams', flat=True)
return Challenge.objects.filter(pk=challenge_id, participant_teams__in=participant_teams).exists()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should essentially use the information from get_participant_team_id_of_a_user_for_a_challenge function.
If participant_team is provided, use the info otherwise call the function to get that info based on user.
Should also rename these functions to make them concise.

@dexter1691
Copy link
Member

I am assuming this is gonna handle only the file upload part! actual update of the submission status will be done through channels?

if ((submissions_done_today_count + 1 - failed_count > self.challenge_phase.max_submissions_per_day) or
(self.challenge_phase.max_submissions_per_day == 0)):
print 'PERMISSION DENIED'
raise PermissionDenied(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we are not handling this exception in the views.py L78 I guess. We should handle this exception gracefully and send a proper error message.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure what kind of exception we can get there? Can you please elaborate more?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If permission is denied then on save, it will throw an error, right? This I guess will call 500 Internal error. We should catch this exception and throw a 401 error saying that max number of submissions have been made.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it will give a 403 response so we are good to go with this.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm. Ok for now but minor point is the stack trace will miss the actual endpoint that is called. Can you plz add a simple /TODO just so that we remember to check.

participant_team=self.participant_team,
status=Submission.FAILED).count()

offset_submission_count = self.submission_number - failed_count
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we rename this to successful_count?
Also we are going to use success_count more, so might be better to explicitly save that and not save failed_count?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, we are not storing the successful or failure counts since we can calculate that by seeing the status of the submissions made by the user.



@admin.register(Submission)
class SubmissioneAdmin(TimeStampedAdmin):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo, SubmissioneAdmin --> SubmissionAdmin

'submission_number', 'submitted_at', 'input_file', 'stdout_file', 'stderr_file')
list_filter = ('participant_team', 'challenge_phase',
'created_by', 'status', 'is_public')
search_fields = ('participant_team', 'challenge_phase',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ideally search field should contains fields which are text or integer(if it is very necessary). Can you please update here ?

from django.db import models
from django.db.models import Max
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you please order the import alphabetically ?

offset_submission_count = self.submission_number - failed_count

if (offset_submission_count > self.challenge_phase.max_submissions):
print "Checking to see if the offset_submission_count (%d) ", \
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this print statement can be more meaningful if we log additional details like submission id, participant team id or phase id. can you please add accordingly here and for the else block also


self.status = Submission.SUBMITTED

saved_model = super(Submission, self).save(*args, **kwargs)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think this variable name should be renamed to saved_model_instance. It actually makes more sense.


from accounts.permissions import HasVerifiedEmail

from participants.utils import (
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you please reorder imports alphabetically ?

# check if the challenge exists or not
try:
challenge = Challenge.objects.get(pk=challenge_id)
if not challenge.is_active():
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what if take is_active check out of try-catch

return Response(response_data, status=status.HTTP_401_UNAUTHORIZED)

# check if challenge is public and accepting solutions
if not challenge.is_public:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why querying for phase, when challenge is not active, should be after challenge.is_public check


if ((submissions_done_today_count + 1 - failed_count > self.challenge_phase.max_submissions_per_day) or
(self.challenge_phase.max_submissions_per_day == 0)):
print 'PERMISSION DENIED'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also this print statement can be made more meaningful, so that it becomes easier for us to debug any issue(in case if it happens).
One more thing which I am curious about is why are we not using python logging module?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah we will replace all the debugging statements with logging once the pr is ready.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is still pending.

(self.challenge_phase.max_submissions_per_day == 0)):
print 'PERMISSION DENIED'
raise PermissionDenied(
"The maximum number of submissions this day have been reached.")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it should be The maximum number of submissions that can be made today has reached or The maximum number of submission for today has been reached.

print 'PERMISSION DENIED'
raise PermissionDenied(
"The maximum number of submissions this day have been reached.")
else:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really didn't get this else here. What is it for? Can you explain me more clearly?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just realized that it's not required here according to our use case. I will remove it.

if ((submissions_done_today_count + 1 - failed_count > self.challenge_phase.max_submissions_per_day) or
(self.challenge_phase.max_submissions_per_day == 0)):
print 'PERMISSION DENIED'
raise PermissionDenied(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm. Ok for now but minor point is the stack trace will miss the actual endpoint that is called. Can you plz add a simple /TODO just so that we remember to check.

@deshraj deshraj changed the title [In-Progress] Jobs: Added API endpoint for making submission to a Challenge Phase Jobs: Added API endpoint for making submission to a Challenge Phase Jan 11, 2017
@deshraj deshraj changed the title Jobs: Added API endpoint for making submission to a Challenge Phase Jobs: Added API endpoint for creating submission to a Challenge Phase Jan 11, 2017
@deshraj
Copy link
Member Author

deshraj commented Jan 11, 2017

@trojan can you please review the PR again? I have added the tests.

successful_count = self.submission_number - failed_count

if (successful_count > self.challenge_phase.max_submissions):
print "Checking to see if the successful_count (%d) ", \
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the print message is fine, but what if we write it like this(suggestion)

print 'Submission request is submitted by user {user_id} from participant team {} with successful count {} for challenge phase {} with max submission {}'.format(user_id=, ....)

This way it will be easier for us to grep from logs in case of error.

raise PermissionDenied(
"The maximum number of submissions has been reached.")
else:
print "Submission is below for user (%d) form participant_team (%d) for challenge_phase (%d)" % (
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i guess if we print the above message, then this print is absolutely not required.

try:
challenge = Challenge.objects.get(pk=challenge_id)
except Challenge.DoesNotExist:
response_data = {'error': 'Challenge does not exist!'}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should enforce a standard here, some messages have an exclamation at the end, where as others dont.
just a suggestion.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets remove the exclamation mark. We have been following that convention so far.

successful_count = self.submission_number - failed_count

if successful_count > self.challenge_phase.max_submissions:
print "Checking to see if the successful_count {0} ", \
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you combine both the print statements. Also I will prefer that log a print statement containing every required detail just above the if statement and then you can remove print in else statement.


if ((submissions_done_today_count + 1 - failed_count > self.challenge_phase.max_submissions_per_day) or
(self.challenge_phase.max_submissions_per_day == 0)):
print 'PERMISSION DENIED'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is still pending.

if serializer.is_valid():
serializer.save()
response_data = serializer.data
print response_data
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think this print statement should be not there.

try:
participant_team = ParticipantTeam.objects.get(pk=participant_team_id)
except ParticipantTeam.DoesNotExist:
response_data = {'error': 'You haven\'t participated in the challenge'}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can write this string in double quotes, then we wont have to escape t

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have used single quotes since we have been following this convention so far.

from django.contrib.auth.models import User
from django.utils import timezone


Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a extra newline.

return Challenge.objects.filter(pk=challenge_id, participant_teams__in=participant_teams).exists()


def get_participant_team_id_of_user_for_a_challenge(user, challenge_id):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see if this helps here ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am pretty sure that there is a better way of doing this. Will look into it. For now, I think we are good with this.

@deshraj
Copy link
Member Author

deshraj commented Jan 13, 2017

@trojan can you please check and merge it so that we can start integrating the worker?

@taranjeet
Copy link
Member

@deshraj : looks good, can be merged now

@deshraj deshraj merged commit 0bf97ba into master Jan 13, 2017
@deshraj deshraj deleted the submission branch January 13, 2017 05:44
deshraj added a commit that referenced this pull request Oct 24, 2018
…#396)

* Challenges: property methods added to Challenge and ChallengePhase Model

* Docstring added to each method

* Tests added for Challenge model

* More test cases added

* Jobs: Added API endpoint for making submission to a Challenge Phase

* Minor fixes incorporating the changes suggested

* API endpoint for submission ready

* Unused imports removed

* Minor additions in the business logic

* Tests added for Challenge Submission API

* Unused imports removed from jobs app

* Removed unused modules from tests

* Removed unused modules from views

* Logging statments added for jobs models

* Tests modified for Jobs app
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants