С настройкой очереди задач Redis давайте используем AngularJS для опроса серверной части, чтобы узнать, завершена ли задача, а затем обновим DOM, как только данные станут доступными.
Оглавление
•Текущая функциональность
•Обновите index.html
•Создайте модуль Angular
•Внедрение зависимостей и $ scope
•Рефакторинг app.py
•Базовый опрос
• Обновление DOM
•Заключение и следующие шаги
Обновления:
29.02.2010: Обновлен до версии Python 3.8.1.
22.03.2016: Обновлен до Python версии 3.5.1 и Angular версии 1.4.9.
02/22/2015: Добавлена поддержка Python 3.
Помните: вот что мы создаем - приложение Flask, которое вычисляет пары частот слов на основе текста из заданного URL-адреса.
Часть первая: Настройте локальную среду разработки, а затем разверните как промежуточную, так и производственную среду на Heroku.
Часть вторая: Настройте базу данных PostgreSQL вместе с SQLAlchemy и перегонным кубом для обработки миграций.
Часть третья: Добавьте внутреннюю логику для очистки, а затем обработайте количество слов с веб-страницы с помощью библиотек requests, BeautifulSoup и Natural Language Toolkit (NLTK).
Часть четвертая: Реализация очереди задач Redis для обработки текста.
Часть пятая: Настройте Angular на интерфейсе, чтобы постоянно опрашивать серверную часть, чтобы узнать, обработан ли запрос. (текущий)
Часть шестая: Переход на промежуточный сервер на Heroku - настройка Redis и подробное описание запуска двух процессов (веб-и рабочего) на одном динамо.
Часть седьмая: Обновите интерфейс, чтобы сделать его более удобным для пользователя.
Часть восьмая: Создайте пользовательскую угловую директиву для отображения диаграммы распределения частот с помощью JavaScript и D3.
Текущая функциональность
Во-первых, запустите Redis в одном окне терминала:
$ redis-server
В другом окне перейдите в каталог проекта и запустите рабочий:
$ cd flask-by-example $ python worker.py 20:38:04 RQ worker started, version 0.5.6 20:38:04 20:38:04 *** Listening on default...
Наконец, откройте третье окно терминала, перейдите в каталог проекта и запустите основное приложение:
$ cd flask-by-example $ python manage.py runserver
Открыть http://localhost:5000/ и проверьте с помощью URL-адреса https://realpython.com. В терминале должен быть выведен идентификатор задания. Возьмите идентификатор и перейдите по этому URL-адресу:
http://localhost:5000/results/add_the_job_id_here
Вы должны увидеть аналогичный ответ JSON в своем браузере:
[ [ "Python", 315 ], [ "intermediate", 167 ], [ "python", 161 ], [ "basics", 118 ], [ "web-dev", 108 ], [ "data-science", 51 ], [ "best-practices", 49 ], [ "advanced", 45 ], [ "django", 43 ], [ "flask", 41 ] ]
Теперь мы готовы добавить угловой.
Обновление index.html
Добавить угловой в index.html:
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.4.9/angular.min.js"></script>
Добавьте следующие директивы в index.html:
1. ng-app:
2. ng-controller:
3. ng-submit:
Итак, мы загрузили Angular, который говорит Angular рассматривать этот HTML - документ как приложение Angular, добавили контроллер, а затем добавили функцию GetResults (), которая запускается при отправке формы.
Создайте угловой модуль
Создайте “статический” каталог, а затем добавьте файл с именем main.js в этот каталог. Обязательно добавьте требование к index.html файл:
<script src="{{ url_for('static', filename='main.js') }}"></script>
Давайте начнем с этого базового кода:
(function () { 'use strict'; angular.module('WordcountApp', []) .controller('WordcountController', ['$scope', '$log', function($scope, $log) { $scope.getResults = function() { $log.log("test"); }; } ]); }());
Здесь мы прикрепили результаты к объекту $scope, чтобы он был доступен в представлении.
Внедрение зависимостей и $ scope
В приведенном выше примере мы использовали внедрение зависимостей, чтобы «внедрить» объект $ scope и службу $ log. Остановите здесь. Очень важно понимать $ scope. Начните с документации по Angular, а затем обязательно прочтите вводное руководство по Angular, если вы еще этого не сделали.
.controller('WordcountController', ['$scope', '$log', '$http', function($scope, $log, $http) { $scope.getResults = function() { $log.log("test"); // get the URL from the input var userInput = $scope.url; // fire the API request $http.post('/start', {"url": userInput}). success(function(results) { $log.log(results); }). error(function(error) { $log.log(error); }); }; } ]);
Также обновите элемент ввода в index.html:
Мы внедрили службу $ http, взяли URL-адрес из поля ввода (через ng-model = "url"), а затем отправили запрос POST в серверную часть. Обратные вызовы успеха и ошибки обрабатывают ответ. В случае ответа 200 он будет обработан обработчиком успеха, который, в свою очередь, записывает ответ в консоль.
Перед тестированием давайте проведем рефакторинг серверной части, поскольку конечная точка / start в настоящее время не существует.
Рефакторинг app.py
Выполните рефакторинг создания задания Redis из функции просмотра индекса, а затем добавьте его в новую функцию просмотра с именем
get_counts (): @app.route('/', methods=['GET', 'POST']) def index(): return render_template('index.html')
@app.route('/start', methods=['POST']) def get_counts(): # this import solves a rq bug which currently exists from app import count_and_save_words # get url data = json.loads(request.data.decode()) url = data["url"] if not url[:8].startswith(('https://', 'http://')): url = 'http://' + url # start job job = q.enqueue_call( func=count_and_save_words, args=(url,), result_ttl=5000 ) # return created job id return job.get_id()
Не забудьте также добавить следующий импорт вверху:
импортировать json
Эти изменения должны быть простыми.
Теперь тестируем. Обновите браузер, отправьте новый URL. Вы должны увидеть идентификатор задания в консоли JavaScript. Идеально. Теперь, когда у Angular есть идентификатор задания, мы можем добавить функцию опроса.
Базовый опрос
Обновите main.js, добавив в контроллер следующий код:
function getWordCount(jobID) { var timeout = ""; var poller = function() { // fire another request $http.get('/results/'+jobID). success(function(data, status, headers, config) { if(status === 202) { $log.log(data, status); } else if (status === 200){ $log.log(data); $timeout.cancel(timeout); return false; } // continue to call the poller() function every 2 seconds // until the timeout is cancelled timeout = $timeout(poller, 2000); }); }; poller(); }
Затем обновите обработчик успеха в запросе POST:
$http.post('/start', {"url": userInput}). success(function(results) { $log.log(results); getWordCount(results); }). error(function(error) { $log.log(error); });
Не забудьте также добавить в контроллер службу $ timeout.
Что тут происходит?
1.Успешный HTTP-запрос приводит к срабатыванию функции getWordCount ().
2.В функции poller () мы вызвали конечную точку / results / job_id.
3.Используя службу $ timeout, эта функция продолжает срабатывать каждые 2 секунды до тех пор, пока тайм-аут не будет отменен, когда будет возвращен ответ 200 вместе с подсчетом слов. Ознакомьтесь с документацией по Angular, чтобы узнать, как работает служба $ timeout.
При тестировании обязательно откройте консоль JavaScript. Вы должны увидеть что-то похожее на это:
Nay! 202 Nay! 202 Nay! 202 Nay! 202 Nay! 202 Nay! 202 (10) [Array(2), Array(2), Array(2), Array(2), Array(2), Array(2), Array(2), Array(2), Array(2), Array(2)]
Итак, в приведенном выше примере функция poller () вызывается семь раз. Первые шесть вызовов вернули 202, а последний вызов вернул 200 вместе с массивом подсчета слов.
Идеально.
Теперь нам нужно добавить количество слов в DOM.
Updating the DOM
Update index.html:Wordcount 3000
Frequencies
{% raw %}{{wordcounts}}{% endraw %}
Что мы изменили?
1.Тег input теперь имеет обязательный атрибут, указывающий на то, что поле ввода должно быть заполнено перед отправкой формы.
2.Попрощайтесь с тегами шаблона Jinja2. Jinja2 обслуживается на стороне сервера, и поскольку опрос полностью обрабатывается на стороне клиента, нам нужно использовать теги Angular. Тем не менее, поскольку теги шаблонов Jinja2 и Angular используют двойные фигурные скобки, {{}}, мы должны экранировать теги Jinja2, используя {% raw%} и {% endraw%}. Если вам нужно использовать несколько тегов Angular, рекомендуется изменить теги шаблонов, которые использует AngularJS, с помощью $ interpolateProvider. Для получения дополнительной информации ознакомьтесь с документацией по Angular.
Во-вторых, обновите обработчик успеха в функции poller ():
success(function(data, status, headers, config) { if(status === 202) { $log.log(data, status); } else if (status === 200){ $log.log(data); $scope.wordcounts = data; $timeout.cancel(timeout); return false; } // continue to call the poller() function every 2 seconds // until the timeout is cancelled timeout = $timeout(poller, 2000); });
Здесь мы прикрепили результаты к объекту $ scope, чтобы он был доступен в представлении.
Проверьте это. Если все прошло хорошо, вы должны увидеть объект на DOM. Не очень красиво, но это легко исправить с помощью Bootstrap, добавьте следующий код под div с id=results и удалите теги {% raw %} и {% endraw%}, которые обертывали div результатов из приведенного выше кода:
<div id="results"> <table class="table table-striped"> <thead> <tr> <th>Word</th> <th>Count</th> </tr> </thead> <tbody> {% raw %} <tr ng-repeat="element in wordcounts"> <td>{{ element[0] }}</td> <td>{{ element[1] }}</td> </tr> {% endraw %} </tbody> </table> </div>
Заключение и следующие шаги
Прежде чем перейти к построению графиков с помощью D3, нам все еще нужно:
1.Добавьте загрузочный счетчик: также известный как пульсатор, он будет отображаться до тех пор, пока задача не будет выполнена, чтобы конечный пользователь знал, что что-то происходит.
2.Рефакторинг углового контроллера: прямо сейчас в контроллере происходит слишком много (логики). Нам нужно перенести большую часть функциональности в сервис. Мы обсудим и почему, и как.
3.Обновление промежуточной среды: Нам нужно обновить промежуточную среду на Heroku - добавив изменения кода, нашего работника и Redis.
Увидимся в следующий раз!
Flask на примере –Интегрирование Flask и Angular: 1 комментарий