1

I created an app with flask but I'm having a problem with multi-user access bcs of the global variables. Whenever I run the script (it might take minutes) and someone else is trying to access the page and run a script there too a collision appear and data by both users mix.

I truly understand that I should be using OOP in this case, but I'm not able to replicate the OOP usage for this app (yes, I'm an OOP beginner ). Can someone please give me a hint or a little bit of code about OOP for this case?

Much appreciated.

Python:

from flask import Flask, render_template, url_for, request, redirect, Response
from datetime import datetime
import time
import pandas as pd
import re
import os


app = Flask(__name__)


@app.route('/', methods=['POST', 'GET'])
def index():

    if request.method == 'POST':
        start_date = request.form["Start_date"]
        end_date = request.form["End_date"]
        dates = pd.period_range(start=start_date, end=end_date, freq='D')
        global s_date
        global f_date
        s_date = dates[0]
        f_date = dates[-1]
        print(s_date, f_date)
        query = request.form["query"].strip()
        splitted = query.split("\r\n")

        global fiii
        fiii = pd.DataFrame()

        for x in splitted:
            print(x)
            for date in dates:
                print(date)

                directory = '/home/USER/Parquets/{}/{:02d}/{:02d}/'.format(date.year, date.month, date.day)
                for filename in os.listdir(directory):
                    if filename.endswith(".parquet"):
                        df = pd.read_parquet(directory)
                        df.set_index("query", inplace=True)


                        if request.form.get('lowercase') == "on":
                            df.index = df.index.str.casefold()
                        if request.form.get('sensitive') == "on":

                            fiii = fiii.append(df.filter(regex=re.compile(x), axis=0))
                        else:

                            fiii = fiii.append(df.filter(regex=re.compile(x, re.IGNORECASE), axis=0))

        fiii = fiii.groupby(['query'])["total"].sum().sort_values(ascending=False)

        if request.form.get('csv') == "on":
            return redirect("/getCSV")
        else:
            pass
        # return render_template("index.html")
        return fiii.to_frame().to_html(table_id="table")
    else:
        return render_template("index.html")



@app.route("/getCSV")
def getPlotCSV():


    return Response(
        fiii.to_csv(encoding="UTF-8", sep="\t", header=True),
        mimetype="text/csv",
        headers={"Content-disposition":
                 f"attachment; filename={s_date}-{f_date}.csv"})


if __name__ == "__main__":
    app.run(debug=True,port=4444)

HTML:

<html lang="en"><head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/mate                                                                                                                                       rialize/1.0.0/css/materialize.min.css">
    <script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js                                                                                                                                       /materialize.min.js"></script>
    <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel                                                                                                                                       ="stylesheet">

    </head>
    <body>
        <div class="container">
            <div class="row">
                <form class="col s6 offset-s3" action="/" method="POST">
                    <div class="valign-wrapper col s12">
                        <h3 class="col s6">Query master</h3>
                        <div class="right-align input-field col s6">
                            <button class="btn btn-large btn-floating waves-                                                                                                                                       effect waves-light" type="submit" name="action">
                                <i class="material-icons right">send</i>
                            </button>
                        </div>
                    </div>

                    <div class="input-field col s12">
                        <input type="text" id="date-start" class="datepicker                                                                                                                                       " name="Start_date">
                        <label for="date-start">Start Date</label>
                    </div>

                    <div class="input-field col s12">
                        <input type="text" id="date-end" class="datepicker"                                                                                                                                        name="End_date">
                        <label for="date-end">End Date</label>
                    </div>

                    <label class="input-field col s12">
                        <input type="checkbox" name="lowercase" />
                        <span>Lowercase queries</span>
                    </label>

                    <label class="input-field col s12">
                        <input type="checkbox" name="sensitive" />
                        <span>Case sensitive</span>
                    </label>

                    <label class="input-field col s12">
                        <input type="checkbox" name="csv" />
                        <span>CSV export (Funkční pouze csv export)</span>
                    </label>

                    <div class="input-field col s12">
                        <textarea id="textarea1" name="query" class="materia                                                                                                                                       lize-textarea"></textarea>
                        <label for="textarea1">Queries (RegEx supported)</la                                                                                                                                       bel>
                    </div>

                </form>

            </div>

            <script>
                document.addEventListener('DOMContentLoaded', function() {
                    var elems = document.querySelectorAll('.datepicker');
                    var instances = M.Datepicker.init(elems, {
                        format: 'm/d/yyyy',
                    });
                });
            </script>
        </div>
    </body>
</html>

Leemosh
  • 883
  • 6
  • 19

1 Answers1

1

Your problem has absolutely nothing to do with OO - it's only the result of using gobal variables.

Your flask server process will serve all incoming requests, one after the other. So what happens in a multi-user scenario is:

1/ user A posts to index. This assigns values to your globals s_date, f_date and fills.

2/ user A is redirected to getCSV

3/ in the meantime, user B posts to index. This rebinds your globals s_date, f_date and fills to new values

4/ user A's browser has followed the redirection, and renders the fill of user B.

The solution is quite simple: do NOT use global state for per-user data persistance - you want either a user session for short-lived session data and/or a RDBMS for long-lived user data.

bruno desthuilliers
  • 75,974
  • 6
  • 88
  • 118
  • Hi, yeah, I understood that problem completely, but my problem with session is that I'm not sure if I can store dataframe in session as big as 1GB for example. – Leemosh Jan 28 '20 at 13:30
  • And NO, you can't expect to store gigabytes dataframes in sessions. You can just barely expect to load them in memory at all, actually (1GB * x concurrent users == ??? Did you do the math ?). That's why we use databases of one sort or another... I mean _real_ databases (database server processes, often running on a distinct node). – bruno desthuilliers Jan 28 '20 at 13:42