Creating HTML wrapper in django for matplotlib images - html

I would like to take python generated matplotlib images and embed them into an HTML page that is generated by django. I am relatively new to django and have been struggling to get this to work. I can successfully generate a matplotlib image alone on a webpage but have been unable to embed into an HTML page. Django makes sense as my application will have many users that will have custom views with different data and frequently changing data coming from a database. I would like to avoid creating many static files.
I have looked at several posts but I am clearly missing something. For example:
Generating dynamic charts with Matplotlib in Django, images on django site from matplotlib and Dynamically serving a matplotlib image to the web using python.
I generate my matplotlib image view with temp and I think the wrapper is detail. detail does not seem to work. The filename plotdata.py and under the django tutorial example polls
from datetime import datetime, time
from django.http import HttpResponse
from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
from matplotlib.figure import Figure
from django.contrib.auth.decorators import login_required
from django.shortcuts import render
#login_required()
def temp(request,x_id):
#... code to generate fig for ploting - works well
#This works but does not seem to pass file to HTML
canvas = FigureCanvas(fig)
response = HttpResponse(content_type='image/png')
canvas.print_png(response)
return response
#login_required()
def detail(request, x_id):
render(request, 'polls/plotdata.html', {'x_id': x_id})
My urls.py is as follows. temp works fine
from django.conf.urls import patterns, url
from django.views.generic import DetailView, ListView
from polls.models import Poll
from polls import plotdata
urlpatterns = patterns('',
#polls url chopped out for brevity - follows tutorial
url(r'^(?P<x_id>\d+)/plotdata/temp.png$', plotdata.temp, name='temp'),
url(r'^(?P<x_id>\d+)/plotdata/detail$', plotdata.detail, name='detail'),
)
My plotdata.html is as follows
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="style.css" />
<title>{% block title %}Plotting Template{% endblock %}</title>
</head>
<body>
<div id="content">
{% block content %}
<img src="{% url 'temp' x_id %}" >
{% endblock %}
</div>
</body>
</html>
The error generated is as follows.
NoReverseMatch at /polls/1303070002/plotdata/detail
Reverse for 'temp' with arguments '('1303070002',)' and keyword arguments '{}' not found.
This probably not the only problem with the above. I am certain I have missed something critical.
I tried hardcoding, as a test, to
<img src="/polls/1303070002/plotdata/temp.png" >
but it generated the following error
ValueError at /polls/1303070002/plotdata/detail
The view polls.plotdata.detail didn't return an HttpResponse object.
I would like to get this framework working so I can put text and buttons around the data plot. I am open to other ways to more efficiently create a solution. Thank you very much for helping out!
Repaired code, plotadata.py, is as follows
#same header information from above before this line
canvas = FigureCanvas(fig) #This needs to remain for savefig or print_png command
response = HttpResponse(content_type='image/png')
fig.savefig(response, format='png') #Produces all white background
return response
def detail(request, salesorder_id):
return render(request, 'rsa/plotdata.html', {'x_id':x_id})
I included return before render this time... Changed path to hmtl file to avoid confusion with polls app. Using savefig versus print_png as it formats more generically. urls.py is the same. I am trying to get plotdata.html to work as above, passing a variable to url via {{ x_id }} but I am missing something. Same error as above, NoReverseMatch. If I replace, in plotdata.html
<img src="{% url 'temp' x_id %}" >
with
{% load staticfiles %}
<img src="{% static '/rsa/1303070001/plotdata/temp.png' %}" >
the image is embedded as desired. Now adding a dynamic path such as
<img src="{% static '/rsa/{{ x_id }}/plotdata/temp.png' %}" >
just escapes the literal x_id => /rsa/%7B%7B%20x_id%20%7D%7D/plotdata/temp.png. Trying x_id|safe ends up escaping the pipe and including literal safe... %7B%7B%20x_id%7Csafe%20%7D%7D. Hence I am trying to go back to using url versus static. Seems cleaner. I think there is something wrong with the variable I am passing, x_id

Related

Creating a alert functionality in django wagtail

