This repository has been archived by the owner on Sep 13, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit a6e28f6
Showing
8 changed files
with
148 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
__pycache__/ | ||
.vscode/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
from flask_sqlalchemy import SQLAlchemy | ||
from flask import Flask | ||
app = Flask(__name__) | ||
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///clidata.sqlite3' | ||
app.config['SECRET_KEY'] = "aa20ff7c-8ad5-44a7-b0c3-8060b126e186" | ||
db = SQLAlchemy(app) | ||
|
||
|
||
class GithubUser(db.Model): | ||
id = db.Column(db.Integer, primary_key=True, autoincrement=False) | ||
user = db.Column(db.String(80), unique=True, nullable=False) | ||
image_url = db.Column(db.String(1200), unique=False, nullable=False) | ||
profile = db.Column(db.String(1200), unique=False, nullable=False) | ||
typ3 = db.Column(db.String(10), unique=False, nullable=False) | ||
|
||
def __repr__(self): | ||
return '<User %r>' % self.user | ||
|
||
def update_or_insert(user_dict): | ||
# get all incoming ids and ids on database | ||
all_incoming_ids = set(user_dict.keys()) | ||
all_ids = set(dict(db.session.query(GithubUser.id, GithubUser))) | ||
# which ones are already on db? | ||
updatable_ids = all_ids & all_incoming_ids | ||
# and which are new? | ||
insertable_ids = all_incoming_ids - updatable_ids | ||
# create the list of items to insert and to update in bulk | ||
upd = [user_dict[e] for e in updatable_ids] | ||
ins = [user_dict[e] for e in insertable_ids] | ||
db.session.bulk_insert_mappings(GithubUser, ins) | ||
db.session.bulk_update_mappings(GithubUser, upd) | ||
db.session.commit() | ||
|
||
return True | ||
|
||
def create_all(): | ||
SQLAlchemy.create_all(db) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
# From https://docs.github.com/en/rest/reference/users#list-users | ||
# example: https://api.github.com/users?per_page=10&since=0 | ||
|
||
import requests | ||
|
||
def get_github_users(per_page, since): | ||
'''Return a dict of id:user with specific keys from github users''' | ||
r = requests.get('https://api.github.com/users?per_page=%d&since=%d'%(per_page, since)) | ||
if r.status_code == 200: | ||
return {r['id']:{ | ||
'user': r['login'], | ||
'id': r['id'], | ||
"image_url": r['avatar_url'], | ||
"profile": r['html_url'], | ||
"typ3": r['type'] | ||
} for r in r.json()} | ||
else: | ||
return {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import click | ||
from githubusers import get_github_users | ||
from ghdb import create_all, update_or_insert | ||
|
||
@click.command() | ||
@click.option('--total', default=150, show_default=True) # TODO: type=click.IntRange(1, 100) | ||
@click.option('--page', default=0, show_default=True) | ||
def seed(total, page): | ||
'''Save the users from github user list''' | ||
create_all() | ||
user_list = get_github_users(total, page) | ||
update_or_insert(user_list) | ||
click.echo(f'%d users'%(total)) | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
Using the [API of GitHub](https://docs.github.com/en/free-pro-team@latest/rest/reference) do the following tasks: | ||
|
||
- Create a script called [seed.py](http://seed.py) that populates a SQLite database. By default it should search for the first 150 users from GitHub but the script should accept a param called total to customize the number of users. | ||
- The fields required for this users are: | ||
- username | ||
- id | ||
- image url | ||
- type | ||
- link to his GitHub profile | ||
|
||
Once you have created the script and has a complete db create a Flask application that has: | ||
|
||
- A view to show the info of all the users of the database in a table. Profile avatar should be visible and clicking the username should send me to the GitHub profile. Pagination is needed and by default it should be of size 25. Make sure that the page is responsive even with a large amount of data (use any optimization) *Optional but desired*: arguments to change pagination size and ordering | ||
|
||
Create an script to run your server and a README.md file where you explain your decisions and architecture. | ||
|
||
Unit tests for all the code is needed. | ||
|
||
After completion of the tasks create a public repository and put all the code there. Be sure to not include compiled files and the db. | ||
|
||
### Optional | ||
|
||
- Add an endpoint where you return a JSON with the information stored in the database. The user will be able to use query params to filter the results. At least there should be for username and id but for type, pagination size, order by and GitHub profile is a plus. Example: | ||
|
||
<your_endpoint>/profiles?username=test1&pagination=20&order_by=id | ||
|
||
- Deploy your app to a Heroku server or any other free option and include the link in the email | ||
- Include as many optimizations as you can (e.g cache) | ||
|
||
*Note: Things like code style, documentation, architecture are really important. Develop this app assuming production quality code* |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
from click.testing import CliRunner | ||
from githubusers import get_github_users | ||
from ghdb import db, GithubUser, update_or_insert | ||
|
||
|
||
def test_can_update_or_insert(): | ||
first_user = get_github_users(1,0) | ||
upd_word = 'No profile!!!!' | ||
first_user[1]['profile'] = upd_word | ||
assert update_or_insert(first_user) | ||
# retrieve again from db | ||
first_user = dict(db.session.query(GithubUser.id, GithubUser)) | ||
assert first_user[1].profile == upd_word |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
from click.testing import CliRunner | ||
from githubusers import get_github_users | ||
|
||
def test_first_user(): | ||
first_user = get_github_users(1,0) | ||
assert first_user == {1: | ||
{ | ||
"user": "mojombo", | ||
"id": 1, | ||
"image_url": "https://avatars.githubusercontent.com/u/1?v=4", | ||
"profile": "https://github.com/mojombo", | ||
"typ3": "User" | ||
} | ||
} | ||
|
||
def test_no_users(): | ||
no_result= get_github_users(10_000, 89_999_999) | ||
assert no_result == {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
from click.testing import CliRunner | ||
from seed import seed | ||
|
||
|
||
def test_no_params(): | ||
runner = CliRunner() | ||
result = runner.invoke(seed) | ||
assert result.exit_code == 0 | ||
assert result.output == '150 users\n' | ||
|
||
def test_total(): | ||
runner = CliRunner() | ||
result = runner.invoke(seed, ['--total',800]) | ||
assert result.exit_code == 0 | ||
assert result.output == '800 users\n' |