Django e matplotlib plots
« il: Novembre 12, 2018, 11:38 »
Salve a tutti, avrei bisogno di un chiarimento.
Sto studiando come usare django, e per farlo provo a creare delle semplici webapp (studiando ovviamente la parte corrispondente).
L'obiettivo era creare una webapp che mostra dei plot (ho un piccolo script python che da una tabella (dato un nome scelto dall'utente) seleziona dei dati e crea un plot).

La documentazione non sembra tanta a riguardo, per esempio su matplotlib l'indice mi illude perchè è presente una voce matplotlib with django, ma dice:
Citazione
Matplotlib with django
TODO;
:confused:
(in "Matplotlib in a web application server" c'è questo che mi ha risolto un errore, ma nulla di più:
# do this before importing pylab or pyplot
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
)

Nella mia prima versione ho pensato la cosa più semplice fosse salvare il file prima di mostrarlo, quindi:

Template "inserisci nome" ->
Modulo Django: cancella se presente il vecchio plot.png, crea nuovo plot salvalo come plot.png ->
View e Template mostrano la pagina con il plot.


#forms.py
from django import forms

class ListForm(forms.Form):
    your_name = forms.CharField(label='Insert gene name', widget=forms.Textarea)


#models.py
import pandas as pd
import matplotlib
matplotlib.use('Agg')
import seaborn as sns  # sì, lo so, non uso matplotlib ma seaborn :)
import matplotlib.pyplot as plt
import numpy as np
import re
import io
import os

def lineplot(name):   
    try:
        os.remove("pages/static/pages/plot.png")
    except FileNotFoundError:
        pass
    df=pd.read_csv("pages/static/pages/BMDM/BMDM.csv",sep=",",index_col=0)
    sample=processing(df,name)  # Questa funzione seleziona i dati e ritorna un df.

    if sample is None:
        return "Not found"

    f, ax = plt.subplots(figsize=(15, 6))
    sns.despine(ax=ax)
    ax = sns.pointplot(x=sample["Time"], y=sample["value"], data=sample, hue=sample["gene"], estimator=np.median) 
 
    return f


#views.py
from django.shortcuts import render
from .forms import ListForm
from .models import lineplot

def insert_gene_name(request):
    listform_in_view = ListForm()
    return render(request, 'insert_name_plot.html', {'form_in_html': listform_in_view})

def plotView(request):
    data = ListForm(request.POST)
    if data.is_valid():
        text = data.cleaned_data['your_name']
        if "\r\n" in text:
            text=text.split("\r\n")

    f=lineplot(text)
    try:
        f.savefig("pages/static/pages/plot.png")
        return render(request, 'plot.html')
    except AttributeError:
        return render(request, 'plot.html', {"text":"Gene not found"})


#urls.py
from django.urls import path
from .views import insert_gene_name, plotView

urlpatterns = [
    path('insert_name_plot/', insert_gene_name, name='Insert Name'),
    path('plot/', plotView, name='Plot'),
]


#Template - insert_name_plot.html
{% extends "base.html" %}

{% block content %}
    <form action="/plot/" method="post">
        {% csrf_token %}
        {{ form_in_html }}
        <input type="submit" value="Submit">
    </form>
{% endblock %}


#Template - plot.html

{% extends "base.html" %}

{% block content %}
    <h2>{{text}}</h2>
    {% load static %}
    <img src="{% static "pages/plot.png" %}" alt="image">
{% endblock %}


(spero di aver inserito i codici in un ordine comprensibile)
La cosa sembra funzionare, mi starebbe pure bene.
Però mi sono chiesto: questo continuo creare/cancellare file è efficiente? Direi di no...non ho nessuna esigenza di salvare il file, mi basterebbe tenerlo in memoria e mostrarlo (Prima domanda: mi sbaglio???)

Mi pare di capire che per farlo devo caricarlo nel buffer (seconda domanda, è il modo migliore per farlo?). Funziona con HttpResponse:

#models.py
    #[...]
    ax = sns.pointplot(x=sample["Time"], y=sample["value"], data=sample, hue=sample["gene"], estimator=np.median)

    buf = io.BytesIO()
    plt.savefig(buf, format='png')
    plt.close(f)
    data=buf.getvalue()
   
    return data



#views.py
def plotView(request):
    data = ListForm(request.POST)
    if data.is_valid():
        text = data.cleaned_data['your_name']
        if "\r\n" in text:
            text=text.split("\r\n")

    data=lineplot(text)
    return HttpResponse(data, content_type='image/png')


Se cerco di fare il render con la pagina però non ci riesco
 

#views.py
#[...]
    data=lineplot(text)
#    return HttpResponse(data, content_type='image/png')
    return render(request, 'plot.html', {"img":data})


#Template - plot.html
{% extends "base.html" %}

{% block content %}
    <h2>{{text}}</h2>
    <img src="{{img}}" alt="image">
{% endblock %}


Mi apre la pagina plot.html, ma l'immagine non c'è (nel template ho provato sia "{{img}}" che {{img}})
terza domanda: Il punto è che non riesco a capire se ho scritto male il view.py o il template plot.html. E non riesco a trovare niente nella documentazione o forums.
:confused:

Scusate il post lungo...