I am trying to create a alert section for a menu (similar to the menu on this page https://metageeky.github.io/mega-menu/responsive-header.html)
*Each alert should have an effective date (date alert is “posted” live) and resolved date (date alert is “removed” live). Each alert will also have a maximum of one to two sentences of text describing situation.
The number of active/current alerts will appear in parenthesis following the icon and ALERT link text.
The icon and text are Dark Orange. When you hover over the icon and text, an underline appears.
When users click on the link, they are taken to a page that lists all active alerts. At bottom of page, message displays “If you are experiencing an issue, please contact us at....”
If there are no Alerts:
The number of alerts in parenthesis following the icon and link text will not appear.
Both the icon and alert text will be Primary Blue.
When Users click on the link, they are taken to a secondary alerts page that displays a message that says “There are currently no active alerts. If you are experiencing an issue, please contact us at...”
How would i achieve this?
Thank you.
There is a lot to unpack in your question but here is a high level approach.
1. Define your model
Read the Django docs on how to create a Model
Read the Django docs on what types of Fields exist
In your models.py, you will need to create a new model that has all the data you need for your requirements.
from django.db import models
class Alert(models.Model):
title = models.CharField()
description = models.TextField()
date_from = models.DateTimeField()
date_to = models.DateTimeField()
2. Ensure you can edit/manage your model data
Now you need to provide a way for your admin users to access the data model, edit & create items.
Wagtail has a great Snippets feature that allows this to work without too many changes, you will need to add #register_snippet on your model and also define some panels.
from wagtail.admin.edit_handlers import FieldPanel
from wagtail.snippets.models import register_snippet
from django.db import models
#register_snippet
class Alert(models.Model):
#... fields (defined above)
panels = [
FieldPanel('title'),
FieldPanel('description'),
FieldPanel('date_from'),
FieldPanel('date_to'),
]
def __str__(self):
return self.title
3. Prepare a template tag to show the queried data
Now you will need to work out how to query the model in a way that it will return the alerts based on your requirements (current date should be within the date range of the data).
Django has docs on writing queries
The simplest way to get the results of this query into the template will be with a custom Template Tag
An inclusion_tag is a way to have a small template fragment that can be used anywhere with custom data (without having to pass it into each View).
In the example below, you will still need to create the template file current_alerts.html which will contain how you want to render the alerts.
In your template tag template you can also use the page_url tag to provide a link to the alerts_page
# template_tags/custom_tags.py
# remember to create a template_tags/__init__.py file also
from django import template
from .models import Alert
register = template.Library()
#register.inclusion_tag('current_alerts.html')
def show_alerts():
# just returns all alerts, but this query can be refined to suit what you need
current_alerts = alerts.Objects.all()
alerts_page = AlertPage.Objects.all().first() # this assumes there will only ever be one
return {'alerts_page',alerts_page,'current_alerts': current_alerts}
4. Use your template tag & add styling
Now you need to include the tag at the top of the page inside your root/shared template.
{% extends "base.html" %}
{% load custom_tags %}
{% block body_class %}template-blogpage{% endblock %}
​{% show_alerts %}
{% block content %}...{% endblock %}
5. Create a AlertsPage
You will need to create a new Page type to redirect users to within your alerts link.
https://docs.wagtail.io/en/stable/topics/pages.html
This Page can be anywhere in your tree and the Page's view template can also use the same shared template or you can pass the alerts to the view via the template context

Flask set local html as the src of the iframe

I am trying to understand how to render an html inside another html using iframe and flask
I have tried using the<iframe src="{% include 'status.html' %}" width="100%" height="100%" style="outline:none; border:none; top:5%;"></iframe> way however the server is having a weird Not Found error "GET /%3C!DOCTYPE%20html%3E%3Chtml%20lang= HTTP/1.1" 404
I have also tried using another method, as well <iframe src="{{ status }}" width="100%" height="100%" style="outline:none; border:none; top:5%;"></iframe> and in the flask
status_render = os.path.join(app.root_path, 'templates', 'status.html')
#app.route("/")
def main():
return render_template(
"index.html",
status=status_render
)
Using the second method I am getting this error "GET /home/dimitris/Documents/SmartHome/MobileApp/templates/status.html HTTP/1.1" 404 despite of the fact that the status.html does exist on the above path
I read online that it should be a a separate request which means another flask route, however I am not really sure that I am understanding how this should be implemented.
For security reasons, html files can only be rendered by calls from within the function of the flask application.
Include this code within your .py application:
#app.route('/show_status')
def show():
return render_template('status.html')
In your html file, render the file as follows:
<iframe src="{{ url_for('show_frame') }}"></iframe>
There is no need to pass arguments to the html index in the main () function:
#app.route("/")
def main():
return render_template("index.html")
Funcionou para mim.
Espero ter ajudado.
I am not sure but there is some problem is displaying any kind of static or predefined urls of html/image in flask.
In order to achieve the desired output, follow the below steps:
In the <iframe> tag, give a value that will be used as a route in app.py. For example, let say you need to display iframe in the index.html. Then create iframe tag in index.html as:
<iframe src="/statusRoute" height="200", width="500">
Now create a route with the same name in the app.py as:
#app.route('/statusRoute')
def showStatus():
  return flask.render_template('status.html')
Ensure that the status.html is present inside the templates folder.
This would render the status.html inside an iframe of the index.html page.

Django Nested Views

I'm developing an internal application and I would like to be able to nest my views to keep everything nice and organized. I plan on doing this by keeping different parts of the page in their own HTML files with their own Views (separate sidebar and navbar, separate charts, etc).
views.py
from django.shortcuts import render
from django.views.generic import TemplateView
import Recall.data_logger.models as DLM
class ReportHome(TemplateView):
template_name = 'data_logger/index.html'
class SelectorSidebar(TemplateView):
template_name = 'data_logger/sidebar.html'
def get(self, request, *args, **kwargs):
companies = DLM.Company.objects.order_by('company_name').all()
return render(request, self.template_name, {'companies':companies,})
index.html
<html>
<head></head>
<body data-gr-c-s-loaded="true">
{% include 'data_logger/navbar.html' %}
<div class="container-fluid">
<div class="row">
{% include 'data_logger/sidebar.html' %} <!-- This is the part I need help with-->
</div>
</div>
</body>
</html>
sidebar.html
<div class="col-sm-3 col-md-1 sidebar">
<ul class="nav nav-sidebar">
{% for company in companies %}
<li>{{ company.company_name }}</li>
{% endfor %}
</ul>
</div>
I understand that by just using {% include 'data_logger/sidebar.html' %} it's just loading the HTML and bypassing SelectorSidebar, how do I direct it through the View?
I'd like a solution that allows me to access anything from a simple list of names to relitively large datasets being fed into a D3 chart.
Solution
This is what I ended up using:
index.html
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"
integrity="sha384-vk5WoKIaW/vJyUAd9n/wmopsmNhiy+L2Z+SBxGYnUkunIxVxAv/UtMOhba/xskxh"
crossorigin="anonymous"></script>
<script>
$.get("_sidebar", function(data, status){
$("#_sidebar").html(data);
});
</script>
</head>
<body data-gr-c-s-loaded="true">
{% include 'data_logger/navbar.html' %}
<div class="container-fluid">
<div class="row" id="_sidebar"></div>
</div>
</body>
</html>
Where _sidebar is the URL to SelectorSidebar:
urlpatterns = [
path('', v.ReportHome.as_view(), name='ReportHome'),
path('_sidebar', v.SelectorSidebar.as_view(), name='SelectorSidebar'),
]
I think you are making some confusion on how Django templates and views work together.
In very simple terms a Django template is what defines the HTML code that makes up a page. You can keep your templates very modular and organized; to do this you can use the include template tag or you can use template inheritance, which is a very powerful way to have "modular" templates.
A Django view is basically a function (or a class of you are using class based views) that receive an HTTP request and build an HTTP response.
It doesn't make much sense to have "nested" views because usually you have just one HTTP request and you want to build just a response with the HTML needed to display the page.
So I think that you can happily use Django templates to put together all the modules that make up your page (header, sidebar, etc.), but each page should correspond to a single Django view.
Another approach could use AJAX and Javascript to make different HTTP requests and build up the page client-side, but I think that this is not the approach you are considering here.
As #baxeico answered, you can't have multiple views to serve a page, because one HTTP request is one view.
If you have content that needs to appear on a lot of pages, like your sidebar, and that content also requires some context information to render (like a list of companies to fetch from the db), you have two options:
If the stuff required to add to the sidebar is fairly limited, create a template context processor that you add to the list of context processors in your settings (TEMPLATES setting).
def sidebar_context(request):
return {'companies': DLM.Company.objects.order_by('company_name').all()}
and in your settings, you'd add something like 'myapp.custom_contexts.sidebar_context' at the top of the list.
Now, every single template has access to the context variable companies, including your sidebar template.
If the stuff shown in the sidebar is more dynamic, or more complex, you should consider fetching the data from within the browser using AJAX. You would create a view that returns JSON instead of HTML and in your sidebar template add javascript to fetch the data and populate the sidebar.
The view is as simple as your current one:
def sidebar(request):
return JsonResponse({'companies': Company.objects.all().values('name', 'id')})
which will return a list of dicts containing name and id of each company. In your AJAX handler for the successful response (which receives the data), you can then loop through data and access data[i].name and data[i].id which you can use to populate your list.
I won't go as far as posting the full javascript (please search for jQuery, ajax and django) but here's a bit to give you an idea, assuming jQuery:
$(window).on('load', function() {
$.ajax({
url: "{% url 'sidebar' %}", // assuming this is inside a template, if not {% url %} won't work and you'll have to get it in a different way
success: function(data) {
if (data.length > 0) {
for (var i=0; i<data.length; i++) {
var elem = $("<li>" + data[i].name + "</li>")
$("#companies").append(elem)
}
}
})
})

How to customize ckan header site navigation tabs

I would like to add extra header site navigation tabs to the default ones.
I have tried working with the solution given here but it is not working for me. I am getting Exception: menu itemapicannot be found error
This is my plugin.py code
import ckan.plugins as plugins
import ckan.plugins.toolkit as toolkit
class ApiPlugin(plugins.SingletonPlugin, toolkit.DefaultDatasetForm):
plugins.implements(plugins.IRoutes, inherit=True)
def before_map(self, m):
m.connect('api', #name of path route
'/api', #url to map path to
controller='ckanext.kimetrica_theme.controller:ApiController', #controller
action='api') #controller action (method)
return m
This is my header.html code
{% ckan_extends %}
{% block header_site_navigation_tabs %}
{{ h.build_nav_main(
('search', _('Datasets')),
('organizations_index', _('Organizations')),
('group_index', _('Groups')),
('about', _('About')),
('api', _('api'))
) }}
{% endblock %}
And this is my controller.py code
import ckan.plugins as p
from ckan.lib.base import BaseController
class ApiController(BaseController):
def api(self):
return p.toolkit.render('api.html')
I expect to have the api menu work like the rest of the menu do. I also have my template(api.html) in place
Based on what you posted it looks like you haven't setup plugins.implements(plugins.IConfigurer, inherit=True) to register your new template. Try referencing this extension as an example. https://github.com/ckan/ckan/blob/2.8/ckanext/stats/plugin.py for setting up a new page.
You're on the right track for the menu.
Also what version of CKAN are you using? You may want to pswitch this to a flask blueprint. Like this https://github.com/ckan/ckan/blob/2.8/ckanext/example_flask_iblueprint/plugin.py
If you are using 2.9 (in alphha) check this issue out and the comments ckan 2.9.0 iroute before_map not invoking custom controller
I solved this question by using ckanext-pages extension This extension allows you to add simple static pages and blogs and edit their contents.
I solved it by creating a new HTML file for the header, e.g. header_foo.html. Additionally, you have to change the page.html:
…
{%- block header %}
{% include "header_foo.html" %}
{% endblock -%}
…
In the same way, you can hide the navigation tabs.

Django more than one ListView to an html page

I am looking to be able to access both post_list10, and photo_list on my blog.html however I am only able to access one when it is written like this. Does anyone know how to write it so I can access both?
post_list10 = Post.objects.all().order_by("-date")[:10]
photo_list = Photo.objects.all()
urlpatterns = patterns('',
url(r'^$', ListView.as_view(
queryset=post_list10,
template_name="blog.html")),
# FOR SOME REASON WHICHEVER COMES FIRST TAKES PRECEDENT. Cannot do both.
url(r'^$', ListView.as_view(
queryset=photo_list,
template_name="blog.html")))
Also,
when calling the list in my blog.html I can use
{% for post in object_list %}
.....
{% endfor %}
in order to work with the available post objects but how do I know if I'm working with posts or photos if I can access both? Sorry if my wording is unclear, I'm quite new to this.
The way to solve your problem would usually involve writing your own view.
A simple way of doing this is as follows:
in urls.py:
from views import my_view
urlpatterns = patterns('',
url(r'^$', my_view, name="my_blog"),
)
in views.py
from django.shortcuts import render
from django.http import HttpResponse
from django.template import RequestContext, loader
def my_view(request):
"""The view for your blog page"""
post_list10 = Post.objects.all().order_by("-date")[:10]
photo_list = Photo.objects.all()
template = loader.get_template('blog.html')
context = RequestContext(request,{
'post_list': post_list10,
'photo_list': photo_list,
})
return HttpResponse(template.render(context))
The the template in blog.html would be changed to this:
{% for post in post_list %}
.....
{% endfor %}
{% for photo in photo_list %}
.....
{% endfor %}
Another thing I noticed is this comment "# FOR SOME REASON WHICHEVER COMES FIRST TAKES PRECEDENT. Cannot do both.". This is indeed the correct, and desired, behavior. The way the URL matching works is that Django starts at the top of the of the URL patterns and then iterates through. The first regex match that is found for the URL in the request is the one that is used and no further searching takes place. Because you had the same regex ^$ for both only the first one in the urlpatterns would ever be matched. This behavior will happen every time you have a duplicated regex, the second one just never gets used.
Generally speaking the Django documentation is very high quality, so do read that if you get stuck.