-
Notifications
You must be signed in to change notification settings - Fork 0
List Posts by Rating #1
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
base: master
Are you sure you want to change the base?
Conversation
|
Hi Ivo. Thanks for taking the time to do the challenge. |
| @@ -1,2 +1,57 @@ | |||
| # ListPostsByRating | |||
| App developed in Python, using the Django library | |||
| [](https://travis-ci.com/Ivopires/ListPostsByRating) | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Kudos for the documentation!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey Miguel! Thanks for the feedback on the challenge
| # -*- coding: utf-8 -*- | ||
| from __future__ import unicode_literals | ||
|
|
||
| import numpy as np |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why did you feel the need to use numpy here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I decided to used the numpy library in order to create more precise and consistent floats with 64-bit representation, which were used in the compute_score method implemented.
|
|
||
| def compute_score(self): | ||
| """ | ||
| The compute_score function will compute the Wilson-score Interval, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What are the advantages of using this score algorithm here, as opposed to a simpler one?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The main reasoning behind my decision was to solve the problem that was specified in the challenge description, where 2 posts with the same ratio (ex: 60 up and 40 down votes, and 600 up and 400 down) would have to be evaluated with a different score rather than a simple ratio between up/down votes (which could be a possible implementation of a simpler algorithm)
| def __str__(self): | ||
| return self.post_text | ||
|
|
||
| def compute_score(self): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the application started growing and adding a lot of new functionalities, would you keep the business logic here in the models?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In that case, I think the best solution is to implement a new class with the business logic implemented here inside the model, and then move this method to that class. And from there one, implement different classes with different purposes.
|
|
||
| def up_vote(request, post_id): | ||
| post = get_object_or_404(Post, pk=post_id) | ||
| post.up_votes += 1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if we get two requests at the same time to update the same post with an upvote?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I completely neglected that fact when constructing the logic behind the up/down voting actions! On that case, if two concurrent requests would upvote a post there would happen an inconsistency on the up/down vote counting (where one of them would see the counter with the old value, for example 2 up votes, when the other had already incremented the counter to 3, for example)! But to resolve that problem, one could adopt a locking system to acquire the database lock before accessing and updating the database value of the post's upvotes.
|
|
||
|
|
||
| def up_vote(request, post_id): | ||
| post = get_object_or_404(Post, pk=post_id) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this the right place to hold the incrementing logic?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think one of the possible solutions would be to implement the incrementing logic on an external class, although on the Django documentation the logic that lays behind the data that is presented to the user is implemented on the "view" class.
In order to gain some insight into how Django makes the split of the different layers, I checked some of the Django's documentation, including the FAQ, and some threads on the Stackoverflow (as an example stackoverflow post)
paulosilva86
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice job Ivo. Let us ask you some questions in a code review style. Get back to us whenever you can. Take it easy with the beers 🍻 in Amsterdam 😉
challenge/list_posts/views.py
Outdated
|
|
||
| def index(request): | ||
| return HttpResponse("Your posts will be posted here") | ||
| latest_posts_list = Post.objects.all()[:10] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems to fetch all Posts and then filter the first 10. Am I right?
If that's true, imagine the Posts table has millions of records. What could be the issue here? How would you solve it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi Paulo, thanks for the review!
That could lead to a performance issue, but Django uses the Python's array-slicing syntax to limit the QuerySet to a certain number of results (in the code above, 10 rows), which is the equivalent to the to SQL's LIMIT and OFFSET.
At the beginning, I was also doubting about that syntax, but then after further reading of Django's documentation and other threads found that Django will use the limit clause in the SQL to fetch only the 10 rows.
challenge/list_posts/views.py
Outdated
| post.up_votes += 1 | ||
| elif vote_type == 'downvote': | ||
| post.down_votes += 1 | ||
| post.save() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Imagine you have 2 concurrent requests. Can you guarantee you won't lose votes with this solution? If not, how would you solve it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this question is similar to the question made by @Mike-Sundays, which answer could be found above.
But in short, I think a race condition between two concurrent requests would arise and one plausible solution would be to use a locking mechanism that could acquire the lock to the database before accessing and updating the database values for the post's votes.
| urlpatterns = [ | ||
| url(r'^$', views.index, name='index'), | ||
| url(r'^(?P<pk>[0-9]+)/$', views.PostDetailView.as_view(), name='post'), | ||
| url(r'^vote/(?P<post_id>[0-9]+)/$', views.vote, name='vote'), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi Ivo!
Thanks for your time!
Why do you implemented 2 ways to upvote/downvote? (vote/, upvote/, downvote/ )
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi Pedro, thanks for the review!
Basically, the vote/ endpoint is responsible to handle the POST requests that are sent by the vote mechanism that was implemented in the UI, where the user could choose from up/down vote!
It was just created as an extra feature where the user naturally would navigate from the "index" endpoint to a specific post (hold by the /:post_id endpoint) and then had the possibility to up/down vote the post.
|
|
||
| try: | ||
| vote_type = request.POST['vote'] | ||
| except (KeyError, Post.DoesNotExist): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey dude! Thanks for your time!
Imagine that this application goes to production and eventually you'll have errors like this one. What would you do to be notified about exceptions/errors and it's details?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔝
| @@ -1,2 +1,57 @@ | |||
| # ListPostsByRating | |||
| App developed in Python, using the Django library | |||
| [](https://travis-ci.com/Ivopires/ListPostsByRating) | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.

I chose the List Posts by Rating challenge from the list provided (list of programming challenges).
The challenge was developed using Python(2.7), which was the most familiar programming language, and the Django(1.11.15) framework.
Although, I had never used a Python framework for web application development, I thought it would be a good opportunity to use one.
In the README, you can find all the available application endpoints, as well as all the instructions necessary to run the application and its tests.