Reputation: 73
I have an app with following architecture:
/app
is where all code is stored, /reports
- for reports generated via ydata_profiling
.
The idea is to show user some wait-page with "pls wait" on it, while pinging server once a second whether report is ready or not, and if so - show report in and allow user to download it / open in new webpage.
My current problem is that passing app object, where app_context
is stored, doesn't resolve the problem with RuntimeError: Working outside of request context.
in line session_data = session.get('sid')
succeeding generation of report.
What and how should i pass to generate_statistics_report()
in order to remove this error?
Below is the code:
# app.py
from app import create_app
app = create_app()
if __name__ == "__main__":
app.run(debug=True)
# app/__init__.py
from flask import Flask
def create_app():
app = Flask(__name__)
app.secret_key = 'test'
from .api import statistics
app.register_blueprint(statistics.bp)
return app
# app/api/__init__.py
from flask import Blueprint
from . import statistics
bp = Blueprint('api', __name__, url_prefix='/api/v1/')
bp.register_blueprint(statistics.bp)
# app/api/statistics/__init__.py
from flask import Blueprint
from .routes import statistics, check_report_status, download_report
bp = Blueprint('statistics', __name__, url_prefix='/statistics')
bp.add_url_rule('/', 'statistics', statistics)
bp.add_url_rule('/check_report_status', 'check_report_status', check_report_status)
bp.add_url_rule('/download_report', 'download_report', download_report)
# app/api/statistics/routes.py
import os
import threading
from datetime import datetime
import pandas as pd
import ydata_profiling
from flask import render_template, session, jsonify, send_from_directory, url_for, current_app
BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
def generate_unique_filename() -> str:
timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
unique_filename = f"report_{timestamp}.html"
return unique_filename
def generate_statistics_report(session_id: str, app) -> None:
with app.app_context():
data = {
'Name': ['Alice', 'Bob', 'Charlie', 'David', 'Eva'],
'Age': [24, 27, 22, 32, 29],
'City': ['New York', 'Los Angeles', 'Chicago', 'Houston', 'Phoenix'],
'Score': [85, 90, 78, 88, 92]
}
merged_df = pd.DataFrame(data)
profile = ydata_profiling.ProfileReport(merged_df)
report_filename = generate_unique_filename()
report_path = os.path.join(BASE_DIR, 'reports', report_filename)
profile.to_file(report_path)
session_data = session.get('sid')
session_data['report_ready'] = True
session_data['report_filename'] = report_filename
session[session_id] = session_data
def statistics():
session['sid'] = session.get('sid', os.urandom(16).hex())
session_id = session.get('sid')
session[session_id] = {
'report_ready': False,
'report_filename': None,
}
thread = threading.Thread(target=generate_statistics_report,
args=(session_id,
current_app._get_current_object(), ))
thread.start()
return render_template('statistics.html')
def check_report_status():
session_id = session.get('sid')
session_data = session.get(session_id, {})
if session_data.get('report_ready'):
return jsonify({'status': 'ready', 'url': session_data.get('report_filename')})
return jsonify({'status': 'processing'})
def download_report():
session_id = session.get('sid')
session_data = session.get(session_id, {})
report_filename = session_data.get('report_filename')
if report_filename:
return send_from_directory(os.path.join(BASE_DIR, 'reports'), report_filename, as_attachment=True)
return jsonify({'status': 'report_not_ready'})
<!-- /app/templates/statistics.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<title>Statistics</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script>
$(document).ready(function () {
function checkReportStatus() {
$.getJSON('{{ url_for("statistics.check_report_status") }}', function (data) {
if (data.status === 'ready') {
$('#loadingMessage').hide();
$('#reportFrame').attr('src', data.url);
$('#reportFrame').show();
$('#downloadButton').attr('href', data.url);
$('#downloadButton').show();
$('#downloadReportButton').show(); // Show the download button
} else {
setTimeout(checkReportStatus, 1000); // Check every second
}
});
}
checkReportStatus();
});
</script>
</head>
<body>
<div class="container mt-5">
<h1>Statistics</h1>
<p>Statistics description</p>
<div id="loadingMessage" class="text-center">
<div class="spinner-border" role="status">
<span class="sr-only">Loading...</span>
</div>
<p>Generating report, please wait...</p>
</div>
<!-- Download Button, initially hidden -->
<a href="#" id="downloadButton" target="_blank" class="btn btn-primary my-3"
style="display:none;">Open in new tab</a>
<a href="{{ url_for('statistics.download_report') }}" id="downloadReportButton" class="btn btn-secondary my-3" style="display:none;"
download>Download as file</a>
<iframe id="reportFrame" style="width: 100%; height: 800px; display:none;"></iframe>
</div>
</body>
</html>
Upvotes: 0
Views: 23