1

To clarify the question, using a Flask URL converter how would one parse the json at this website by entering a url such as: https://ds-med-cabinet.herokuapp.com/strainjson/Purple-Kush so that when the user visits the /Purple-Kush website only that json object is displayed? This website is for building an API for educational purposes only so I would appreciate any ideas pertaining to parsing the entire json by URL input mostly likely using Flask URL converter or any other practical method. Thank you very much for your time and consideration. Here is the code I have tried, as a mock up of the Flask URL Converter documentation:

# Directory.py



# Import

from os import path
import pandas as pd
from flask import Blueprint, render_template
from werkzeug.routing import BaseConverter


# Make Blueprint for __init__.py

Directory = Blueprint("Directory", __name__)


# Import Leafly csv

file_name = path.join(path.dirname(__file__), "Leafly.csv")

df = pd.read_csv(file_name)

strains = df['Strain']



# Custom converter

class ListConverter(BaseConverter):

    def to_python(self, value):
        return value.split('+')

    def to_url(self, values):
        return '+'.join(BaseConverter.to_url(value)
                        for value in values)


# Flask Url-converter

@Directory.route('/<strain>')
def strain_url(strain):
    """Show the json object for the given strain."""
    strain = []
    for strain in strains:
        strain

    return render_template('json.html', strain=strain)
# __init__.py



# Imports

from flask import Flask
from web_app.routes.Directory import Directory, ListConverter
from web_app.routes.GET_PUT_API import GET_PUT_API


# Create Flask app

def create_app():
    
    app = Flask(__name__)

    app.register_blueprint(Directory)
    app.register_blueprint(GET_PUT_API)

    app.url_map.converters['list'] = ListConverter
    
    return app

if __name__ == "__main__":
    my_app = create_app()
    my_app.run(debug=True)

the strains in the for loop is a list of every strain from the csv version of the data, and the json.html being rendered is the html file of json objects that are being rendered at this website. This code and /whateveristypedintheurl just renders all of the data at the website shared (because the html file is already full of json objects and nothing is getting parsed). Thanks again for checking this out.

Ps. If trying to replicate this by creating a Flask App, you can find the csv here as cannabis.csv (I switched the named to Leafly.csv) and you can convert the df to json by using the following code:

# dftojson.py


# Imports

from os import path
import csv
import json


file_path = r'C:\Users\johnj\OneDrive\Documents\Lambda\BuildWeek3\data-science\cannabis.csv'

csvfile = open(file_path, encoding="utf8")
jsonfile = open('cannabis.json', 'w')


fieldnames = ("Strain", "Type", "Rating", "Effects", "Flavor"," Description")
reader = csv.DictReader(csvfile, fieldnames)
for row in reader:
    json.dump(row, jsonfile)
    jsonfile.write('\n')

I copied and pasted the json from cannabis.json into a new json.html file (or just change the file extension) and then added the route like so:

# Directory.py



# Strain JSON Page

@Directory.route("/strainjson")
def df():
    return render_template("json.html")
johnjdailey
  • 33
  • 2
  • 7
  • do you created your `Custom converter` as per the tutorial ? if so please share with us your code – cizario Jun 25 '20 at 11:06
  • @cizario I forgot to do that late last night so thank you for this reminder. I just created the Customer converter, however with no change to output. Any insight into the code now shared? Thanks again. – johnjdailey Jun 25 '20 at 22:01
  • 1
    would you mind giving me a link to download `Leafly.csv` i'll try to create a functional `Flask` app. – cizario Jun 25 '20 at 22:28
  • 1
    Sure, it's called cannabis.csv here: https://www.kaggle.com/kingburrito666/cannabis-strains – johnjdailey Jun 25 '20 at 22:50

2 Answers2

1

if i understand your concern very well, there's 2 parts:

  • create Custom URL Converter (refer to tutorial and doc ) to get a 2 separate lists of columns and terms following those patterns:
(venv) C:\Python37\myapps\flask\cannabis>flask routes
Endpoint            Methods  Rule
------------------  -------  -----------------------------
api.index           GET      /api/
api.index           GET      /api/<list:cols>
api.index           GET      /api/<list:cols>/<list:terms>
  • with pandas reading a cannabis.csv file (download) you filter data depending on what you get as list of columns combined with an optional list of terms

for the moment the part 1 is SOLVED and i'm little bit stuck with pandas

i've made my best and this how i setup a working Flask app demo:

cannabis
.. cannabis

.... api
      __init__.py
      views.py

.... errors
      __init__.py
      views.py

.... __init__.py
.... cannabis.py
.... utils.py

.. tests
.. venv
.. cannabis.csv
.. .flaskenv

/.flaskenv

FLASK_APP=cannabis:create_app()
FLASK_ENV=development
FLASK_DEBUG=0

/cannabis/__init __.py

from .cannabis import create_app

/cannabis/cannabis.py

from flask import Flask

from .utils import ListConverter


