Search code examples
djangodjango-templatesdjango-viewsdata-visualizationbokeh

Django and Bokeh: How do I add my graph to a Class Detail View?


I can't seem to figure out how to add my graph to a Class Detail View? Is it not possible to do so? I add it to the detailView, and call it in my template with:

 {{ div | safe }}

But it does not show? I've gotten it to work perfectly fine in a view and template separately.

Here's the whole detailview I'm trying to implement it into.

DetailView

class MedarbejderDetailView(FormMixin, DetailView):
template_name = 'evalsys/medarbejder/detail.html'
model = Medarbejder
form_class = OpretEvalForm

def evalgraph(self):
    colors = ["#40e862", "#ff9d26", "#ff1424"]
    over = 0
    møder = 0
    under = 0
    none = 0
    counts = []
    items = ["Overstiger forventning", "Møder forventning", "Under forventning", "Ingen bedømmelse"]
    eval_vudering = Evaluering.objects.values("vuderingsnavn__vuderingsnavn")
    source = ColumnDataSource(data=dict(items=items, counts=counts))

    for i in eval_vudering:
        if "Overstiger forventning" in i.values():
            over += 1
        elif "Møder forventning" in i.values():
            møder += 1
        elif "Under forventning" in i.values():
            under += 1
        elif None in i.values():
            none += 1
    counts.extend([over, møder, under, none])

    plot = figure(x_range=items, plot_height=500, plot_width=500, title="Opsumering af evalueringer",
                  toolbar_location=None, tools="pan, wheel_zoom, box_zoom, reset, tap", tooltips="@items: @counts")
    plot.title.text_font_size = "20pt"
    plot.vbar(x="items", top="counts", width=0.9, source=source, legend="items", line_color='black',
              fill_color=factor_cmap("items", palette=colors, factors=items))
    plot.legend.label_text_font_size = "13pt"
    script, div = components(plot)
    return render(self, 'evalsys/medarbejder/detail.html', {'script': script, 'div': div})

def view_medarbejder_with_pk(self, pk=None):
    if pk:
        medarbejder = Medarbejder.objects.get(pk=pk)
    else:
        medarbejder = self.medarbejder
    args = {'medarbejder': medarbejder}
    return render(self, 'evalsys/medarbejder/detail.html', args)

def get_context_data(self, **kwargs):
    context = super(MedarbejderDetailView, self).get_context_data(**kwargs)
    context['eval_list'] = Evaluering.objects.all()
    context['fag_list'] = Fag.objects.all()
    context['ma'] = Medarbejder.objects.get(pk=self.kwargs.get('pk'))
    context['instruktør'] = User.objects.get(username=self.request.user)
    return context

def post(self, request, pk):
    self.object = self.get_object()
    form = self.get_form()
    if form.is_valid():
        print(form.cleaned_data)
        instance = form.save(commit=False)
        instance.instruktør = request.user
        instance.ma = self.object
        return self.form_valid(form)
    else:
        return self.form_invalid(form)

def form_valid(self, form):
    item = form.save()
    self.pk = item.pk
    return super(MedarbejderDetailView, self).form_valid(form)

def form_invalid(self, form):
    return super(MedarbejderDetailView, self).form_invalid(form)

def get_success_url(self):
    return reverse_lazy("evalsys:view_evaluering_with_pk", kwargs={'pk': self.pk})

URLs

path('se_alle_evalueringer/<int:pk>', views.MedarbejderEvalDetailView.as_view(), name="view_evaluering_with_fag"),

I know I'm calling the function "view_evaluering_with_fag", so it is because I'm not calling my Bokeh function "evalgraph"?


Solution

  • Didn't know folks couldn't use this as a way to ask follow up questions when we're researching the same question.

    Anyhow, I am so about to make your day! With A LOT of trial and error (I've only been coding with Python and Django for a month with no real coding background except Java back in 1999) I got bokeh to render from detailview. It appears the trick is to get stuff under the get_context_data function. I don't know how I came to this conclusion, but I figured the script and div context weren't making their way to the render so I was trying to get them into context. As you'll see below, I put script and div as context['script']=script and context['div']=div. My situation looks a bit simpler, I'm just parsing a bike data file and plotting the data, but hopefully this gets you on your way if you're still trying to make this work.

    class FitFileDetailView(DetailView):
        model = FitFiles
    
        def get_context_data(self, **kwargs):
            model = FitFiles
            ff = FitFiles.objects.get(pk=self.kwargs.get('pk'))
    
            ffile = ff.fitfiles.path
            fitfile2 = FitFile(ffile)
            while True:
                try:
                    fitfile2.messages
                    break
                except KeyError:
                    continue
    
            workout2 = []
            for record in fitfile2.get_messages('record'):
                r2 = {}
        # Go through all the data entries in this record
                for record_data in record:
                    r2[record_data.name] = record_data.value
    
                workout2.append(r2)
    
            df2 = pd.DataFrame(workout2)
            df2['time']=(df2['timestamp'] - df2['timestamp'].iloc[0]).astype('timedelta64[s]')
    
        #Bokeh code
    
            df2 = pd.DataFrame(workout2)
            df2['time']=(df2['timestamp'] - df2['timestamp'].iloc[0]).astype('timedelta64[s]')
            p2 = figure(x_axis_label='time', y_axis_label='watts', tools="", plot_width=1000, plot_height=500)
            p2.line(df2['time'], df2['power'])
            p2.line(df2['time'], df2['heart_rate'], color='red')
            script, div = components(p2)
            context = super(FitFileDetailView, self).get_context_data(**kwargs)
            context['script']=script
            context['div']=div
            return context