Code Monkey home page Code Monkey logo

todo-list-app's Introduction

ToDo List API

This project features a robust Todo list REST API using Flask and Redis and a React/Redux front end.

last-commit open-issues coverage

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.

Installation

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

Development setup

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

REDIS Implementation

-- Lower-level concepts for the datastructure minded.

Hashmap object storage

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.

Sorted Set (hash_name, date_int, task_id)

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.

todo-list-app's People

Contributors

drycode avatar

Stargazers

 avatar  avatar  avatar

Watchers

 avatar

todo-list-app's Issues

Redis.py in server directory

Move all Redis initializations into module files called redis_local.py and redis_production.py, and use the __init__.py to determine which file to call when the server starts. It may be that a fake_redis.py should be created to handle mocking needs for integration testing. #1

Organization of Import Statements

Pretty self-explanatory. Find a more pythonic way to organize the import statements to avoid duplication across files and directories.

Importing of Sibling Directories

On the original local version of this project, databaseconfig.py is being imported from a sibling directory. After cloning this project on another machine, it seems the import is not valid in the Python ecosystem.

Directories need to be adjusted to support importing appropriate modules that are currently housed in sibling directories.

Integration Testing of Redis Methods

At present, most of the testing in this repository is limited to unit testing of individual methods in auth.py or app.py. There is a need for more comprehensive testing, particularly with regards to the functions that interact directly with the Redis store. There are also not automated tests for the API's routes.

Python has various mocking libraries and a way to create integration tests in Redis with the fakeredis library.

Subtasks

Create instances of Redis lists for subtasks at Todos:users:<user_id>:subtasks:<task_id> in the set_tasks method in the redis_methods.py module. Implement querying of subtasks where necessary (likely on routes that query a single task)

Sessions not persisting after server restart

Session data needs to be stored in Redis in order to persist through server restarts, which are triggered automatically whenever a change is made to the code in development.

Pagination

GET methods will need to limit displayed tasks to between 8 and 12 tasks per query. A pagination argument will need to be implemented either by the API, or (more likely) by the front end.

Dict to use .get()

Self-explanatory: Use dict.get() in redis_methods.py to make the code more readable, and to marginally increase efficiency.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.