diff --git a/flask_to_docker/.old/app_third.py b/flask_to_docker/.old/app_third.py new file mode 100644 index 00000000..5d457963 --- /dev/null +++ b/flask_to_docker/.old/app_third.py @@ -0,0 +1,73 @@ +from flask import Flask, request, make_response, Response, render_template, redirect, url_for, send_from_directory, jsonify +import pandas as pd +import os +import uuid +app = Flask(__name__, template_folder='templates') + + +@app.route('/', methods=['GET', 'POST']) +def index(): + if request.method == 'GET': + return render_template('index.html') + elif request.method == 'POST': + username = request.form.get('username') + password = request.form.get('password') + + if username == 'kynsight' and password == 'pass': + return 'Success' + else: + return 'Failure' + + +@app.route('/file_upload', methods=['GET', 'POST']) +def file_upload(): + file = request.files.get('file') + if file.content_type == 'text/plain': + return file.read().decode() + elif file.content_type == 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' or file.content_type == 'application/vnd.ms-excel': + df = pd.read_excel(file) + return df.to_html() + +@app.route('/convert_csv', methods=['GET', 'POST']) +def convert_csv(): + file = request.files.get('file') + df = pd.read_excel(file) + response = Response( + df.to_csv(), + mimetype = 'text/csv', + headers={'Content-Disposition': 'attachement; filename=result.csv'} + ) + return response + +@app.route('/convert_csv_two', methods=['GET', 'POST']) +def convert_csv_two(): + file = request.files.get('file') + df = pd.read_excel(file) + + if not os.path.exists('downloads'): + os.makedirs('downloads') + + filename = f'{uuid.uuid4()}.cvs' + df.to_csv(os.path.join('downloads', filename)) + + return render_template('download.html', filename=filename) + +@app.route('/download/', methods=['GET', 'POST']) +def download(filename): + return send_from_directory('downloads', filename, download_name='result.csv') + + + +@app.route('/handle_post', methods=['GET', 'POST']) +def handle_post(): + greeting = request.json.get('greeting') + name = request.json.get('name') + + with open('file.txt','w') as f: + f.write(f'{greeting}, {name}') + + return jsonify({'message': 'Successfully written!'}) + + +if __name__ == '__main__': + app.run(host='0.0.0.0', port=5000, debug=True) diff --git a/flask_to_docker/README.md b/flask_to_docker/README.md index 2b317a90..348684a6 100644 --- a/flask_to_docker/README.md +++ b/flask_to_docker/README.md @@ -13,13 +13,13 @@ python3 -m venv .venv ## Activate the virtual enviroement Then you need to activate the virtual enviroment. - for Bash - ```bash - source .venv/bin/activate - ``` +```bash +source .venv/bin/activate +``` - for Fish - ```bash - . .venv/bin/activate.fish - ``` +```bash +. .venv/bin/activate.fish +``` --- If executed corectly your *(.venv)* should apear on your terminal ```bash @@ -91,9 +91,7 @@ In the *run()* funtion there are some main parameters that needs to be defined t - **port** ([*int*](https://docs.python.org/3/library/functions.html#int)* | **None*) – the port of the webserver. Defaults to *5000* or the port defined in the *SERVER_NAME* config variable if present. - **debug** ([*bool*](https://docs.python.org/3/library/functions.html#bool)* | **None*) if given, enable or disable debug mode. See [debug](https://flask.palletsprojects.com/en/2.3.x/api/#flask.Flask.debug). - --- - ## First Run The firsat run is simple enought. Just execute your *app.py*. Just make sure that your *venv* is active. ```bash @@ -127,10 +125,8 @@ Hereyou can see as before the main route *(/)* def index(): return "Welcome" ``` - --- - -## url proccessors +## URL proccessors We can also use url proccessors to get something that we have typed on the url. In this case the url to visit would be [http://localhost:5000/greet/kerem](http://localhost:5000/greet/kerem) ```python @@ -138,18 +134,14 @@ In this case the url to visit would be [http://localhost:5000/greet/kerem](http: def greet(name): return f"Hello {name}" ``` - ---- - Variables given to the url can also be *typed* here as integers. This allows us to do some advanced operations. In this case the url to visit would be [http://localhost:5000/add/20/10](http://localhost:5000/add/20/10) ```python @app.route('/add//') def add(number1, number2): return f'{number1} + {number2} = {number1+number2}' ``` - --- - +## Responses Now to do generate some responses. Here you can use [curl](https://curl.se/) so see exactly what is happening ```python #We can also return some responses (like 404 page not found and co) @@ -171,11 +163,9 @@ content-type: application/octet-stream Content-Length: 11 Connection: close ``` - --- - -## Methodes -This is the way to allow or block some [Methodes](https://www.w3schools.com/tags/att_method.asp). +## Methods +This is the way to allow or block some [Methods](https://www.w3schools.com/tags/att_method.asp). ```python @app.route('/test_methodes', methods=['GET', 'POST']) def test_methodes(): @@ -186,10 +176,9 @@ def test_methodes(): else: return f"You will never see this message\n" ``` - --- - -You have surely seen some Parametes embedde in liks like *?user=bgfdrt56+pass=Rtrssdf*. Flask alows some asy maniputation of those parameters. To test this : [http://localhost:5000/handle_url_params?greeting=hello&name=kerem](http://localhost:5000/handle_url_params?greeting=hello&name=kerem). If there was a mistmatch you would get the Error message instead. +## Parameters +You have surely seen some Parametes embedded in liks like *?user=bgfdrt56+pass=Rtrssdf*. Flask alows some asy maniputation of those parameters. To test this : [http://localhost:5000/handle_url_params?greeting=hello&name=kerem](http://localhost:5000/handle_url_params?greeting=hello&name=kerem). If there was a mistmatch you would get the Error message instead. ```python #Handle parameters : @app.route('/handle_url_params') @@ -205,7 +194,7 @@ def handle_url_params(): --- # Flask HTML files And [TEMPLATES](https://flask.palletsprojects.com/en/3.0.x/tutorial/templates/) [YouTube](https://youtu.be/w6Ui_DVxluc?si=B2JshnjVMu3DWcBC) -## Indicate that we use Templates +## Declare that we use Templates To indicate that we will use *templates* first we need to create a *directory* containing those tmeplates and second we need to *reference* it in our code. let's create the *directory* ```bash @@ -225,9 +214,7 @@ Let's declare it in our code. *template_folder* take the source of *app.py* as s ```python app = Flask(__name__, template_folder='templates') ``` - --- - ## Using Templates The [`render_template()`](https://flask.palletsprojects.com/en/2.3.x/templating/#) let's use use tem when returning the content of the page. @@ -240,12 +227,9 @@ def index(): return render_template('index.html', value=myval, result=myres, list=mylist) ``` Now this allows us to have a huge flexibility that HTML doesn't otherwise allow. -This is thnaks to [Jinja](https://jinja.palletsprojects.com/en/3.1.x/templates/). -Let's see how it works. - ---- - +This is thnaks to [Jinja](https://jinja.palletsprojects.com/en/3.1.x/templates/). Let's see how it works. ## HTML & Jinja + Now let's have a look to `index.html` herre we can now se some *Jinja* templates funtionlaities. ```HTML @@ -275,7 +259,7 @@ Now let's have a look to `index.html` herre we can now se some *Jinja* templates *Jinja* Allows us to use these kind of functions : `{% for item in list %}` that are ususaly inpossible wiht HTML. All *Jinja* Functions starts with `{%` and ends with `%}`. - They can also be used inline like: `
  • {{ item }}
  • `. - +--- ## Extend Templates We cna extend templates to other templates. What does this bring us ? Let's say we want ta have the *same header* on *multiple pages* we can use a template with the header and only add an *content* *BLOCK* onto this template. In *templeates* directory we can now create a base html file let's call it *base.htm* and two pages that extends on it *index.html* and *other.html*. @@ -357,10 +341,7 @@ but here we need to add a route to this page at *app.py* def other(): return render_template('other.html') ``` - - --- - ## Jinja [Filters](https://jinja.palletsprojects.com/en/3.0.x/templates/#filters) Filters that python uses lin `.uppercase` can't be used in Jinja. There are some filters taht already exists but most importnatly we can create our own filters. @@ -391,9 +372,11 @@ We of course need to define `reverse_string` in *app.py* as follows : def reverse_string(s): return s[::-1] ``` -you can see that we use a speciel decorator named [template_filter](https://tedboy.github.io/flask/generated/generated/flask.Flask.template_filter.html) to declare our custom filters. +you can see that we use a speciel decorator named [template_filter](https://tedboy.github.io/flask/generated/generated/flask.Flask.template_filter.html) to declare our custom filters. + +--- -## Redirection +# Redirection Redirections can be tricky but thankfully Flask has a way od dynamic *redirection* wich takes a *function* as *argument* ```PYTHON @app.route('/redirect_endpoint') @@ -450,3 +433,115 @@ So what is going on here ? - If the *Methode* is *POST* we collect the information filler in *form* sent by pressing *submit* - We get collect the *information* by *referencing* form items by their *name* attribute : `request.form.get('username')` - Then we can put these infiormation in varibales and use them as we please. + +--- + +# Uploading & downloading files +Be careful here are some notions that are not related to Flask or Jinja. Like [Pandas](https://pandas.pydata.org/) for excel manipulation and [os](https://docs.python.org/3/library/os.html) of opperating system functions. Nevertheless this is a very important topic with widely used applications for website. +## Uploading a file +Here is the *File Upload form* located in *index.html* the most important element to know are : +- [enctype="multipart/form-data"](https://www.w3schools.com/tags/att_form_enctype.asp) Used while uploading files +- [accept=](https://www.w3schools.com/tags/att_input_accept.asp)"[media-types](http://www.iana.org/assignments/media-types/media-types.xhtml)" do define which types of files are allowed. +```html +

    File Upload

    +
    +
    + +
    +``` +Wiht the `action="{{ url_for('file_upload') }}"` we go back to our *app.py* funtion *def file_upload():* where we treat this information. Where we do nothing else then to read the file and print his output. +- If it's a *Text* file we put directly it's output +- If it's a *Excel* file we read the file using pandas `df = pd.read_excel(file)` and return the result as html table using `return df.to_html()` +```python +import pandas as pd + +@app.route('/file_upload', methods=['GET', 'POST']) +def file_upload(): + file = request.files.get('file') + if file.content_type == 'text/plain': + return file.read().decode() + elif file.content_type == 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' or file.content_type == 'application/vnd.ms-excel': + df = pd.read_excel(file) + return df.to_html() +``` +--- +## Download a file directly +Here is the *File Upload form* located in *index.html* tnothing mroe to know then the previous example: +```html +

    Convert To CSV

    +
    +
    + +
    +``` +Wiht the `action="{{ url_for('convert_csv') }}"` we go back to our *app.py* funtion *def convert_csv():* where we convert the excel into csv but also directly donwload the ouput. It is a specific example and we don't need to waste too much time on it as there is, in my opignon a much better way to do this. +```python +@app.route('/convert_csv', methods=['GET', 'POST']) +def convert_csv(): + file = request.files.get('file') + df = pd.read_excel(file) + response = Response( + df.to_csv(), + mimetype = 'text/csv', + headers={'Content-Disposition': 'attachement; filename=result.csv'} + ) + return response +``` +--- +## Download a file thourgh a directory +Again nothing special to note here, same a the ones before. +```html +

    Convert To CSV Two

    +
    +
    + +
    +``` +Wiht the `action="{{ url_for('convert_csv_two') }}"` we go back to our *app.py* funtion *def convert_csv_two():*, convert the file to csv and make it redy for download: +- `os.makedirs('downloads')` we create a donwload directory +- `filename = f'{uuid.uuid4()}.cvs'` we give the file a unique name +- `df.to_csv(os.path.join('downloads', filename))`we convert the file into csv and register in *downloads* with his*filename* +- `return render_template('download.html', filename=filename)` we return the *download.html* page with *filename* as argument +```python +@app.route('/convert_csv_two', methods=['GET', 'POST']) +def convert_csv_two(): + file = request.files.get('file') + df = pd.read_excel(file) + + if not os.path.exists('downloads'): + os.makedirs('downloads') + + filename = f'{uuid.uuid4()}.cvs' + df.to_csv(os.path.join('downloads', filename)) + + return render_template('download.html', filename=filename) +``` +for that to work we need to create the *download.html* in *templates* directory: +- `href="{{ url_for('download', filename=filename) }}"`Directs the request to `def download(filename):` function which generates the download proccess +```html +{% extends "base.html" %} + +{% block title %}Download Page{% endblock %} + +{% block content %} +

    Download {{ filename }}

    + Donwload +{% endblock %} +``` +and aso create a new route for *download* in *app.py*: +- `@app.route('/download/'` the route has de name route to */download* but allso the argument ** +- `send_from_directory('downloads', filename, download_name='result.csv')` alows us to *send* the file *downloads* directory and *rename* it at the same time +```python +@app.route('/download/', methods=['GET', 'POST']) +def download(filename): + return send_from_directory('downloads', filename, download_name='result.csv') +``` + +--- + +# JS/json Request for live update +todo if needed. for now i am nto so shure. + +--- + +# Static Files & Integrating Bootstrap diff --git a/flask_to_docker/app.py b/flask_to_docker/app.py index 403bcb5c..5d457963 100644 --- a/flask_to_docker/app.py +++ b/flask_to_docker/app.py @@ -1,4 +1,4 @@ -from flask import Flask, request, make_response, Response, render_template, redirect, url_for, send_from_directory +from flask import Flask, request, make_response, Response, render_template, redirect, url_for, send_from_directory, jsonify import pandas as pd import os import uuid @@ -56,5 +56,18 @@ def convert_csv_two(): def download(filename): return send_from_directory('downloads', filename, download_name='result.csv') + + +@app.route('/handle_post', methods=['GET', 'POST']) +def handle_post(): + greeting = request.json.get('greeting') + name = request.json.get('name') + + with open('file.txt','w') as f: + f.write(f'{greeting}, {name}') + + return jsonify({'message': 'Successfully written!'}) + + if __name__ == '__main__': app.run(host='0.0.0.0', port=5000, debug=True) diff --git a/flask_to_docker/file.txt b/flask_to_docker/file.txt new file mode 100644 index 00000000..25b67e93 --- /dev/null +++ b/flask_to_docker/file.txt @@ -0,0 +1 @@ +Hello, Mike \ No newline at end of file diff --git a/flask_to_docker/templates/index.html b/flask_to_docker/templates/index.html index 25d122a4..578a531e 100644 --- a/flask_to_docker/templates/index.html +++ b/flask_to_docker/templates/index.html @@ -27,5 +27,27 @@
    + +

    JS JSON request

    + + {% endblock %}