def create_app():
    """Create a Flask application using the app factory pattern."""

    app = Flask(__name__)
    app.url_map.converters['list'] = ListConverter

    """Register blueprints."""
    from .errors import bp as errors_bp
    app.register_blueprint(errors_bp)

    from .api import bp as api_bp
    app.register_blueprint(api_bp, url_prefix='/api')

    
    return app

/cannabis/utils.py

from werkzeug.routing import BaseConverter

class ListConverter(BaseConverter):

    def to_python(self, values):
        return values.split('+')

    def to_url(self, values):
        return '+'.join(value for value in values)

for to_url() function i think you don't need BaseConverter.to_url(value) otherwise you'll stuck with error:

[..]
File "C:\Python37\myapps\flask\cannabis\cannabis\utils.py", line 11, in <genexpr>
    return '+'.join(BaseConverter.to_url(term) for term in terms)
TypeError: to_url() missing 1 required positional argument: 'value'

/cannabis/api/__init __.py

from .views import bp

/cannabis/api/views.py

from flask import Blueprint, jsonify

bp = Blueprint('api', __name__)

@bp.route('/', defaults={'cols': [], 'terms': []})
@bp.route('/<list:cols>', defaults={'terms': []})
@bp.route('/<list:cols>/<list:terms>')
def index(cols, terms):

    cols_a = []
    for col in cols:
        if col: cols_a.append(col)

    terms_a = []
    for term in terms:
        if term: terms_a.append(term)

    # we need list of cols so we can filter data for list of terms within the list of cols
    if not cols_a and terms_a:
        return jsonify(message="Please choose at least one column")
        
    kws = []
    kws.append(cols_a)
    kws.append(terms_a)


    # form the moment just make sure we get the 2 lists/arrays
    # then filter data with pandas using the combination of 2 lists then convert to json and return result
    # .. WORKING ..
   
    return jsonify(message="api index page", kws=kws)

/cannabis/errors/__init __.py

from .views import bp

/cannabis/errors/views.py

from flask import Blueprint, jsonify
from werkzeug.exceptions import HTTPException

bp = Blueprint('errors', __name__)

@bp.app_errorhandler(HTTPException)
def handle_exception(e):
    return jsonify(code=e.code,
                   name=e.name,
                   description=e.description)

now you can run the Flask app and make some tests with different URLS:

no given columns and terms lists, default : return non-filtered data
http://localhost:5000/api/
{"kws":[[],[]],"message":"api index page"}

1 column is given , return data with only the give column
http://localhost:5000/api/Strain
{"kws":[["Strain"],[]],"message":"api index page"}

N columns are given , return data with only the give N columns
http://localhost:5000/api/Strain+Rating
{"kws":[["Strain","Rating"],[]],"message":"api index page"}

notice this url with extra optional '+', it works.
http://localhost:5000/api/Strain++++Rating++Flavor
{"kws":[["Strain","Rating","Flavor"],[]],"message":"api index page"}

N columns and M terms are given , return data with only the give N columns and  filtered rows that contains M terms (CHECK how to use this with pandas)
http://localhost:5000/api/Strain++Rating++Flavor/Purple-Kush++5+++Blackberry+++
{"kws":[["Strain","Rating","Flavor"],["Purple-Kush","5" "Blackberry"]],"message":"api index page"}


notice this url when no column is given.
http://localhost:5000/api/+/Purple-Kush
{"message":"Please add at least one column"}

other url, return non-filtered data
http://localhost:5000/api/+++/+++
{"kws":[[],[]],"message":"api page"}


[..]

i'll update my code once i finish implementing pandas..

other resources: flask jsonify vs python json dumps : json.dumps vs flask.jsonify

cizario
  • 3,995
  • 3
  • 13
  • 27
1

This is what I came up with. I got built off this article: Medium article on using CSV with Flask. For example locally in your URL you can type ' 5Th-Element' and that should display is JSON format. Adding jsonify on the return will help with API issues.

import csv
from os import path
from flask import Flask, render_template, Blueprint, jsonify, json


# Make Blueprint for __init__.py

  ParseURL = Blueprint("ParseURL", __name__)

# Import Leafly csv

  file_name = path.join(path.dirname(__file__), "Leafly.csv")


# route to display single dictionary list item as JSON object

@APP.route('/<strain>')
def strain_url(strain):
    '''
    Parameters: name of strain from database as a string.
    For loops the cannabis.csv file, creating a dictionary.
    Returning only the strain that was given as a parameter.
    '''
    with open('cannabis.csv') as csv_file:
        data = csv.reader(csv_file, delimiter=',')
        dict_strain = {}
        for row in data:
            if row[0] == strain:
                dict_strain = {
                    "strain": row[0],
                    "type": row[1],
                    "rating": row[2],
                    "effects": row[3],
                    "flavor": row[4],
                    "description": row[5]
                }
                break
    return jsonify(dict_strain)