23 Сен, 2016

Excel-Django-Angularjs (график по данным из .xls) #2

В первой части разобрали модели, представления и валидатор. Теперь frontend и API.

UPD: Статья пишется спустя несколько месяцев, от этого скомконность.

TastypieAPI
Resources (api.py)
Angularjs
Принимаем данные и находим границы графика
Рисуем график
Конечный app.js
Страница HTML

Tastypie API.
Сейчас модно сообщать frontend и backend через RESTfull API. Это когда с клиента на определенный URL по HTTP отправляется конкретно структурированный запрос, а сервер так же отдает информацию. Используются GET, POST, DELETE, PUT методы.

На стороне сервера нам необходим какой-то инструмент для сериализации данных, определяющий, по каким URL какую информацию кому отдавать. Но сначала пойдем в url.py:

from tastypie.api import Api
from web.api import OfficeFileResource

v1_api = Api(api_name='v1')
v1_api.register(OfficeFileResource())

urlpatterns = [
url(r'^api/', include(v1_api.urls))]

Теперь Django будет принимать запросы к API по http://127.0.0.1/api/v1/… Рассмотрим в директории приложения файл api.py:

from tastypie.resources import ModelResource
from tastypie.authentication import Authentication
from tastypie.authorization import Authorization
from .models import OfficeFile

class OfficeFileResource(ModelResource):
class Meta:
queryset = OfficeFile.objects.all() //делаем запрос всех объектов модели
resource_name = 'officefile' //имя ресурса в запросе
allowed_methods = ['get', 'delete'] //методы, которые нужно использовать
authentication = Authentication() //определяем, как аутентифицировать и авторизировать юзеров
authorization = Authorization()
always_return_data = True

Запрос http://127.0.0.1/api/v1/officefile/ вернет информацию по всем объектам. Для проверки информации прямо в браузерной строке нужно прибавить к запросу «/?format=json» (http://127.0.0.1/api/v1/officefile/?format=json).

Кроме этого, мы можем конкретизировать запросы с помощью фильтров. Например, если нужно получить объекты, созданные в определенное время. По любому полю модели можно делать фильтр, но это материал следующей статьи про Memebook! (Тут будет ссылочка)

Angularjs.
Я всегда боялся Javascript. После Python, C++, да хоть BASIC, он с разбега ломает все надежды на лучшее об свой синтаксис. Но через пару часов становится ясно — тащится. Весь код ниже это смесь из нескольких начальных туториалов по AngularJS и я почти уверен, что он ужасен. Но это работает.

Сначала ревью функций, потом конечный код.

Принимаем данные.
Пишем функцию, которая отправляет http запрос GET нашему API и вносит все принятые объекты в массив files. Каждый JSON объект — объект из базы данных, а все его поля были определены нами в API. Мы будем работать только со словарем dict_coor.

$scope.files = [];
$scope.getAll =  function() {
$http.get('/api/v1/officefile/').then(function(response) {
$scope.files = response.data.objects;
});
};

На странице таблица со всеми объектами. При нажатии на какой-нибудь запускается функция clickme(f), где f — сам объект. Функция парсит наш словарь dict_coor в actualFile, где мы проходим по каждому ключу и огромным некрасивым способом вносим данные в массив array, попутно определяя предельные координаты (это необходимо для масштабирования итогового графика.

$scope.clickme = function (f) {
var actualFile = JSON.parse(f.dict_coor);
var array = [];
console.log(actualFile);

for (key in actualFile) {
if (key == 'x_value') {
var minX = actualFile.x_value[0];
var maxX = actualFile.x_value[0];

for(var i=0; i<actualFile.x_value.length; i++) {
array.push([actualFile.x_value[i], actualFile.y_value[i]]);

if(actualFile.x_value[i] > maxX){
actualFile.x_value[i] = maxX;
} else if (actualFile.x_value[i] < minX) {
actualFile.x_value[i] = minX;
}

if(actualFile.y_value[i] > maxX){
actualFile.y_value[i] = maxX;
} else if (actualFile.y_value[i] < minX) {
actualFile.y_value[i] = minX;
}
}
}

if (key == 'x') {
var minX = actualFile.x[0];
var maxX = actualFile.x[0];

for(var i=0; i<actualFile.x.length; i++) {
array.push([actualFile.x[i], actualFile.y[i]]);

if(actualFile.x[i] > maxX){
actualFile.x[i] = maxX;
} else if (actualFile.x[i] < minX) {
actualFile.x[i] = minX;
}

if(actualFile.y[i] > maxX){
actualFile.y[i] = maxX;
} else if (actualFile.y[i] < minX) {
actualFile.y[i] = minX;
}
}
}
}

Рисуем графики.

Поверхностное гугление показало, что самый простой фреймворк для отрисовки графиков на JS — Google Charts. Границы графика minX-5 и maxX+5 зависят от наших данных, а не захардкожены. График гнется, а цифра 5 от балды, чтобы точки не находились прямо на краю графика.

google.charts.load('current', {'packages':['corechart']});
google.charts.setOnLoadCallback(drawChart);
function drawChart() {
var data = new google.visualization.DataTable();
data.addColumn('number', 'x');
data.addColumn('number', 'y');
data.addRows(array);

var options = {
title: 'Graphic:',
hAxis: {title: 'y', minValue: minX-5, maxValue: maxX+5},
vAxis: {title: 'x', minValue: minX-5, maxValue: maxX+5},
legend: 'none'
};

var chart = new google.visualization.ScatterChart(document.getElementById('chart_div'));

chart.draw(data, options);
}

Конечный app.js.
Пойдем в /static/js/app.js:

app.js

Страница HTML.
Ревью конечного HTML не имеет никакого смысла, потому что там и Bootstrap, и Materialize, и Google Charts с Angular, не говоря уже о шаблонизаторе Django. Вот конечный код, в которым видны и зависимости, и версии софта, и все красоты Material, и отображение элементов AngularJS.

А это версия, которая сейчас работает:

base.html

Вопросы?