This project features a robust Todo list REST API using Flask and Redis and a React/Redux front end.
In an attempt to learn more about the use of a strict key/value store, I started creating ToDo task items in the Redis CLI. After a few hours of building different representations of task items, I decided to build out a full API in Flask. This repository reflects both that API project, and a React front end that I build to render it in the browser.
Be sure to first install the latest versions of Python and React.
Next by making a clone of this repository.
~ mkdir todo-app
~ cd todo-app
~ git clone https://github.com/danyoungmusic93/ToDo-List-App
Because this repository is monolithic, be sure to create separate dependency management systems for the ./api
and ./client
directories.
~ python -m virtualenv api
::virtual env output::
~ source bin/activate //or another command to initiate virtual environment
~ (api) cd api
~ (api) pip install -r requirements.txt
~ (api) which python
/env/bin/python // copy the path to the virtual environments version of python
This will install the primary dependencies for the Python/Flask API portion of our application. You will still need to set up some configuration files, and apply for an API key through Google's OAuth API. Once you have your login credentials, be sure to save them in a configuration file at /todo-app/api/server/authentication/google_api_config.py
.
The copied PYTHONPATH will be added to the top of the /todo-app/api/app.py
file in the following shebang format: #!/env/bin/python
. This allows us to launch our Flask server with the following command and output:
~ (api) ./app.py
* Serving Flask app "app" (lazy loading)
* Environment: production
WARNING: Do not use the development server in a production environment.
Use a production WSGI server instead.
* Debug mode: on
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
* Restarting with stat
* Debugger is active!
* Debugger PIN: 490-370-085
Now lets move onto the client side of our application.
~ (api) cd ..
~ (api) deactivate // to deactivate the virtual environment
~ cd client
~ npm install // installs nodemodules listed in package.json file
~ npm start
-- Lower-level concepts for the datastructure minded.
This project uses the Google OAuth2 API to allow for single sign-on, and stores the user's numerical Google ID in the Redis datastore. Task items are stored in a Redis hash, with a key containing both the user_id, and a unique task_id.
127.0.0.1:6379> hgetall key
127.0.0.1:6379> hgetall Todos:users:<user_id>:tasks:<task_id>
1) "title"
2) "Practice the saxophone"
3) "category"
4) "Freelance Work"
5) "date_created"
6) "20180502122411"
7) "due_date"
8) "20190209222411"
-- Note that Python datetime objects have been converted to integers.
The integer representation of dates allows for storage of task_ids in a sorted set for querying by date.
127.0.0.1:6379> zrange key start stop [WITHSCORES]
127.0.0.1:6379> zrange Todos:users:<user_id>:tasks:due_sort_all_task_ids 0 20190209222411
1) "0480a119a478b410bba4" (task_id1)
2) "55f1001e5afcdecd7a0a" (task_id2)
3) "fb4b0dc2756d5131a6f5" (task_id3)
-- Task_ids are generated from task_obj['title']
by using Python's built-in hashlib library to create a reversible 20 digit hashed number.
string = task_obj['title']
def _blake2b_hash_title(self, string):
h = blake2b(digest_size=10)
title = bytes(string, encoding="utf-8")
h.update(title)
task_id = h.hexdigest()
return task_id
This Redis implementation sacrifices space for speed by creating multiple sets for different query parameters. For example, when querying tasks of a certain category, this set returns a collection of task_ids:
127.0.0.1:6379> smembers Todos:users:<user_id>:tasks:<category>:task_ids
1) "c5a0c4f15622e15ab4c7"
2) "55f1001e5afcdecd7a0a"
-- These task_ids can then be used to query the hash map representation of the task object using hget as seen above.