-Поиск по дневнику

Поиск сообщений в rss_rss_hh_new

 -Подписка по e-mail

 

 -Статистика

Статистика LiveInternet.ru: показано количество хитов и посетителей
Создан: 17.03.2011
Записей:
Комментариев:
Написано: 51

Habrahabr/New








Добавить любой RSS - источник (включая журнал LiveJournal) в свою ленту друзей вы можете на странице синдикации.

Исходная информация - http://habrahabr.ru/rss/new/.
Данный дневник сформирован из открытого RSS-источника по адресу http://feeds.feedburner.com/xtmb/hh-new-full, и дополняется в соответствии с дополнением данного источника. Он может не соответствовать содержимому оригинальной страницы. Трансляция создана автоматически по запросу читателей этой RSS ленты.
По всем вопросам о работе данного сервиса обращаться со страницы контактной информации.

[Обновить трансляцию]

[Из песочницы] DI для полностью переиспользуемых JSX-компонентов

Понедельник, 25 Сентября 2017 г. 14:24 + в цитатник
redyuf сегодня в 14:24 Разработка

DI для полностью переиспользуемых JSX-компонентов

Dependency inception


Привет, меня зовут Сергей и мне интересна проблема переиспользования компонент в вебе. Глядя на то, как пытаются применить SOLID к реакту, я решил продолжить эту тему и показать, как можно достичь хорошей переиспользуемости, развивая идею внедрения зависимостей или DI.


DI как основа для построения фреймворка, применительно к вебу, довольно молодой подход. Что бы тут было понятней о чем идет речь, начну c привычных для react-разработчиков вещей.


От контекстов к DI


Уверен, что многие использовали контексты при работе с react. Если не напрямую, то наверняка через connect в redux или inject в mobx-react. Суть в том, что в одном компоненте (MessageList) мы объявляем нечто в контексте, а в другом (Button) — говорим, что хотим получить это нечто из контекста.


const PropTypes = require('prop-types');

const Button = ({children}, context) =>
  ;

Button.contextTypes = {color: PropTypes.string};

class MessageList extends React.Component {
  getChildContext() {
    return {color: "purple"};
  }

  render() {
    return ;
  }
}

Т.е. один раз в родительском компоненте задается context.color, а далее он автоматически пробрасывается любым нижележащим компонентам, в которых через contextTypes объявлена зависимость от color. Таким образом Button можно кастомизировать без прокидывания свойств по иерархии. Причем на любом уровне иерархии можно через getChildContext() ... переопределить color для всех дочерних компонент.


Такой подход лучше изолирует компоненты друг от друга, упрощая их настройку и переиспользование. В примере выше, достаточно в родительском компоненте определить color и все кнопки поменяют цвет. Причем компонент Button может быть в другой библиотеке, которую при этом не надо рефакторить.


Однако, для реакта, в виду недостаточной продуманности, этот подход пока развит слабо. Напрямую его использовать разработчики не рекомендуют:


It is an experimental API and it is likely to break in future releases of React

написано в документации. Оно experimental в текущем виде уже достаточно давно и ощущение, что разработка зашла в тупик. Контексты в компонентах сцеплены с инфраструктурой (getChildContext), псевдотипизацией через PropTypes и больше похожи на service locator, который некоторые считают антипаттерном. Роль контекстов, на мой взгляд, недооценена и в реакте второстепенная: локализация и темизация, а также биндинги к библиотекам вроде redux и mobx.


В других фреймворках подобные инструменты развиты лучше. Например, в vue: provide/inject, а в angular, его angular di это уже полноценный навороченный DI с поддержкой типов typescript. По-сути, начиная с Angular второй версии, разработчики попытались переосмыслить опыт бэкенда (где DI уже давно существует) применительно к фронтенду. А что если попытаться развить аналогичную идею для реакта и его клонов, какие проблемы бы решились?


Прибивать или нет, вот в чем вопрос


В полноценном реакт/redux-приложении не всё делают через redux-экшены. Состояние какой-нибудь малозначительной галочки удобнее реализовать через setState. Получается — через redux громоздко, а через setState не универсально, но проще, т.к. он всегда под рукой. Статья You Might Not Need Redux известного автора, как бы говорит "если вам не нужно масштабирование — не используйте redux", подтверждая эту двойственность. Проблема в том, что это сейчас не нужно, а завтра может быть понадобится прикрутить логирование состояния галочки.


В другой статье того же автора, Presentational and Container Components, говорится примерно, что "Все компоненты равны (Presentational), но некоторые равнее (Container)" и при этом высечены в граните (прибиты к redux, mobx, relay, setState). Кастомизация Container компонента усложняется — не предполагается его переиспользовать, он уже прибит к реализации состояния и контексту.


Что бы как-то упростить создание Container-компонент, придумали HOC, но по-сути мало что поменялось. Просто чистый компонент стали комбинировать через connect/inject с чем-то вроде redux, mobx, relay. А полученный монолитный Container использовать в коде.


Иными словами, говорим Presentational и Container, а подразумеваем — переиспользуемый и непереиспользуемый. Первый удобно кастомизировать т.к. все точки расширения в свойствах, а второй — рефакторить, т.к свойств меньше, за счет его прибитости к стейту и некоторой логике. Это — некий компромисс по решению двух противоположных проблем, плата за который — разделение компонент на два типа и жертвование принципом открытости/закрытости.


Например, как в статье Заменяй и властвуй — подход SOLID, где предлагается делать большинство компонент максимально простыми, ухудшая их целостность. Но, сложные компоненты из простых все-равно надо будет где-то собирать и при этом остается вопрос как их кастомизировать. Т.е. проблема переносится на другой уровень.


> this.setState({ dialogOpen: false })} />
    
        
            
                > this.setState({ dialogOpen: false })} />
                Dialog header
            
            Some content
            
                
                
            
        
    

Если все же условиться, что эти оконечные компоненты мы не кастомизируем, то в реальности получается большое кол-во шаблонного кода, когда ради замены одной Button, весь компонент строится заново. Полноценный SOLID при таком подходе невозможен. Всегда будут компоненты-биндинги к стейту, которые нельзя расширить без модификации и компоненты-шаблоны без логики внутри и этим сложные в использовании.


Прототип


Развивая идею внедрения зависимостей, можно решить некоторые из этих проблем. Разберем решение на основе вот такого примера:


// @flow

// @jsx lom_h

// setup...

class HelloService {
    @mem name = ''
}

function HelloView(props: {greet: string}, service: HelloService) {
    return 
{props.greet}, {service.name}
> { service.name = e.target.value }} />
} // HelloView.deps = [HelloService] ReactDOM.render(, document.getElementById('mount'))

fiddle


Здесь есть одна универсальная форма компонента в виде функции, независимо от того, работает он с состоянием или нет. Контексты используют типы. Из них автоматически генерируются описания зависимостей с помощью babel-plugin-transform-metadata. Аналогично typescript, который это делает, правда, только для классов. Хотя можно описывать аргументы и вручную: HelloView.deps = [HelloService]


Lifecycle


А как же быть с жизненным циклом компонента? А так ли нужна низкоуровневая работа с ним в коде? Посредством HOC как раз пытаются убрать эти lifecycle methods из основного кода, например, как в relay/graphql.


Идея в том, что актуализация данных — это не ответственность компонента. Если у вас загрузка данных происходит по факту доступа к этим данным (например, используется lazyObservable из mobx-utils), то componentDidMount в этом случае не нужен. Если надо прикрутить jquery-плагин, то есть свойство refs в элементе и т.д.


Предположим, что универсальный компонент, свободный от vendor lock-in реакта, теперь есть. Пусть, мы даже выделили его в отдельную библиотеку. Осталось решить, как расширять и настраивать то, что приходит в контекст. Ведь HelloService — это некая реализация по-умолчанию.


Поди туда — не знаю куда, принеси то — не знаю что


Что если компоненты, в силу частых изменений требований, та часть приложения, где инкапсуляция начинает мешать. Не сама по себе конечно, а в том виде, как она сегодня реализована практически во всех фреймворках: в виде шаблона, композиции функций или JSX.


Представим на секунду, что в случае любого компонента нельзя заранее сказать, что у него будет кастомизироваться. И нужен способ менять любую внутреннюю часть компонента без рефакторинга (принцип открытости/закрытости), при этом не ухудшая его читабельности, не усложняя его исходную реализацию и не вкладываясь в переиспользуемость изначально (нельзя все предвидеть).


Например, без DI можно проектировать, подразумевая кастомизацию через наследование. Т.е. дробить содержимое на мелкие методы, теряя при этом в наглядности и иерархии. О минусах этого подхода пишет автор в статье Идеальный UI фреймворк:


class MyPanel extends React.Component {
    header() { return 
{this.props.head}
} bodier() { return
{this.props.body}
} childs() { return [ this.header() , this.bodier() ] } render() { return
{this.childs()}
}

class MyPanelExt extends MyPanel {
    footer() { return 
{this.props.foot}
} childs() { return [ this.header() , this.bodier() , this.footer() ] } }

Надо сказать, что этот автор (@vintage), придумал формат tree, который позволяет описать вышеприведенный пример с сохранением иерархии. Несмотря на то, что многие критикуют этот формат, у него есть преимущество как раз в виде переопределяемости даже самых мелких деталей без специального разбиения на части и рефакторинга. Иными словами, это бесплатная (почти, кроме постижения новой необычной концепции) буковка O в SOLID.


Полностью перенести этот принцип на JSX невозможно, однако частично реализовать его через DI можно попытаться. Смысл в том, что любой компонент в иерархии — это еще и точка расширения, слот, если рассуждать в терминах vue. И мы в родительском компоненте можем поменять его реализацию, зная его идентификатор (исходная реализация или интерфейс). Примерно так работают многие контейнеры зависимостей, позволяя ассоциировать реализации с интерфейсами.


В js/ts, в runtime, без усложнений или внедрения строковых ключей, ухудшающих безопасность кода, нельзя ссылаться на интерфейс. Поэтому следующий пример не заработает в flow или typescript (но аналогичный заработает в C# или Dart):


interface ISome {}

class MySome implements ISome {}

const map = new Map()
map.set(ISome, MySome)

Однако, можно ссылаться на абстрактный класс или функцию.


class AbstractSome {}

class MySome extends AbstractSome {}

const map = new Map()
map.set(AbstractSome, MySome)

Т.к. создание объектов и компонент происходит внутри DI-контейнера, а там внутри может быть подобный map, то любую реализацию можно переопределить. А т.к. компоненты, кроме самых примитивных — функции, то их можно подменять на функции с таким же интерфейсом, но с другой реализацией.


Например, TodoResetButtonView является частью TodoView. Требуется переопределить TodoResetButtonView на кастомную реализацию.


function TodoResetButtonView({onClick}) {
  return 
}

function TodoView({todo, desc, reset}) {
    return 
  • > todo.finished = !todo.finished} />{todo.title} #{todo.id} ({desc.title}) reset
  • }

    Предположим у нас нет возможности править TodoView (он в другой библиотеке и мы не хотим его трогать, нарушая open/close принцип и заново тестировать 11 других проектов, которые его использовали со старой кнопкой).


    Поэтому создаем новую кнопку и клонируем существующий TodoView, заменяя ее в клоне. Это наследование, только наглядность не нарушается — остается иерархия и не нужно специально заранее проектировать TodoView так, что бы можно было заменить кнопку.


    function ClonedTodoResetButtonView({onClick}) {
      return 
    }
    
    const ClonedTodoView = cloneComponent(TodoView, [
        [TodoResetButtonView, ClonedTodoResetButtonView]
    ], 'ClonedTodoView')
    
    const ClonedTodoListView = cloneComponent(TodoListView, [
        [TodoView, ClonedTodoView]
    ], 'ClonedTodoListView')
    
    ReactDOM.render(, document.getElementById('mount'));

    fiddle


    Переопределять иногда надо не только компоненты, но и их зависимости:


    class AbstractHelloService {
        name: string
    }
    
    function HelloView(props: {greet: string}, service: AbstractHelloService) {
        return 
    {props.greet}, {service.name}
    > { service.name = e.target.value }} />
    } class AppHelloService { @mem name = 'Jonny' } function AppView() { return } AppView.aliases = [ [AbstractHelloService, AppHelloService] ]

    fiddle


    HelloView получит экземпляр класса AppHelloService. Т.к. AppView.aliases для всех дочерних компонент переопределяет AbstractHelloService.


    Есть конечно и минус подхода "все кастомизируется" через наследование. Т.к. фреймворк предоставляет больше точек расширения, то, больше ответственности по кастомизации перекладывается на того, кто использует компонент, а не проектирует его. Переопределяя части компонента "таблица", без осознания смысла, можно ненароком превратить его в "список", а это плохой признак, т.к. является искажением исходного смысла (нарушается LSP принцип).


    Разделение состояния


    По-умолчанию, состояние в зависимостях компонента будет выделено под каждый компонент. Однако действует общий принцип: все определяемое в компонентах выше — имеет приоритет над нижележащими зависимостями. Т.е. если зависимость впервые используется в родительском компоненте, то она будет жить вместе с ним и все нижележащие компоненты, ее запросившие, получат именно родительский экземпляр.


    class HelloService {
        @mem name = 'John'
    }
    
    function HelloView(props: {greet: string}, service: HelloService) {
        return 
    {props.greet}, {service.name}
    > { service.name = e.target.value }} />
    } class AppHelloService { @mem name = 'Jonny' } function AppView(_, service: HelloService) { return
    }

    fiddle


    В такой конфигурации, оба HelloView разделяют общий экземпляр HelloService. Однако, без HelloService в AppView, на каждый дочерний компонент будет свой экземпляр.


    function AppView() {
        return 
    }

    Подобный принцип, когда можно управлять, какому компоненту принадлежит объект, используется в иерархическом DI ангулара.


    Стили


    Я не утверждаю, что подход css-in-js единственно правильный для использования в веб. Но и тут можно применить идею внедрения зависимостей. Проблема аналогична вышеописанной с redux/mobx и контекстами. Например, как и во многих подобных библиотеках, стили jss прибиваются к компоненту через обертку injectSheet и компонент связывается с конкретной реализацией стилей, с react-jss:


    import React from 'react'
    import injectSheet from 'react-jss'
    
    const styles = {
      button: {
        background: props => props.color
      },
      label: {
        fontWeight: 'bold'
      }
    }
    
    const Button = ({classes, children}) => (
      
    )
    
    export default injectSheet(styles)(Button)

    Однако, эту прямую зависимость от jss и ему подобных можно убрать, перенеся эту ответственность на DI. В коде приложения достаточно определить функцию со стилями, как зависимость компонента и пометить ее соответственно.


    // ... setup
    import {action, props, mem} from 'lom_atom'
    import type {NamesOf} from 'lom_atom'
    
    class Store {
      @mem red = 140
    }
    
    function HelloTheme(store: Store) {
      return {
        wrapper: {
          background: `rgb(${store.red}, 0, 0)`
        }
      }
    }
    HelloTheme.theme = true
    
    function HelloView(
      _,
      {store, theme}: {
        store: Store,
        theme: NameOf
      }
    ) {
      return 
    color via css {store.red}: > { store.red = Number(target.value) }} />
    }

    fiddle


    Такой подход для стилей обладает всеми преимуществами DI, таким образом обеспечивается темизация и реактивность. В отличие от переменных в css, здесь работают типы в flow/ts. Из минусов — накладные расходы на генерацию и обновление css.


    Итог


    В попытке адаптировать идею внедрения зависимостей для компонентов, получилась библиотека reactive-di. Простые примеры в статье постронены на ее основе, но есть и более сложные, с загрузкой, обработкой статусов загрузки, ошибок и т.д. Есть todomvc бенчмарк для react, preact, inferno. В котором можно оценить оверхед от использования reactive-di. Правда, на 100 todos, погрешность измерений у меня была больше, чем этот оверхед.


    Получился упрощенный Angular. Однако есть ряд особенностей, reactive-di


    1. Умеет интегрироваться с реактом и его клонами, оставаясь совместимым с legacy-компонентами на чистом реакте
    2. Позволяет писать на одних чистых компонентах, не оборачивая их в mobx/observe или подобное
    3. Хорошо работает с типами в flowtype не только для классов, но и для компонент-функций
    4. Ненавязчив: не требуется кучи декораторов в коде, компоненты абстрагируются от react, его можно поменять на свою реализацию, не затрагивая основной код
    5. Настраивается просто, не нужно региситрации зависимостей, provide/inject-подобных конструкций
    6. Позволяет доопределять содержимое компонента без его модификации, c сохранением иерархии его внутренностей
    7. Позволяет ненавязчиво, через интерфейсы, интегрировать css-in-js решения в компоненты

    Почему до сих пор идею контекстов не развивали в этом ключе? Скорее всего непопулярность DI на фронтенде объясняется не повсеместным господством flow/ts и отсутствием стандартной поддержки интерфейсов на уровне метаданных. Попытками скопировать сложные реализации с других backend-ориентированных языков (как InversifyJS клон Ninject из C#) без глубокого переосмысления. А также пока недостаточным акцентом: например, некоторое подобие DI есть в react и vue, но там эти реализации являются неотделимой частью фреймворка и роль их второстепенная.


    Хороший DI — это еще половина решения. В примерах выше часто мелькал декоратор @mem, который необходим для управления состоянием, построенном на идее ОРП. С помощью mem можно писать код в псевдосинхронном стиле, с простой, по-сравнению с mobx, обработкой ошибок и статусов загрузки. Про него я расскажу в следующей статье.

    Original source: habrahabr.ru (comments, light).

    https://habrahabr.ru/post/338666/


    Метки:  

    [Перевод] Реактивные приложения с Model-View-Intent. Часть 2: View и Intent

    Понедельник, 25 Сентября 2017 г. 14:02 + в цитатник
    valpostnov сегодня в 14:02 Разработка

    Реактивные приложения с Model-View-Intent. Часть 2: View и Intent

    • Перевод
    В первой части мы обсудили что такое модель, ее связь с состоянием и то, как правильно спроектированная модель помогает решить некоторые проблемы в разработке под Android. В этой статье мы продолжим наш путь к созданию реактивных приложений с помощью паттерна Model-View-Intent.

    Прежде, чем начать, вкратце обсудим основную идею MVI.

    Model-View-Intent (MVI)


    Этот паттерн был описан Андре Штальтцем (Andr'e Staltz) для JavaScript-фреймворка cycle.js. С теоретической и математической точки зрения MVI можно описать следующим образом:

    image


    • intent(): Функция, которая принимает входные данные от пользователя (например, события пользовательского интерфейса, такие как события click) и переводит в то, что будет передано как параметр функции model(). Это может быть простая строка для установки значения модели или более сложная структура данных, например, объект.
    • model(): Функция, которая использует выходные данные из функции intent() в качестве входных данных для работы с моделью. Результат работы этой функции – новая модель (с измененным состоянием). При этом нужно, чтобы данные были неизменяемыми. В первой части я приводил пример с приложением-счетчиком: мы не меняем уже существующий экземпляр модели, а создаем новую модель согласно изменениям, описанным в интенте. Функция model() – лишь часть кода, ответственная за создание нового объекта модели. По сути, функция model() осуществляет вызов бизнес-логики приложения (будь-то Interactor, UseCase, Repository) и в результате возвращает новый объект модели.
    • view(): Функция, которая получает на входе модель от model() и просто отображает ее. Обычно функция view() выглядит как view.render(model).

    Вернемся к нашей задаче. Мы хотим создать реактивное приложение. Но реактивен ли MVI? Что же в действительности означает реактивность в этом контексте?

    Под реактивностью мы понимаем приложение с UI, которое реагирует на изменение состояния. Поскольку состояние отражает модель, то необходимо, чтобы наша бизнес-логика реагировала на события (интенты), а на выходе создавала модель, которую можно было бы отобразить во View, вызвав метод render(model).

    Соединяем точки с помощью RxJava


    Нам нужно, чтобы поток данных был однонаправленным. Здесь в игру вступает RxJava. При создании реактивных приложений с однонаправленным потоком данных не обязательно использовать именно эту библиотеку. Однако RxJava хорошо подходит для событийного программирования. И поскольку UI основан на событиях, имеет смысл использовать именно ее.

    В этой статье я опишу создание простого приложения для вымышленного интернет-магазина. В приложение можно будет искать продукты и добавлять их в корзину.

    Готовое приложение будет выглядеть так:





    Исходный код можно найти на GitHub.

    Начнем с реализации экрана поиска. Первым делом я определяю модель, которая будет отображена с помощью View, как описано в первой части этого цикла статей. Все классы моделей я буду писать с суффиксом ViewState, поскольку модель отражает состояние.

    SearchViewState
    public interface SearchViewState {
    
       final class SearchNotStartedYet implements SearchViewState {}
       final class Loading implements SearchViewState {}
    
       final class EmptyResult implements SearchViewState {
           private final String searchQueryText;
    
           public EmptyResult(String searchQueryText) {
               this.searchQueryText = searchQueryText;
           }
    
           public String getSearchQueryText() {
               return searchQueryText;
           }
       }
    
       final class SearchResult implements SearchViewState {
           private final String searchQueryText;
           private final List result;
    
           public SearchResult(String searchQueryText, List result) {
               this.searchQueryText = searchQueryText;
               this.result = result;
           }
    
           public String getSearchQueryText() {
               return searchQueryText;
           }
    
           public List getResult() {
               return result;
           }
       }
    
       final class Error implements SearchViewState {
           private final String searchQueryText;
           private final Throwable error;
    
           public Error(String searchQueryText, Throwable error) {
               this.searchQueryText = searchQueryText;
               this.error = error;
           }
    
           public String getSearchQueryText() {
               return searchQueryText;
           }
    
           public Throwable getError() {
               return error;
           }
       }
    


    Java – строго типизированный язык, поэтому я выбрал типобезопасный подход к созданию модели, разделив каждое подсостояние внутри класса. Бизнес-логика будет возвращать объект типа SearchViewState, который может быть экземпляром SearchViewState.Error и т.д. Это мое личное предпочтение, вы можете проектировать модель по-своему.

    Сосредоточимся на бизнес–логике. Создадим SearchInteractor, который будет отвечать за поиск. Результатом выполнения будет объект SearchViewState.

    SearchInteractor
    public class SearchInteractor {
       final SearchEngine searchEngine; 
    
       public Observable search(String searchString) {
           if (searchString.isEmpty()) {
               return Observable.just(new SearchViewState.SearchNotStartedYet());
           }
    
           return searchEngine.searchFor(searchString)
                  .map(products -> {
                      if (products.isEmpty()) {
                          return new SearchViewState.EmptyResult(searchString);
                      } else {
                          return new SearchViewState.SearchResult(searchString, products);
                      }
                  })
                  .startWith(new SearchViewState.Loading())
                  .onErrorReturn(error -> new SearchViewState.Error(searchString, error));
       }
    }
    


    Посмотрим на сигнатуру метода SearchInteractor.search(): есть входной параметр searchString и выходной параметр Observable. Это говорит о том, что на наблюдаемом потоке мы ожидаем произвольное количество экземпляров SearchViewState. Метод startWith() нужен для того, чтобы заэмитить SearchViewState.Loading перед тем, как начать поисковый запрос. Тогда View сможет показать progressBar во время выполнения поиска.

    Метод onErrorReturn() ловит любые исключения, которые могут возникнуть во время выполнения поиска, и эмитит SearchViewState.Error. Мы не можем просто использовать колбэк onError() при подписке на Observable. Это распространенное заблуждение в RxJava: колбэк onError() нужно использовать тогда, когда весь наблюдаемый поток наталкивается на неустранимые ошибки и весь поток завершается.

    В нашем случае ошибка отсутствия подключения к интернету не попадает под определение неустранимых ошибок – это просто одно из состояний нашей модели. Кроме того, мы сможем переключиться на другое состояние – SearchViewState.Loading, – после того, как подключение к интернету снова будет доступно.

    Таким образом мы создаем наблюдаемый поток из бизнес–логики во View, который эмитит новую модель каждый раз, когда изменяется состояние. Нам не нужно, чтобы наблюдаемый поток завершался при ошибке подключения к интернету, поэтому такие ошибки обрабатываются как состояние. Обычно в MVI наблюдаемый поток никогда не завершается (не вызываются методы onComplete или onError()).

    Подводя итог: SearchInteractor предоставляет наблюдаемый поток Observable и эмитит новый SearchViewState каждый раз при изменении состояния.

    Рассмотрим, как выглядит слой View, который должен отобразить модель. Ранее я предложил, чтобы View имела функцию render(model). Кроме того, View должна предоставлять возможность другим слоям реагировать на события UI. В MVI эти события называются интенты. В нашем случае есть только один интент: пользователь ищет продукт путем ввода поискового запроса в поле поиска. В MVP есть хорошая практика создавать интерфейс для слоя View, поступим также и для MVI.

    public interface SearchView {
       Observable searchIntent();
       void render(SearchViewState viewState);
    }
    

    В нашем случае View предоставляет только один интент, но в зависимости от задачи их может быть несколько.

    В первой части мы обсудили, почему использование единственного метода render() является хорошим решением. Перед созданием конкретной реализации слоя View посмотрим на то, как будет выглядеть окончательный вариант:





    SearchFragment
    public class SearchFragment extends Fragment implements SearchView {
    
       @BindView(R.id.searchView) android.widget.SearchView searchView;
       @BindView(R.id.container) ViewGroup container;
       @BindView(R.id.loadingView) View loadingView;
       @BindView(R.id.errorView) TextView errorView;
       @BindView(R.id.recyclerView) RecyclerView recyclerView;
       @BindView(R.id.emptyView) View emptyView;
       private SearchAdapter adapter;
    
       @Override 
        public Observable searchIntent() {
           return RxSearchView.queryTextChanges(searchView)
                   .filter(queryString -> queryString.length() > 3 || queryString.length() == 0)
                   .debounce(500, TimeUnit.MILLISECONDS);
       }
    
       @Override
        public void render(SearchViewState viewState) {
           if (viewState instanceof SearchViewState.SearchNotStartedYet) {
               renderSearchNotStarted();
           } else if (viewState instanceof SearchViewState.Loading) {
               renderLoading();
           } else if (viewState instanceof SearchViewState.SearchResult) {
               renderResult(((SearchViewState.SearchResult) viewState).getResult());
           } else if (viewState instanceof SearchViewState.EmptyResult) {
               renderEmptyResult();
           } else if (viewState instanceof SearchViewState.Error) {
               renderError();
           } else {
               throw new IllegalArgumentException("Don't know how to render viewState " + viewState);
           }
       }
    
       private void renderResult(List result) {
           TransitionManager.beginDelayedTransition(container);
           recyclerView.setVisibility(View.VISIBLE);
           loadingView.setVisibility(View.GONE);
           emptyView.setVisibility(View.GONE);
           errorView.setVisibility(View.GONE);
           adapter.setProducts(result);
           adapter.notifyDataSetChanged();
       }
    
       private void renderSearchNotStarted() {
           recyclerView.setVisibility(View.GONE);
           loadingView.setVisibility(View.GONE);
           errorView.setVisibility(View.GONE);
           emptyView.setVisibility(View.GONE);
       }
    
       private void renderLoading() {
           recyclerView.setVisibility(View.GONE);
           loadingView.setVisibility(View.VISIBLE);
           errorView.setVisibility(View.GONE);
           emptyView.setVisibility(View.GONE);
       }
    
       private void renderError() {
           recyclerView.setVisibility(View.GONE);
           loadingView.setVisibility(View.GONE);
           errorView.setVisibility(View.VISIBLE);
           emptyView.setVisibility(View.GONE);
       }
    
       private void renderEmptyResult() {
           recyclerView.setVisibility(View.GONE);
           loadingView.setVisibility(View.GONE);
           errorView.setVisibility(View.GONE);
           emptyView.setVisibility(View.VISIBLE);
       }
    }
    


    Метод render(SearchViewState) должен выглядеть лаконично. В searchIntent() я использую библиотеку RxBindings. RxSearchView.queryText() создает Observable, который эмитит строку каждый раз, когда пользователь что-то вводит в виджет EditText. Я использую filter(), чтобы поисковый запрос начинался после ввода трех символов и более. Нам не нужно, чтобы поисковый запрос отправлялся на сервер каждый раз, когда пользователь вводит новый символ, поэтому я добавил оператор debounce().

    Мы знаем, что входной поток данных для данного экрана – метод searchIntent(), а выходной поток данных – метод render().
    Следующее видео наглядно демонстрирует, как происходит взаимодействие между двумя этими потоками.





    Остался вопрос, как связать интент и бизнес–логику? Если вы внимательно посмотрите видео, то увидите оператор flatMap() посередине. Это указывает на наличие дополнительного компонента, о котором я не говорил – Presenter, – отвечающего за соединения слоев.

    public class SearchPresenter extends MviBasePresenter {
       private final SearchInteractor searchInteractor;
    
       @Override protected void bindIntents() {
           Observable search =
                   intent(SearchView::searchIntent)
                           .switchMap(searchInteractor::search) // на видео я использовал flatMap(), но здесь имеет смысл использовать switchMap()
                           .observeOn(AndroidSchedulers.mainThread());
    
           subscribeViewState(search, SearchView::render);
       }
    }
    

    Что такое MviBasePresenter, методы intent() и subscribeViewState(). Этот класс – часть библиотеки Mosby. Стоит сказать несколько слов о Mosby и о том, как работает MviBasePresenter. Начнем с жизненного цикла: у MviBasePresenter его нет. Метод bindIntent() связывает интент из View с бизнес–логикой. Как правило для пересылки интента в бизнес-логику используется flatMap() или switchMap(). Этот метод вызывается единожды, когда View присоединяется к Presenter, но не вызывается после того, как View вновь присоединится к Presenter, например, после изменение ориентации экрана.

    Может возникнуть вопрос, действительно ли MviBasePresenter может пережить изменение ориентации экрана, и если да, то каким образом Mosby гарантирует, что наблюдаемый поток не “утекает”? Для этого и предназначены методы intent() и subscribeViewState().

    intent() создает PublishSubject внутри Presenter и использует его как “шлюз” для бизнес–логики. PublishSubject подписывается на интент View. Вызов интента (О1) на самом деле возвращает PublishSubject, который подписан на О1.

    После изменения ориентации экрана Mosby отсоединяет View от Presenter, но только временно отписывает внутренний PublishSubject из View и повторно переподпиcывает PublishSubject на интент View, когда View вновь присоединяется к Presenter.

    subscribeViewState() делает то же самое в обратную сторону. Он создает внутри Presenter BehaviorSubject в качестве “шлюза” от бизнес–логики ко View. Так как это BehaviorSubject, мы можем получить обновленную модель из бизнес–логики даже когда View отсоединена от Presenter. BehaviorSubject всегда хранит последнее значение, которое он получил, и повторяет его, когда View вновь присоеденится к Presenter.

    Простое правило: используйте метод intent() чтобы обернуть любой интент. Используйте subscribeViewState() вместо Observable.subscribe(…).



    Как насчет других событий жизненного цикла, например, onPause() или onResume()? Я по–прежнему считаю, что презентеру не нужны события жизненного цикла. Однако если вы действительно думаете, что они вам нужны, то можете создать их как интент. В вашем View появится pauseIntent(), запуск которого инициирует жизненный цикл Android, а не действие пользователя.

    Заключение


    В этой части мы поговорили об основах MVI и реализовали простой экран. Вероятно, этот пример слишком прост, чтобы понять все преимущества MVI. Нет ничего плохого в MVP или MVVM, и я не говорю, что MVI лучше, чем другие архитектурные шаблоны. Тем не менее, я считаю, что MVI помогает нам писать более элегантный код для сложных проблем, как мы увидим в следующей части, в которой поговорим о state reducer.
    Original source: habrahabr.ru (comments, light).

    https://habrahabr.ru/post/338558/


    Метки:  

    Визуализация результатов выборов в Москве на карте в Jupyter Notebook

    Понедельник, 25 Сентября 2017 г. 14:00 + в цитатник
    fall_out_bug сегодня в 14:00 Разработка

    Визуализация результатов выборов в Москве на карте в Jupyter Notebook


      Всем привет!


      Сегодня мы поговорим о визуализации геоданных. Имея на руках статистику, явно имеющую пространственную привязку, всегда хочется сделать красивую карту. Желательно, с навигацией да инфоокнами В тетрадках. И, конечно же, чтоб потом можно было показать всему интернету свои успехи в визуализации!


      В качестве примера возьмем недавно отгремевшие муниципальные выборы в Москве. Сами данные можно взять с сайта мосгоризбиркома, в можно просто забрать датасеты с https://gudkov.ru/. Там даже есть какая-никакая визуализация, но мы пойдем глубже. Итак, что же у нас в итоге должно получиться?


      Потратив некоторое время на написание парсера сайта Избиркома, я получил нужные мне данные. Итак, начнем с импортов.


      импорты
      import pandas as pd
      import numpy as np
      import os
      import pickle

      Я работаю в jupyter notebook на Linux-машине. Если вы захотите использовать мой код на Windows машине, то обращайте внимание на написание путей, а также на важные отступления в тексте.


      Обычно я использую отдельную папку для проекта, поэтому для простоты задаю текущую директорию:


      os.chdir('/data01/jupyter/notebooks/habr/ods_votes/')

      Дальше нам трубуется забрать данные с самого сайта Избиркома. Для разбора данных я написал отдельный парсер. Весь процесс занимает 10-15 минут. Забрать его можно из репозитория.


      Я решил создать большой словарь с датафреймами внутри. Для превращения html-страниц в датафреймы я использовал read_html, эмпирически подбирал нужные датафрейс, а после этого делал небольшую обработку, выкидывая лишнее и добавляя недостающее. Предварительно я уже обработал данные по партиям. Изначально они были не особо читаемы. К тому же, встречается разное написание одних и тех же партий (забавно, но в некоторых случаях это не разное написание, а реально разные партии).


      Разбор данных избиркома


      Непосредственно сборка справочника. Что здесь происходит:


      • собираются структура административных и муниципальных округов;
      • собираются все ссылки на Территориальные Избирательные Комиссии (ТИК);
      • для каждого ТИК собирается список кандидатов;
      • внутри каждого ТИК собираются Окружные Избирательные Комиссии (ОИК);
      • для каждого ОИК собирается статистика по ОИК и статистика по кандидатам;
      • из полученного датасета собираем статистику по муниципальным округам.

      В репозитории этой статьи лежат уже готовые данные. Их мы и будем использовать.


      Разбор данных
      import glob
      
      # забираем справочник сокращений для партий
      with open('tmp/party_aliases.pkl', 'rb') as f:
          party_aliases = pickle.load(f)
      
      votes = {}
      # забираем список округов и мунициальных образований
      votes['atd'] = pd.read_csv('tmp/atd.csv', index_col=0, sep=';')
      votes['data'] = {}
      # идем по мунициальным образованиям и собираем статистику ТИК
      for v in votes['atd']['municipal'].values:
          votes['data']
          # забираем статистику по кандидатам
          candidates = glob.glob('tmp/data_{}_candidates.csv'.format(v))[0]
          votes['data'][v]['candidates'] = pd.read_csv(candidates, index_col=0, sep=';')
          votes['data'][v]['votes'] = {}
          # теперь по каждому ОИК собираем его статистику
          # статистика по УИК
          okrug_stats_list = glob.glob('tmp/data_{}*_okrug_stats.csv'.format(v))
          for okrug_stats in okrug_stats_list:
              okrug = int(okrug_stats.split('_')[2])
              try:
                  votes['data'][v]['votes'][okrug]
              except:
                  votes['data'][v]['votes'][okrug] = {}
              votes['data'][v]['votes'][okrug]['okrug_stats'] = pd.read_csv(okrug_stats, index_col=0, sep=';')
          # статистика по кандидатам
          candidates_stats_list = glob.glob('tmp/data_{}*_candidates_stats.csv'.format(v))
          for candidates_stats in candidates_stats_list:
              okrug = int(candidates_stats.split('_')[2])
              votes['data'][v]['votes'][okrug]['candidates_stats'] = pd.read_csv(candidates_stats, index_col=0, sep=';')
      
      # теперь собираем статистику в удобной нам форме
      data = []
      
      # пройдемся по муниципальным округам
      for okrug in list(votes['data'].keys()):
      
          #чистим данные
          candidates = votes['data'][okrug]['candidates'].replace(to_replace={'party':party_aliases})
          group_parties = candidates[['party','elected']].groupby('party').count()
      
          # создаем общую статистику по избирателям
          stats = np.zeros(shape=(12))
          for oik in votes['data'][okrug]['votes'].keys():
              stat = votes['data'][okrug]['votes'][oik]['okrug_stats'].iloc[:,1]
              stats += stat
      
          # создаем статистику по партиям
          # количество мест
          sum_parties = group_parties.sum().values[0]
      
          # количество полученных мест
          data_parties = candidates[['party','elected']].groupby('party').count().reset_index()
      
          # процент полученных мест
          data_parties['percent'] = data_parties['elected']/sum_parties*100
      
          # собираем итоговую таблицу по округу
          tops = data_parties.sort_values('elected', ascending=False)
          c = pd.DataFrame({'okrug':okrug}, index=[0])
          c['top1'], c['top1_elected'], c['top1_percent'] = tops.iloc[0,:3]
          c['top2'], c['top2_elected'], c['top2_percent'] = tops.iloc[1,:3]
          c['top3'], c['top3_elected'], c['top3_percent'] = tops.iloc[2,:3]
          c['voters_oa'], c['state_rec'], c['state_given'], c['state_anticip'], c['state_out'], c['state_fired'], c['state_box'], c['state_move'], c['state_error'], c['state_right'], c['state_lost'] , c['state_unacc'] = stats 
          c['voters_percent'] = (c['state_rec'] - c['state_fired'])/c['voters_oa']*100
          c['total'] = sum_parties
          c['full'] = (c['top1_elected']== sum_parties)
      
          # добавляем полученный датафрейм в список
          data.append(c)
      
      # создаем итоговый датафрейм
      winners = pd.concat(data,axis=0)

      Мы получили датафрейм со статистикой явки, бюллютеней (от количества выданных до количества испорченных), распределением мест между партиями.
      Можно приступать к визуализации!


      Базовая работа с геоданными в geopandas


      Для работы с геоданными мы будем использовать библиотеку geopandas. Что такое geopandas? Это расширение функциональности pandas географическими абстракциями (унаследованными из Shapely), которые позволяют нам проводит аналитические географические операции с геоданными: выборки, оверлей, аггрегация (как, например, в PostGIS для Postgresql).


      Напомню, что существует три базовых типа геометрии — точка, линия (а точнее, полилиния, так как состоит из соединенных отрезков) и полигон. У всех у них бывает вариант мульти-(Multi), где геометрия представляет собой объединение отдельных географических образований в один. Например, выход метро может быть точкой, но несколько выходов, объединенных в сущность "станция", уже являются мультиточкой.


      Важное отступление

      Следует обратить внимание, что geopandas неохотно ставится через pip в стандартной установке Python в среде Windows. Проблема, как обычно, в зависимостях. Geopandas опирается на абстракции библиотеки fiona, у которой нет официальных сборок под Windows. Идеально использовать среду Linux, например, в docker-контейнере. Кроме того, в Windows можно использовать менеджер conda, он все зависимости подтягивает из своих репозиториев.


      C геометрией муниципальных образований все достаточно просто. Их можно легко забрать из OpenStreetMap (подробнее тут) или, например, из выгрузок NextGIS. Я использую уже готовые шейпы.


      Итак, начнем! Выполняем нужные импорты, активируем графики matplotlib...


      import geopandas as gpd
      %matplotlib inline
      mo_gdf = gpd.read_file('atd/mo.shp')
      mo_gdf.head()


      Как видите, это привычный DataFrame. Поле geometry — представление географических объектов (в данном случае — полигонов) в виде WKT, well known text (подробнее — https://en.wikipedia.org/wiki/Well-known_text). Можно довольно просто построить карту наших объектов.


      mo_gdf.plot()


      Угадывается Москва! Правда, не совсем привычно выглядит. Причина в проекции карты. На Хабре уже есть отличный ликбез по ним.


      Итак, представим наши данные в более привычной проекции Web Mercator (исходную проекцию можно легко получить по параметру crs). Окрасим полигоны по названию Административного округа. Ширину линий выставим 0,5. Метод окраски cmap использует стандартные значения matplotlib (если вы, как и я, не помните их наизусть, то вот шпаргалка). Чтобы увидеть легенду карты, задаем параметр legend. Ну а figsize отвечает за размер нашей карты.


      mo_gdf_wm = mo_gdf.to_crs({'init' :'epsg:3857'}) #непосредственно преобразование проекции
      mo_gdf_wm.plot(column = 'ABBREV_AO', linewidth=0.5, cmap='plasma', legend=True, figsize=[15,15])


      Можно построить карту и по типу муниципального образования:


      mo_gdf_wm.plot(column = 'TYPE_MO', linewidth=0.5, cmap='plasma', legend=True, figsize=[15,15])


      Итак, построим карту статистики по муниципальным округам. Ранее мы уже создали датафрейм winners.
      Нам необходимо соединить наш датафрейм с геодатафреймом для создания карты. Немного причешем названия мунициальных округов, чтобы соединение произошло без сюрпризов.


      winners['municipal_low'] = winners['okrug'].str.lower()
      winners['municipal_low'] = winners['municipal_low'].str.replace('ё', 'е')
      mo_gdf_wm['name_low'] = mo_gdf_wm['NAME'].str.lower()
      mo_gdf_wm['name_low'] = mo_gdf_wm['name_low'].str.replace('ё', 'е')
      full_gdf = winners.merge(mo_gdf_wm[['geometry', 'name_low']], left_on='municipal_low', right_on='name_low', how='left')
      full_gdf = gpd.GeoDataFrame(full_gdf)

      Построим простую категориальная карту, где от зеленого к синему распределены партии-победители. В районе Щукино в этом году и правда не было выборов.


      full_gdf.plot(column = 'top1', linewidth=0, cmap='GnBu', legend=True, figsize=[15,15])


      Явка:


      full_gdf.plot(column = 'voters_percent', linewidth=0, cmap='BuPu', legend=True, figsize=[15,15])


      Жители:


      full_gdf.plot(column = 'voters_oa', linewidth=0, cmap='YlOrRd', legend=True, figsize=[15,15])


      Отлично! У нас получилась симпатичная визуализация. Но хочется и базовую карту, и навигацию! На помощь нам придет библиотека cartoframes.


      Визуализация геоданных с помощью cartoframes


      Одним из самых удобных инструментов для визуализации геоданных является Carto. Для работы с этим сервисом существуюет библиотеке cartoframes, которая позволяет работать с функциями сервиса прямо из тетрадок Jupyter.


      Важное отступление

      Библиотека cartoframes требует внимательного обращения под Windows в силу особенностей разработки (например, при заливке датасета библиотека пытается использовать стиль папок linux, что приводит к печальным последствиям). С кириллическими данными можно легко отстрелить себе ногу (кодировка cp1251 может быть превращена в кракозябры). Лучше ее использовать или в docker-контейнере, или на полноценном Linux. Ставится библиотека только через pip. В windows ее можно успешно установить, предварительно поставив geopandas через conda (или поставив все зависимости руками).


      Cartoframes работает с проекцией WGS84. В нее и перепроецируем наш датасет. После соединения двух датафреймов может теряться информация о проекции. Зададим ее заново и перепроецируем.


      full_gdf.crs = ({'init' :'epsg:3857'})
      full_gdf = full_gdf.to_crs({'init' :'epsg:4326'})

      Делаем нужные импорты...


      import cartoframes
      import json
      import warnings
      warnings.filterwarnings("ignore")

      Добавляем данные от аккаунта Carto:


      USERNAME = 'ваш пользователь Carto'
      APIKEY = 'ваш ключ API'

      И, наконец, подключаемся к Carto и заливаем наш датасет:


      cc = cartoframes.CartoContext(api_key=APIKEY, base_url='https://{}.carto.com/'.format(USERNAME))
      cc.write(full_gdf, encode_geom=True, table_name='mo_votes', overwrite=True)

      Датасет можно выгрузить с Carto обратно. Но полноценный геодатафрейм пока только в проекте. Правда, можно с помощью gdal и shapely сконвертировать бинарное представление геометрии PostGIS снова в WKT.
      Особенностью работы плагина является приведением типов. Увы, в текущей версии датафрейм заливается в таблицу с назначением типа str для каждого столбца. Об этом надо помнить при работе с картами.
      Наконец, карта! Разукрасим данные, положим на базовую карту и включим навигацию. Подсмотреть схемы окрашивания можно здесь.


      Для нормальной работы с разбиением классов напишем запрос с приведением типов. Синтаксис PostgreSQL


      query_layer = 'select cartodb_id, the_geom, the_geom_webmercator, voters_oa::integer, voters_percent::float, state_out::float from mo_votes'

      Итак, явка:


      from cartoframes import Layer, BaseMap, styling, QueryLayer
      l = QueryLayer(query_layer, color={'column': 'voters_percent', 'scheme': styling.darkMint(bins=7)})
      map = cc.map(layers=[BaseMap(source='light', labels='front'), l], size=(990, 500), interactive=False)


      Количество жителей


      l = QueryLayer(query_layer, color={'column': 'voters_oa', 'scheme': styling.burg(bins=7)})
      map = cc.map(layers=[BaseMap(source='light', labels='front'), l], size=(990, 500), interactive=False)


      И, например, надомное голосование


      l = QueryLayer(query_layer, color={'column': 'state_out', 'scheme': styling.sunsetDark(bins=5)})
      map = cc.map(layers=[BaseMap(source='light', labels='front'), l], size=(990, 500), interactive=False)


      Следует заметить, что в данный момент cartoframes не позволяет встроить инфоокна прямо в окно тетрадки, показывать легенду, а также публиковать карты на Carto. Но эти опции в процессе имплементации.


      А теперь попробуем более сложный, но весьма гибкий способ встраивания карт в Jupyter Notebook...


      Визуализация геоданных с помощью folium


      Итак, нам хотелось бы получить не только навигацию, но и инфоокна на карте. А еще получить возможность публикации визуализации на своем сервере или на github. Нам поможет folium.


      Важное отступление

      Библиотека folium — довольно специфичная штука. Она представляет собой python-обертку вокруг JS-библиотеки Leaflet, которая как раз и отвечает за картографическую визуализацию. Следующие манипуляции выглядят не очень pythonic, но не пугайтесь, я все поясню.


      import folium

      Простая визуализация наподобие Carto делается достаточно просто.
      Что происходит?


      • мы создаем инстанс карты, m, с центром в выбранных координатах;
      • добавляем инстанс картограммы (choropleth)
        В инстансе картограммы мы задаем много атрибутов:
      • geo_data — геоданные, мы конвертируем данные нашего датафрейма в geojson;
      • name — задаем имя слоя;
      • data — непосредственно данные, их мы выбираем тоже из датафрейма;
      • key_on — ключ для соединения (обратите внимание, в geojson все атрибуты сложены в отдельный элемент, properties);
      • columns — ключ и атрибут для раскрашивания;
      • fill_color, fill_opacity, line_weight, line_opacity — цветовая шкала заливки, прозрачность заливки, ширина и прозрачность линий;
      • legend_name — заголовок легенды;
      • highlight — добавление интерактива (подсветки при наведении и приближения при клике) у объектов.

      Цветовая шкала основывается на библиотеке Color Brewer. Я крайне рекомендую при работе с картами пользоваться ей.


      m = folium.Map(location=[55.764414, 37.647859])
      m.choropleth(
          geo_data=full_gdf[['okrug', 'geometry']].to_json(),
          name='choropleth',
          data=full_gdf[['okrug', 'voters_oa']],
          key_on='feature.properties.okrug',
          columns=['okrug', 'voters_oa'],
          fill_color='YlGnBu',
          line_weight=1,
          fill_opacity=0.7,
          line_opacity=0.2,
          legend_name='type',
          highlight = True
      )
      m


      Итак, у нас получилась интерактивная картограмма. Но хотелось бы и инфоокон...


      Здесь нам придется немного хакнуть библиотеку. У нас есть партии-победители в каждом ТИК. Для каждой из них мы определим базовый цвет. Но не в каждом округе победа партии означает 100% голосов. К каждому базовому цвету мы определим 3 градации: абсолютная власть (100%), контрольный пакет (>50%) и кооперация (<50%). Напишем функцию определения цвета:


      def party_color(feature):
          party = feature['properties']['top1']
          percent = feature['properties']['top1_percent']
          if party == 'Единая Россия':
              if percent == 100:
                  color = '#969696'
              elif 50 < percent < 100:
                  color = '#bdbdbd'
              else:
                  color = '#d9d9d9'
          elif party == 'Яблоко':
              if percent == 100:
                  color = '#78c679'
              elif 50 < percent < 100:
                  color = '#addd8e'
              else:
                  color = '#d9f0a3'
          elif party == 'КПРФ':
              if percent == 100:
                  color = '#ef3b2c'
              elif 50 < percent < 100:
                  color = '#fb6a4a'
              else:
                  color = '#fc9272'
          elif party == 'Справедливая Россия':
              if percent == 100:
                  color = '#2171b5'
              elif 50 < percent < 100:
                  color = '#4292c6'
              else:
                  color = '#6baed6'
          elif party == 'Самовыдвижение':
              if percent == 100:
                  color = '#ec7014'
              elif 50 < percent < 100:
                  color = '#fe9929'
              else:
                  color = '#fec44f'
          return {"fillColor":color, "fillOpacity":0.8,"opacity":0}

      Теперь напишем функцию формирования html для инфоокна:


      def popup_html(feature):
          html = '
      Распределение мест в ТИК {}
      '.format(feature['properties']['okrug']) for p in ['top1', 'top2', 'top3']: if feature['properties'][p + '_elected'] > 0: html += '
      {}: {} мест'.format(feature['properties'][p], feature['properties'][p + '_elected']) return html

      Наконец, мы конвертируем каждый объект датафрейма в geojson и добавляем его к карте, привязывая к каждому стиль, поведение при наведении и инфоокно


      m = folium.Map(location=[55.764414, 37.647859], zoom_start=9)
      
      for mo in json.loads(full_gdf.to_json())['features']:
          gj = folium.GeoJson(data=mo, style_function = party_color, control=False, highlight_function=lambda x:{"fillOpacity":1, "opacity":1}, smooth_factor=0)
          folium.Popup(popup_html(mo)).add_to(gj)
          gj.add_to(m)
      m



      Наконец, мы сохраняем нашу карту. Ее можно опубликовать, например, на Github:


      m.save('tmp/map.html')

      Заключение


      С помощью простых инструментов визуализации геоданных можно найти бесконечный простор для инсайтов. А немного поработав над данными и визуализацией, можно успешно опубликовать ваши инсайты на Carto или на github. Репозиторий этот статьи: https://github.com/fall-out-bug/izbirkom_viz.


      Поздравляю, теперь вы политолог!

      Вы научились анализировать результаты выборов. Поделитесь инсайтами в коментах!

      Original source: habrahabr.ru (comments, light).

      https://habrahabr.ru/post/338554/


      Метки:  

      [Из песочницы] Как сделать карьеру в digital: первый год жизни интернет-маркетолога

      Понедельник, 25 Сентября 2017 г. 13:19 + в цитатник
      Mlle_booo сегодня в 13:19 Маркетинг

      Как сделать карьеру в digital: первый год жизни интернет-маркетолога

      Интернет-маркетинг манит хорошими зарплатами и драйвом, рынок развивается, спрос на хороших специалистов растет. Кроме того, искусственный интеллект и нейросети в ближайшем будущем не планируют отбирать у человечества эту профессию. Другими словами, не работа — мечта. Одна проблема: новичку сложно прорваться в digital даже несмотря на обилие курсов и материалов. Работодатели не готовы наслаждаться ошибками новоиспеченного диджитальщика и терять в деньгах.

      Рассказываем, как пережить первый год в онлайн-маркетинге и не сломаться.



      Путь интернет-маркетолога по мнению экспертов в интернете выглядит следующим образом:


      1) Получите высшее образование по маркетингу, научитесь делать SWOT-анализ и работать с матрицей BCG
      2) 5 лет оптимизируйте сайты, станьте гуру контекста, выучите PHP, HTML и JavaScript, научитесь писать не хуже Максима Ильяхова, получите сертификат на знание Яндекс.Метрики
      3) Поздравляем! Вы — интернет-маркетолог!

      План амбициозный, но увы, бестолковый. Он оставляет больше вопросов, чем ответов: с чего начать, как выбрать специализацию и где практиковать навыки?

      Чтобы помочь новичкам выжить в безжалостном мире digital, мы с Елизаветой Целютиной, интернет-маркетологом бизнес-школы IHSBM, составили «маршрут развития». Начиная стажером, Лиза прошла диджитал-интенсив по ирландской программе DMI и «без отрыва от производства» тут же тестировала новые знания в своей компании. Надеемся, этот опыт поможет джуниорам за короткий срок пройти путь с нуля до перспективного специалиста по онлайн-маркетингу.

      Шаг 1. Обучение


      Не слушайте старообрядцев, которые рекомендуют начинать с классического маркетинга: в одно ухо влетит, в другое вылетит. В первый год никто вам не доверит составлять SWOT-анализ и выводить новый продукт на рынок. Вернитесь к этому чуть позже.

      Если выбирать самый короткий путь в профессию, лучше все-таки разориться на хороший курс по digital-маркетингу (IHSBM, Нетология, Digital October). Лучшие инвестиции — инвестиции в себя, не жадничайте на знаниях, это ваш карьерный фундамент.

      Специализация — дискуссионный вопрос. По нашим наблюдениям, быстрее растут те диджитальщики, которые выбрали путь «всего по чуть-чуть». Поэтому если у вас нет твердого убеждения всю жизнь посвятить только контекстной рекламе, лучше выбрать «обзорный курс», в котором изучаются все аспекты интернет-маркетинга. Кроме того, только попробовав каждый инструмент digital-маркетинга, сможете понять, к чему лежит душа — SMM, планирование кампаний или SEO-хардкор.

      Что касается бесплатного контента, доступного в интернете. Он поможет, если у вас уже есть в голове база и система. Для комплексной самостоятельной подготовки с нуля бесплатные вебинары-семинары, блоги и книги не подходят, но могут еще больше запутать и разочаровать.



      Зато во время обучения стоит подписаться на проверенные источники: специализированные СМИ (Cossa, vc.ru, Seonews), блоги известных агентств (Texterra, Ingate, i-Media, Adventum, IT-agency). Задача — нарастить вокабуляр. Вас должны перестать пугать бесконечные CPC, CTR, ARPU и LTV. Отдельное внимание уделяйте изучению кейсов. Чтобы проникнуться целями и задачами интернет-маркетинга, читайте и впитывайте, как компания N с помощью рассылок повысила продажи на 15%, а стартап X с помощью социальных сетей привлек 1000 лидов.

      Шаг 2. Идем практиковаться!


      Итак, у вас голова трещит от новых знаний, а на стенке висит сертификат. Пришло время практики. Составляем резюме на должность ассистента. Лайфхак: лучше делать не одно универсальное, а каждое затачивать под конкретную вакансию. Да, это дольше, чем бездумно жать на кнопку «Откликнуться», но и выхлоп больше.

      Если нет цели попасть в консервативную компанию с сотрудниками в галстуках, можно составить резюме в неформальном виде. Добавив щепотку творчества и оригинальности, не забудьте про свой серьезный подход к делу. В этой сфере ценится креативность, особенно для SMM и email-маркетинга, которую вы таким способом и продемонстрируете. Никому не хочется пичкать потребителя сухими и монотонными постами. Возможно, так вы избавите себя от тестового задания.

      Есть мнение, что лучше и быстрее можно стартовать в агентстве, однако мы не будем так категоричны. Самое главное и неожиданное — на собеседовании вы должны задать очень много вопросов! Не смотрите пока на соцпакеты, корпоративные плюшки и даже зарплату. Выиграв бесплатные завтраки и оклад на 10 тысяч больше сейчас, вы рискуете целый год проверять договоры с подрядчиками или делать бесконечные ресайзы баннеров. Интернет-маркетинг у каждого разный.

      Так как нам нужен быстрый старт в digital-маркетинге, ищите компании с разноплановыми проектами. Выбрав должность ассистента по SEO или помощника по контекстной рекламе, вы будете долгое время выполнять механическую работу, решая однотипные задачи. Да, вы быстро вырастете в одной сфере. Однако потом вам будет трудно получить работу с более широким кругом обязанностей. Как минимум по зарплате вы не сможете сильно вырасти.

      image

      Итак, вот список вопросов, который можете смело брать на собеседование, чтобы приятно удивить рекрутера.


      1) Отдел маркетинга или команда проекта

      Узнайте, как можно больше об отделе интернет-маркетинга, уточните, какие задачи решаются штатными силами, какие — аутсорсом. Что получается сейчас хорошо, а где провалы или проблемы, попробуйте уточнить их причины. Может, до вас уже три человека сгорели на этих участках еще на испытательном сроке, и вы следующая жертва, просто потому что компания сама не знает, чего хочет?

      2) Бизнес-процессы и ИТ-составляющая

      Интересные маркетинговые задачи требуют системного подхода и грамотного управления проектами. У команды должны быть инструменты для обсуждения и планирования работы. Используют ли Trello, Меgaplan, Worksection, Basecamp? А как насчет Scrum и Agile? Современный маркетинг не работает без ИТ-решений: нагло выясняйте, какую CRM используют в компании, какой сервис для рассылок, настроена ли Яндекс.Метрика и на каком движке сделан сайт. Чем шире применяемый инструментарий, тем интереснее и продуктивнее будет работа.

      3) Бюджет на маркетинг или средний бюджет проекта

      Все просто: он должен быть. Да, на рынке есть мастера по партизанскому маркетингу, но начинать лучше в проектах, где есть маркетинговые бюджеты, иначе массу эффективных приемов привлечения аудитории вы так и не распробуете. Новичку надо «понюхать» проекты хотя бы с небольшими бюджетами, чтобы почувствовать возможности онлайн-маркетинга.
      «С деньгами любой дурак сможет» — эта присказка скорее про PR, потому что в маркетинге работает другой подход: каждый вложенный рубль должен принести 10 рублей прибыли.

      4) Как растут и обучаются сотрудники?

      В компании внедрена система наставничества — сразу два жирных плюса в её пользу. Если нет, спросите у HR-менеджера, часто ли сотрудников отправляют на обучение, принято ли ездить на профильные конференции? В таких компаниях вы будете расти в два раза быстрее, да и веселее, пожалуй.

      5) Какие планы на ближайшее будущее у компании в интернете?

      Если она довольна своей контекстной рекламой и почтовыми рассылками, и ничего больше не планирует, значит, вы будете работать роботом. А как же ваша мечта сделать мир лучше? На рынке полно компаний, которые только сейчас открывают для себя новые соцсети, мессенджеры, чат-боты, блоги, видео-стриминг, с помощью которых делают свои продукты и обслуживание по-настоящему увлекательными и полезными. Вот, где нужно реализовываться!

      Не сдавайтесь и не снижайте планку, если на первых собеседованиях вы встретили полнейшее недоумение.

      Значит, у компании нет ресурсов на реализацию интересных и качественных проектов, хорошего опыта вы здесь не получите. Работа просто на стаж в digital — смертный приговор для карьеры.

      Осторожнее относитесь к фармацевтической, алкогольной и табачной отрасли — здесь работают очень специализированные инструменты. Избегайте сложных b2b-компаний: тут тоже специфический маркетинг, традиционные онлайн-инструменты работают пока плохо. Не ставьте крест на этих сферах насовсем — нет, просто с наработанным опытом там будет проще.

      Идеально: ИТ-компании и e-commerce, digital-агентства полного цикла, услуги (трэвел, ивенты, развлечения, недвижимость, фитнес).

      Шаг 3. Практика и специализация


      Итак, вы ассистент отдела маркетинга или junior digital specialist. Если вам выделили наставника — поздравляем, это счастливый билет. Но увы, в российских компаниях такая практика еще большая редкость.

      В остальных случаях, старайтесь держать под контролем ваше развитие в компании. Это не так трудно, как кажется — рассказывайте, какими задачами вам интересно заниматься, проявляйте инициативу, задавайте вопросы коллегам.

      Если есть возможность, сразу начинайте с социальных сетей. Изучите, как устроены аккаунты компании, какой контент там публикуется, какие вопросы задают подписчики. Проследите, в какой форме общались с подписчиками: было ли это «Хай, котики» или же «Здравствуйте, уважаемые подписчики»? Параллельно погружайтесь в бизнес, тщательно промониторьте конкурентов. При должной сноровке у вас на это уйдет 1-2 недели.

      Далее подключайтесь или просите присоединиться к e-mail маркетингу. Изучив прежний опыт, предложите написать текст рассылки, освойте сервис, поверстайте в нем тестовые письма. Особое внимание уделите теме письма — текст рассылки может быть бесконечно хорош, но какой в этом толк, если тема не сработала, и Open Rate низкий. На это уйдет еще неделя.

      Теперь подступайте к контексту. Посмотрите, как настроены текущие рекламные кампании. Изучите, какие установлены таргетинги по времени и географии, как выставляются ставки, какие используются минус-слова. Нашли ошибку? Не стесняйтесь предлагать свои рекомендации. Попросите потренироваться в составлении семантического ядра для новой услуги или акции. На изучение аккаунтов Директа и AdWords заложите неделю.

      Пошла тяжелая артиллерия: теперь можно залезть в SEO. Посмотрите, что на вашем сайте из контента работает на благо выдачи, как устроена навигация, есть ли блог, по каким ключевым словам продвигается ваш сайт, какие пункты из внешней оптимизации выполнены. И опять не спускаем глаз с конкурентов. Недели должно хватить.

      И вот только сейчас пришло время аналитики. Изучите, что и как настроено в личном кабинете, какие выставлены цели, какие цифры собираются. Проштудируйте отчеты, загляните в вебвизор. Аналитика — один из самых жизненно важных разделов онлайн-маркетинга. С ее помощью вы узнаете, какая часть рекламного бюджета тратится впустую, а что приносит максимальный эффект.

      К концу испытательного срока у вас должно быть четкое представление об эффективности каналов, стоимости лидов, системе распределения бюджета. Кроме того, вы должны знать ваших конкурентов и ЦА почти в лицо!

      За следующие полгода вам надо научиться оптимизировать маркетинговый бюджет, анализируя стоимость одного клика, лида или покупки по всем каналам. Так вы сможете на каждый вложенный рубль генерировать не 10 рублей прибыли, а 20 или даже 30. Такие успехи руководство не оставляет без внимания и поощряет премиями.

      image

      Вот еще 4 совета, чтобы хорошо себя зарекомендовать в компании


      • На самой начальной стадии с особой осторожностью беритесь за задачи на креатив и планирование (идеи для рассылок, посты для социальных сетей, темы объявлений). Чтобы не изобретать велосипед, всегда выясняйте, что уже было сделано, и главное, что из этого получилось, а что — нет.

      • На первых порах вам может показаться, что в компании все устроено не так идеально, как рассказывали на курсах. Не спешите критиковать или опрометчиво советовать, как надо делать. Первые два месяца работы — не время для радикальных предложений. Запишите свои наблюдения и вернитесь к ним спустя некоторое время. И помните, что любой спорный вопрос можно решить с помощью А/В-тестирования.

      • Составляйте чек-листы и проверяйте свои действия — новички часто по невнимательности забывают поставить какую-нибудь галочку. А одна такая галочка, например, по геотаргетингу в контекстной рекламе, может очень серьёзно сказаться на качестве трафика.

      • Научитесь грамотно и интересно писать! Вам необязательно выдавать креативные слоганы, достойные каннских львов, но писать хорошие тексты для блога, рассылок, социальных сетей вы должны! При этом не забывайте, что для каждой компании нужна своя стилистика.

      • Учитесь, учитесь и еще раз учитесь! Digital — это развитие нон-стоп: новинки, лайфхаки и тренды. За этим нужно следить и успевать оперативно внедрять, пока вас не опередили конкуренты. Составьте план по обучению — вот теперь пришло время для блогов, книг, вебинаров.

      Где-то через 9-12 месяцев упорного труда, начинайте думать о своих KPI. Зарплата хорошего маркетолога складывается из оклада и бонуса, который высчитывается по ключевым показателям. Чаще всего это новые лиды-заявки. Учитесь работать на результат — это здорово мотивирует, не даст вам заскучать и затормозить в развитии. Да и фраза: «Я получил большее за меньшую цену» для работодателя лучше любой вашей «корочки».

      Если год в компании прошел, а вы до сих пор занимаетесь только мониторингом СМИ, ответами на комментарии клиентов в соцсетях, размещением новостей на сайте и бумажками — это повод серьезно задуматься. Кажется, что вы занимаетесь фигней? Меняйте компанию. Подумайте о специализации. Может, за время работы у вас появился фаворит — тогда стоит отдать ему предпочтение и посвятить год-два оттачиванию навыка в какой-то одной сфере, например, SMM или видео-блогинг. Подтяните теорию, подбейте резюме цифрами — и снова в бой!
      Original source: habrahabr.ru (comments, light).

      https://habrahabr.ru/post/338662/


      Метки:  

      HR-робот обзванивает тысячи людей одновременно: рассказываем, как

      Понедельник, 25 Сентября 2017 г. 13:05 + в цитатник
      glagoleva сегодня в 13:05 Разработка

      HR-робот обзванивает тысячи людей одновременно: рассказываем, как

        Хабр, привет. Период отпусков закончился, так что вливаемся в работу. Мы работаем и пишем, вы — читаете и тоже работаете (надеемся, и с нашей помощью тоже). Сегодня хотим поделиться еще одним кейсом – расскажем о нашем сотрудничестве с Роботом Верой. Эта компания помогает подбирать персонал для тех, кому нужны новые кадры. Частичная (и эффективная) автоматизация процесса рекрутинга не отменяет того, что нужно много звонить. Здесь на помощь Вере приходим мы – на Voximplant компания автоматизирует телефонный дозвон кандидатам.


        Что делает автоматика в HR?


        Робот Вера – настоящая помощница hr-менеджера. Начинается все с задания клиента – найти специалиста в определенной сфере, соответствующего ряду критериев (тут ничего необычного – регион, образование, уровень английского, гибкость графика, опыт работы, возраст, пол). Для работы клиентам предоставляется уютный и минималистичный кабинет (понятно, виртуальный), где формируется задание. Как только заказ готов, отдается приказ роботу, и начинается процесс поиска.

        Вера (голос женский, имя – тоже, поэтому по тексту ниже — «она») ищет резюме на крупнейших сайтах по работе. Затем она в автоматическом режиме обрабатывает данные по соискателям, чьи анкеты размещены на таких ресурсах.

        Обработав базу резюме, Робот Вера приступает к обзвону. Если вызов по какой-то причине не прошел (отключен телефон, занято, сбой сети), то она пытается “достучаться” по другим каналам – например, email. На усмотрение работодателя, письмо может содержать, помимо описания вакансии, ссылку для отклика, ссылку для прохождения видеоинтервью и номер телефона.

        Как рассказала нам команда Веры, 50% откликов на вакансии они получают по телефону. Оставшиеся 50 распределяются по остальным каналам привлечения кандидатов — лидогенерирующая страничка в интернете, email. Давайте рассмотрим, как устроен самый эффективный канал в рекрутинге.

        Зима Сингулярность близко!


        Итак, Вера звонит соискателю. Если на том конце снимают трубку, она представляется и спрашивает, желает ли кандидат на вакансию получить информацию о работе. Если ответ положительный, робот кратко рассказывает об условиях (зарплата, время работы, наличие печенек в буфете) и снова спрашивает, интересна ли эта вакансия собеседнику. Если ответ — снова “да”, то Вера сообщает, что соискателю перезвонит уже менеджер-человек для подробного обсуждения условий. И это лишь один вариант общения по телефону, самый простой. Большой плюс системы в том, что алгоритм “вопрос-ответ” можно изменять, усложнять или упрощать.

        Спама, кстати, нет. Если кандидат говорит, что он не ищет работу, звонить не будут.

        В процессе звонка задействованы технологии Voximplant. Вера анализирует звонок и предоставляет своим клиентам отчет.

        Вся система телефонной связи у робота — это задача Voximplant. При этом компания может делать со звонками все, что захочет: облачные сценарии Voximplant пишутся на JavaScript и любой веб разработчик может сделать сложную телефонию за пару часов. В команде Веры экспериментируют и прикручивают “свой” синтез голоса. Модульность – это всегда хорошо, правда-правда. Добавить или убрать что-то к основной платформе Voximplant может любой программист.

        Что касается распознавания голоса Верой, то здесь бывают проблемы, винить в которых нельзя ни нас, ни ее. Практически любая современная система, которая занимается распознаванием речи, не справляется с сильными сторонними шумами. Проезжающие машины, разговоры коллег во время звонка звуки — все это очень мешает. Так что из 100% звонков около 80% оказываются успешными, в 20% наблюдаются проблемы. В случае обнаружения проблемного звонка на помощь приходит email-рассылка.

        Положительный момент — это то, что потенциальные кандидаты, которые по какой-то причине не были на связи во время телефонного звонка Веры или не увидели e-mail, могут связаться с ней позже, и работодатель увидит отклик.

        И еще один момент (не менее положительный) — при помощи Voximplant наш партнер настроил обработку входящих звонков таким образом, что когда кандидат перезванивает, его номер распознается и Вера говорит ему именно то, что предназначено для этого человека, интересующегося конкретной вакансией.

        Очень упрощенный пример кода, как это сделать:


        Синтез речи у Веры — свой. При желании клиент может указать роботу общаться с потенциальными соискателями голосом с нейтральными интонациями, есть еще положительная интонация, добрая и радостная.

        Кроме Веры сейчас с клиентами работает и робот Захар — то есть, добавлен и мужской голос. У Веры и Захара только голоса разные, суть – одна.

        Почему Voximplant, а не Asterisk?


        Мы любим, когда все работает, как часы — и без ложной скромности можем сказать, что с Voximplant так все и обстоит. Сотрудничество с Верой продолжается уже несколько месяцев, и за это время у компании не было проблем и сбоев.

        Команда робота начала работать с нами уже после того, как интегрировала в свою платформу Asterisk. Почему? Работать он работал, но с платформой часто что-то случалось. Приходилось вызывать специалиста по Asterisk для решения мелких и крупных проблем, включая отсутствие звонков. Проблемы были частым явлением, а специалист мог отсутствовать, заболеть, он мог быть занят. Само собой, простой не нравился клиентам и ухудшал общую эффективность работы. С Voximplant у ребят ничего такого пока не случалось, и мы надеемся, что и не случится. Во всяком случае, мы делаем все для этого.

        Вторая причина, которая побудила обратиться к нам — возможность работы с JavaScript. Если возникает проблема, нужно добавить, изменить или убрать что-то, то, не требуется вызывать специалиста откуда-то, все можно сделать своими силами.

        Переход с Asterisk осуществлялся поэтапно, чтобы не было ничего вроде «в омут с головой». Сначала ребята добавили звонки из браузера — всего за один вечер был добавлен соответствующий функционал. После того, как эта функция хорошо себя показала, последовали и другие этапы интеграции.

        Вера, вперед! Чемпионские результаты робота


        Вера (и Захар, да) работают очень эффективно. О процессе их работы дольше писать, чем все происходит в реальности. Обзвон соискателей Верой по заданию конкретного клиента, то есть сформированной в кабинете заявки, выполняется всего за полтора часа. Средняя конверсия поиска кандидатов — 15%. То есть из 100 человек, которых нашел робот, остается 15, которые соответствуют всем критериям и сами заинтересованы в сотрудничестве.

        Сейчас робот Вера делает за день в 10 раз больше звонков, чем профессиональный рекрутер. Это подтверждает не только собственный опыт, но и обратная связь от клиентов.

        Единовременно Робот обзванивает десятки тысяч человек. В день число звонков достигает 30-50 тысяч. С начала работы Вера провела 2300 интервью с кандидатами на разные позиции, совершила 440000 звонков соискателям, обработала миллионы анкет на работных сайтах.

        Об эффективности работы робота можно судить и по результатам, полученным компанией МТС после формирования заказа по поиску сотрудников. Компания получила 5000 откликов за месяц. Вера совершила более 40000 звонков, отправила 37000 сообщений электронной почты, более 100 кандидатов прошли видео-интервью.

        В общем и целом, у нас с Верой получился отличный симбиоз технологий, куда входят распознавание голоса, звонки, синтез речи, работа с Big Data и многое другое. Определить, где заканчивается Voximplant и начинается робот, непосвященному человеку реально сложно. И главное — все это работает!
        Original source: habrahabr.ru (comments, light).

        https://habrahabr.ru/post/338660/


        Метки:  

        «Человек» искусства: способен ли искусственный интеллект творить?

        Понедельник, 25 Сентября 2017 г. 12:58 + в цитатник
        IgorLevin сегодня в 12:58 Разработка

        «Человек» искусства: способен ли искусственный интеллект творить?

          С развитием нейросетей им придумывают всё более разнообразные способы применения. С их помощью обучаются автопилоты Tesla, а распознавание лиц используется не только для обработки фотографий приложениями типа Prisma, но и в системах безопасности. Искусственный интеллект учат диагностировать болезни. В конце концов, с его помощью даже выигрывают выборы.

          Но есть одна сфера, которая традиционно считалась принадлежащей исключительно человеку — творчество. Однако и это утверждение начинают ставить под сомнение. Ли Седоль, проигравший AlphaGo, признался: «Поражение заставило меня засомневаться в человеческой креативности. Когда я увидел, как играет AlphaGo, то усомнился в том, насколько хорошо играю сам». Поэтому в сегодняшнем посте давайте поговорим о том, способны ли роботы ступить на территорию искусства, в пространство креативности, а значит эмоций и восприятия.

          / Flickr / franck injapan / PD

          Творчество роботов


          Самообучающиеся системы давно начали проверять на креативность. Например, в 1970 году ученые разработали алгоритм, который мог писать прозаические тексты — правда, довольно бессмысленные.

          С тех пор нейросети научились рисовать картины, сочинять музыку и стихи, а также придумывать сценарии к фильмам. Яндекс учит нейросети записывать музыкальные альбомы, похожие на альбомы популярных групп, и писать стихи в стиле Егора Летова. Принцип действия всех алгоритмов похож: они анализируют огромный массив произведений искусства, а потом на основе полученных закономерностей «создают» свое творение: картину, музыкальную композицию, роман и т. д.

          Творчество нейросетей постепенно институционализируется. Так, в 2016 году впервые прошел конкурс художественных произведений, созданных роботами. В этом году главный приз в 40 тыс. долларов выиграл алгоритм PIX18, придуманный Creative Machines Lab: его похвалили за хороший мазок и умение генерировать произведения на базе фотографий, находящихся в его распоряжении.

          Так прокомментировали победу в комитете: «Композиция и работа с кистью напоминает Ван Гога. Интересная палитра». Это выглядит как настоящая критика картины начинающего художника.

          Восприятие произведений


          Картины, созданные алгоритмом DeepDream от Google, считаются практически искусством — в первую очередь именно потому, что их создал искусственный интеллект.

          Однако есть другой важный вопрос — новизна. По этому критерию мы оцениваем и творения художников. Если алгоритмы не срисовывают или не обрабатывают фотографии, а, например, пишут абстрактные картины, могут ли они действительно создать что-то новое?

          На этот вопрос попробовали ответить разработчики из Лаборатории искусственного интеллекта и искусства Ратгерского университета, создав генеративно-состязательную сеть (GAN). Ранее алгоритм учился на основе ответов одного дискриминатора: анализировал картины, рисовал свои и сверял результат. Он продуцировал изображения, похожие на те, что изучал до того.

          Команда сделала следующий шаг в развитии сети и добавила второй дискриминатор, соревнующийся с первым. Теперь нейросеть анализирует примерно 81 тысячу картин и, опираясь на такую объемную выборку, формирует список условий, при которых созданная картина может быть отнесена к произведениям искусства. Параллельно второй дискриминатор составляет список стилей и проверяет картину на схожесть с ними – проводит операцию по верификации. Новая картина рождается тогда, когда изображение признается произведением искусства, не идентичным ни одному из существовавших ранее стилей.

          Кроме того, нейросети уже способны создавать мульфильмы. Компьютерная программа The Painting Fool, разработанная Саймоном Колтоном (Simon Colton) из Имперского колледжа Лондона, смогла сплести нарисованные ею же изображения в видеоряд.




          Система адаптирует специальные решения для автоматизированного создания коллажей, симулируя мазки кисти на холсте. ПО в состоянии имитировать техники рисования, для чего используются возможности многоядерных процессоров — каждый поток контролирует отдельную кисть. Это позволяет «смешивать» кисти в непредсказуемых комбинациях, что приводит к более правдоподобному эффекту.

          Сам процесс рисования – например, портрета – начинается с разметки областей заинтересованности: глаз, рта, бровей и т. д. Для каждого «региона» программа сегментирует изображение методом neighbourhood-growing и обосновывает границы. Затем The Painting Fool занимается окраской каждого сегмента. Он может «рисовать» карандашами, пастелью, акварелью и мелками, учитывая освещенность, условия среды.

          И The Painting Fool — лишь показательный пример, только один представитель «компьютеров искусства». Причем их количество постоянно увеличивается. Одному роботу удалось настолько очаровать аудиторию музыкальной композицией, что они решили, будто её написал человек. А короткий роман, написанный японским роботом, чуть не выиграл литературную премию.

          Это подводит нас к важному вопросу — проблеме рецепции искусства смотрящим. Существует ли разница между нашим восприятием произведения, созданного человеком, и тем, что «сгенерировано» роботом? В интернете есть сайт Bot or Not, предлагающий угадать, кто написал то или иное стихотворение — бот или человек. Ответ не всегда очевиден. Это неоднозначная территория.

          На сайте Bot or Not есть стихотворения, написанные роботами – при том, что люди отнесли их к авторству человека. Соответственно, можно считать, что эти алгоритмы прошли тест Тьюринга для поэзии. Компьютер должен убедить 30% людей в своей «человечности», чтобы пройти тест. Но писатель Оскар Шварц, создатель Bot or Not, отмечает что это игра не в одни ворота: мы не только можем перепутать написанное ботом с работой человека, но и наоборот — принимаем творчество людей за творчество роботов. Возникает смешение уровней, новое понимание текстов и смыслов, где стирается грань между иллюзией и аутентичностью в привычном нам виде.

          Творчество — это эмоциональное воздействие


          Здесь встает еще одна проблема, связанная с сутью художественного произведения: чем оно отличается от копирования и воспроизведения прошлого опыта.

          Американский психолог Колин Мартиндейл (Colin Martindale) предложил оригинальную теорию креативности. Согласно его исследованиям, первоочередная цель творца — вызвать в потребителе эмоциональное возбуждение. Этого можно достичь разными средствами: новизной, сложностью идей, интеллектуальным вызовом, двусмысленностью и неоднозначностью трактовок и посылов. Социум, в котором уровень возбуждения перестает расти (или начинает убывать) — деградирует.

          Мартиндейл выделил два этапа познавательного процесса. Первичный процесс — ненаправленное, иррациональное мышление вроде сновидений или мечтаний. Вторичный процесс — осознанный, концептуальный, это решение конкретных задач и использование логики. Похожую оптику он приложил и к творческому процессу: концептуальное сознание может различать, может логически мыслить, но оно не способно создать или вывести что-то, чего не знало раньше, ex nihilo nihil fit — «из ничего ничто не происходит». Изначальное мышление может проводить аналогии, выстраивать цепочки ассоциации и сравнивать, порождая новые комбинации ментальных элементов. Оно производит сырье, которое концептуальное мышление может обработать.

          По близкому принципу работает описанная выше сеть GAN — одна нейросеть «различает», другая — «сравнивает и находит ассоциации». Алгоритм следует теории креативности, производит новые полотна, провоцирующие эмоциональный отклик в людях.

          Нейросети — в помощь художнику. И музыканту


          Искусство и технологии всегда пересекались и подпитывали друг друга (достаточно вспомнить об эпохе Возрождения, об экспериментах Леонардо и Микеланджело). Новые материалы, подходы и изобретения часто позволяли художникам создавать шедевры и целые виды искусства. Так и сегодня, помимо самостоятельного «производства» поэм, картин и музыки, нейросети помогают ученым проводить исследования в творческой сфере.

          Развитие современной музыкальной индустрии ориентируется на классифицированные паттерны, помогающие в буквальном смысле построить математическую модель музыки и «запрограммировать» желаемый эффект от прослушивания композиции.

          Международная исследовательская группа из университетов Японии и Бельгии в сотрудничестве с компанией Crimson Technologies выпустила специальное устройство на базе машинного обучения, умеющее выявлять эмоциональное состояние слушателей и генерировать на основании собранной информации принципиально новый контент.

          «Как правило, машины и программы для создания песен зависят напрямую от автоматических композиционных систем, их предварительно заданный и хранящийся объем готового музыкального материала позволяет сочинять только похожие между собой треки», — отмечает профессор Масаюки Нумао (Numao Masayuki) из Университета Осаки.

          Нумао и его команда хотят предоставить «машинам» информацию об эмоциональном состоянии человека. По их мнению, это должно способствовать увеличению интерактивности музыкального опыта. Ученые провели эксперимент, во время которого испытуемые слушали музыку в наушниках с сенсорами активности головного мозга. Сведенные воедино данные ЭЭГ транслировались роботу-композитору. В результате удалось выявить большую вовлеченность и более интенсивную эмоциональную реакцию слушателей на определенную музыку.

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

          Искусство видеть


          Джон Бергер (Бёрджер) в «Искусстве видеть» отмечал, что зрение для человека первично по отношению к языку. Знание влияет на нашу оценку. По Бергеру, любое изображение — это просто один из многочисленных способов видения, но наше восприятие изображения зависит от того, каким способом видения пользуемся мы.

          Поэтому дискуссия о творчестве алгоритмов мотивирует нас задуматься не только о том, как «творят» программы, но и о том, как мы сами воспринимаем творчество. Нейросети могут писать стихи, а мы порой путаем их с человеческими: но именно наше восприятие, наше прочтение наполняет их смыслом. Например, для алгоритма слова, мазки, цвета и звуки являются всего лишь набором знаков, которые он может сложить в гармоничную структуру. Это сырье, за которым робот не видит содержания, смыслового поля. По крайней мере, пока не видит.

          Роботы не могут придать предметам значение, а произведениям — глобальную культурную ценность. «Никогда еще не писалось так много стихов и так мало поэзии», – читаем у Умберто Эко. ИИ может создать гениальную симфонию или сочетание рифм, правильно организованных графически, но лишь признание человека позволит всему этому обрести столь желаемый многими статус – действительно быть искусством, а не казаться им.
          Original source: habrahabr.ru (comments, light).

          https://habrahabr.ru/post/337624/


          Метки:  

          Ближайшие сто лет в ста словах + конкурс с розыгрышем приглашения на Хабрахабр

          Понедельник, 25 Сентября 2017 г. 12:39 + в цитатник

          Метки:  

          Как чат-боты помогают выстраивать омниканальный опыт

          Понедельник, 25 Сентября 2017 г. 12:19 + в цитатник
          LiveTex сегодня в 12:19 Маркетинг

          Как чат-боты помогают выстраивать омниканальный опыт

            Практически каждый человек, у которого сегодня есть смартфон, хоть раз использовал чат-бота. Со стороны пользователя боты выглядят как простые программы в мессенджерах, которые могут решать элементарные задачи с помощью текстовых сообщений. Например, помогут найти и заказать авиабилет или подобрать нужный лук в интернет-магазине. А вот как выглядит все это с другой стороны баррикад, как бизнес внедряет в свою работу чат-ботов и какую реальную пользу они могут ему принести? Все ли так просто?

            Тотальная цифровизация и омниканальность


            Бизнес всё больше становится клиентоцентричным, а цифровая трансформация модифицирует бизнес-процессы и подходы к клиентскому сервису – появление новых технологий позволяет потребителю и его ожиданиям определять развитие компании или бренда.

            Омниканальность – одна из главных составляющих цифровизации бизнеса. У
            этого понятия множество определений, но наиболее точно выражает его суть такое: бесшовное взаимодействие бренда с клиентами, позволяющее совершать непрерывный клиентский путь, используя разные платформы. При омниканальности все каналы объединены вокруг пользователя. Клиент, совершая customer journey, может безболезненно переключаться между каналами, и при этом получать персонализированное обслуживание. Главная цель омниканальности – создание позитивного клиентского опыта.

            По данным Gartner к 2018 году половина компаний постарается так изменить бизнес-модель, чтобы улучшить свой пользовательский опыт. А к 2020 году пользовательский опыт обгонит по своей значимости для потребителя цены и качество самого продукта (Walker).

            Омниканальность – это ключ к первоклассному клиентскому опыту, а, следовательно, и повышению прибыльности бизнеса. По данным Форбс сегодня организации используют в среднем 35 (!) разных систем для сбора данных, которые практически не интегрированы между собой. Получается, что большинство компаний не умеют идентифицировать клиента и не понимают механики его customer journey по различным цифровым каналам.

            Современным же пользователям, а особенно миллениалам и поколению Z, при общении с брендами важны:

            1. мгновенная и адекватная обратная связь
            2. качественный клиентский сервис онлайн
            3. бесшовное взаимодействие (омниканальность)
            4. понятный и удобный интерфейс, возможность заказать и оплатить товар или услугу в пару кликов.

            Перед компаниями возникает ряд важных задач: уметь идентифицировать и отслеживать путь клиента, формировать пул данных о нем и очень быстро отвечать на пользовательские запросы в любых цифровых каналах.

            На этом пути есть несколько главных барьеров:

            — компании не могут самостоятельно работать с Big Data
            — не умеют идентифицировать клиента. Результаты исследования Dimension Data по всему миру показывают, что только 36% организаций могут отслеживать переходы между несколькими каналами.
            — не понимают, как автоматизировать работу клиентского сервиса

            Согласно eConsultancy потребители предпочитают получать поддержку в первую очередь по трем главным каналам: телефон, электронная почта и чат на сайте. Причем 61% из них не могут легко переключаться между каналами при общении со службой клиентской поддержки (Aspect). Проблему автоматизации клиентского сервиса и поддержки клиента при смене канала практически на 100% могут закрывать чат-боты.

            Чат-боты и омниканальность


            Бесшовное общение бренда с клиентами реализуется за счет пула различных технологий. Использование ботов помогает повышать эффективность клиентской службы, в первую очередь за счет маршрутизации и персонализации входящих обращений.

            По данным BI Intelligence, November 2016 чат-боты позволяют экономить до 30% ресурсов, пропорционально повышая и общую доходность бизнеса.

            image

            Сами по себе технологии теряют свою ценность для онлайн-пользователей, на первое место выходит качественный и быстрый клиентский сервис. Современный клиент желает получить ответ на свои вопросы здесь и сейчас, оперативно решить проблему, получить своевременную помощь или консультацию. При этом пользователь может заходить на сайт компании минимум с 3-5 устройств, и с нескольких аккаунтов из разных соцсетей. Однако он ожидает, что диалог с ним будет продолжен с того места, где он его закончил. Просьба операторов службы поддержки повторять несколько раз свои запросы вызывает только раздражение.

            Для того, чтобы оставаться эффективным, бизнес усиливает клиентскую поддержку: компании пользуются услугами аутсорсинговых контакт-центров (КЦ) или пытаются создавать их инхаус, но старая схема работы только с голосовыми обращениями уже не позволяет КЦ быть эффективными.

            Крупный бизнес использует корпоративные КЦ для информационной поддержки клиентов. Рабочий день обычных сотрудников ограничен, в то время как потребность в помощи у клиента может появиться в любой момент, будь то поздний вечер или выходной. Чат-боты же могут оказывать клиентскую поддержку 24/7. Соответственно и потребность в ботах будет только расти.

            По данным аналитической компании iKS-Consulting среднегодовой рост рынка аутсорсинговых контакт-центров до 2020 года останется на уровне 5,5 %.

            И второй момент – качественный клиентский сервис без обработки текстовых каналов сегодня невозможен. Текстовые каналы увеличивают долю в общем объеме коммуникаций, а это ставит задачу перед бизнесом уметь с ними эффективно работать. Для общения с компаниями 90% современных потребителей предпочитают использовать мессенджеры (по данным Twilio). Это один из ключевых моментов, на который делают ставку разработчики чат-ботов – автоматизация работы по обработке простых первичных обращений. Например, внедрение в службу поддержки бота каршеринга YouDrive помогло на 2/3 снизить число звонков в компанию.

            Большинство современных потребителей не против первых контактов с бизнесом при помощи чат-ботов. Чтобы быстро получить ответы свои вопросы, многие готовы взаимодействовать с чат-ботами. По сведениям emarketer задавать вопросы компаниям в онлайн-чатах и мессенджерах уже предпочитает треть всех активных интернет-пользователей.
            Веб-помощник Nina крупного шведского банка Swedbank ведет до 30 000 разговоров в мессенджере в месяц и может обрабатывать более 350 различных запросов клиентов. Такой уровень сервиса — это то, к чему стремятся многие компании.

            Известны цифры, согласно которым повышение качества обработки запросов клиентов на 10% может повысить эффективность продаж почти в 2 раза. Используя чат-ботов, можно добиться повышения качества клиентского сопровождения, не делая при этом дополнительных расходов на расширение штата отдела продаж.

            Есть еще один момент, о котором многие предпочитают не упоминать вслух — псевдоомниканальность. Очень часто поставщики IТ-решений или маркетологи говорят об омниканальном подходе и внедрении бесшовного взаимодействия в бизнес-процессы. На деле же компания или бренд просто научились работать с несколькими разными каналами коммуникаций. Клиент, общаясь с компанией, не получает никакого бесшовного опыта: при обращении клиента, менеджер компании не видит, о чем с клиентом общались его коллеги в другом канале.

            Для развития омниканального подхода необходимо, чтобы у руководства компании было четкое видение цифровой трансформации, а также сформированная стратегия работы в цифровых и текстовых каналах.

            Результаты исследования Dimension Data по всему миру показывают, что лишь 17% способны определить проблемные точки взаимодействия на пути клиента. Для большинства компаний составить карту переходов клиентов между всеми каналами и проанализировать проблемные точки клиентского опыта – непосильная задача. А именно эти места являются точками роста для проактивного цифрового общения с клиентами. Воплощать в жизнь сценарии проактивного взаимодействия с пользователями помогают умные чат-боты в связке с искусственным интеллектом и Big Data.

            Где и в чем бизнесу пригодятся боты?


            Согласно исследованиям businesswire.com сегодня самые перспективные отрасли для применения ботов связаны с клиентским обслуживанием. В первую очередь, это банковская сфера, страхование и e-commerce, а также бронирование билетов и туров.

            Чат-боты делают клиентское обслуживание более эффективным за счет автоматизации рутины — это одна из их сильных сторон. Чем более «умны» чат-боты, тем большую долю стандартных повторяющихся процессов они готовы взять на себя. Вот пара воодушевляющих примеров.

            В мобильном приложении H&M чат-бот задает клиенту вопросы о предпочитаемом стиле одежды. Собрав полную информацию, бот дает рекомендации по вещам, которые наилучшим образом подходят ко вкусам покупателя.

            В стартапе HelloAva бот работает как эксперт, который дает клиентам персональные рекомендации по уходу за кожей.

            А вот пример успешного применения продвинутого бота — умный чат-бот Зоряна. Его внедрил в свою работу украинский оператор связи Киевстар. В базе Зоряны 12 000 стандартизированных ответов на вопросы клиентов, бот помогает с решением 70% входящих вопросов. Он работает на сайте компании, в ее соцсетях и мессенджерах, помогает он и колл-центру оператора. Ссылка на Зоряну есть в стандартном голосовом меню, при желании пользователь может задать свои вопросы боту: 25% звонящих переходят из IVR к Зоряне. И только 10% из этих 25% потом снова перезванивают в КЦ. Это говорит о том, что бот прекрасно справляется с решением простых клиентских запросов, разгружает живых операторов и берет основную рутину общения с клиентами на себя.

            По прогнозам Market Research Future мировой рынок чат-ботов вырастет к 2023 году на 37% и достигнет оборота в 6 млрд долларов. Использование ботов для эффективного общения с клиентами в цифровых каналах, только набирает обороты и в ближайшем будущем откроет бизнесу широкие перспективы.

            Не смотря на радужные перспективы от внедрения ботов, которые усиленно подогревают в последнее время СМИ, есть в их применении ряд серьезных ограничений:

            • Бизнес не понимает, как внедрять ботов. У него нет сформированной стратегии работы в цифровых каналах или дорожной карты цифровой трансформации.
            • Разработка ботов оторвана от бизнес-процессов, а многие IT-вендоры стремятся поднять хайп, чтобы увеличить поток обращений.
            • В массовом секторе боты пока еще не умеют качественно распознавать голос и переводить его в текст. На рынке есть очень продвинутые решения, но стоимость их пока еще очень высока.
            • На рынке практически нет решений, которые можно легко интегрировать со сторонними платформами. Поэтому сложно внедрять чат-ботов в бизнес-процессы. Одним из пионеров таких решений является компания LiveTex, недавно выпустившая на рынок коммуникационное API, с помощью которого можно не только встроить бота в платформу для омниканального обслуживания, но и собирать статистику по всем обращениям из всех текстовых каналов и подсчитывать эффект от их внедрения.

            Что же в сухом остатке?


            Сегодня бизнес только экспериментирует c чат-ботами. Он применяет их для оптимизации служб клиентской поддержки и автоматизации рутинных действий. Для бизнеса привлекательность чат-ботов заключается в операционной эффективности и экономии средств, а также в управлении взаимодействиями с клиентами. Чат-боты могут стать конкурентным преимуществом, помогающим экономить время и деньги и повышающим лояльность клиентов.

            Прогнозы говорят, что рынок чат-ботов будет расти, а использование искусственного интеллекта сделает их гораздо «умнее» и позволит сильно расширить функциональность. Однако волшебной пилюли не бывает. Бизнесу надо будет провести большую работу по цифровизации всех своих процессов, внешних и внутренних коммуникаций.

            Поддержка клиентов по цифровым каналам – это обещание скорости, эффективности и, в значительной мере, ощущение контроля. Чат-боты – отличный инструмент для этого, с помощью которого можно создавать настоящий омниканальный клиентский опыт. Однако, важно понимать, как можно грамотно адаптировать ботов к бизнес-процессам и структуре компании.
            Original source: habrahabr.ru (comments, light).

            https://habrahabr.ru/post/338656/


            Метки:  

            Обзор NAS для малого и среднего бизнеса QNAP TDS-16489U

            Понедельник, 25 Сентября 2017 г. 11:53 + в цитатник
            KorP сегодня в 11:53 Разное

            Обзор NAS для малого и среднего бизнеса QNAP TDS-16489U


              Российское представительство компании QNAP предоставило мне для обзора свою NAS систему TDS-16489U-SB3, которая позиционируется, как решения для малого и среднего бизнеса. Сегодня я расскажу о её, их применимости в небольшой компании, а также расскажу об операционных системах QTS.

              TDS-16489U содержит только один контроллер, позволяет устанавливать как SAS и SATA диски, так и SSD, и управляется классической QTS, которую вы могли встретить в своих домашних NAS от компании QNAP. При этом аппаратное обеспечение позволяет добиться высокой производительности, а операционная система QTS позволяет использовать его не только для хранения данных.

              Технические характеристики:
              • Форм-фактор: Cтоечный (3U)
              • Процессор: 2 x Intel Xeon E5-2600 v3 (6 или 8 ядерная конфигурация)
              • Память: 256Gb (DDR4), может быть расширена до 1Tb (16 слотов)
              • Flash-память: 512Mb
              • Диски:
                • 16 x 3.5" SAS, SATA или 2.5" SAS/SATA SSD
                • 4 x 2.5" SAS/SATA SSD
                • 2 x mSATA
              • Максимальная емкость хранилища: 160TB
              • Максимальная емкость решения (при помощи полок расширения): 1440TB
              • Сетевые интерфейсы:
                • 4 x SFP+ 10GbE ports
                • 2 x RJ-45 1GbE ports
              • Слоты/порты расширения:
                • 4 x PCle: 3 слота PCIe Gen3 x8, 1 слот PCIe Gen3 x16
                • 4 x USB 3.0
                • 1 x VGA
              • Питание: 2 x 650 Вт
              • Энергопотребление:
                • В спящем режиме: 255 Вт
                • В работе: 363 Вт


              16 отсеков для 3.5» SAS/SATA. Безвинтовое крепление дисков здесь отсутствует

              Глядя на технические характеристики, можно сказать, что система крайне универсальна и может быть интегрирована в любую существующую сеть. К сожалению, в списке совместимости нет FC карт, поэтому придётся ограничиваться только LAN. Зато систему можно расширить при помощи 40/56GbE сетевых адаптеров, устанавливающиеся в PCIe слоты. Также эти слоты можно использовать и для установки PCIe SSD, которые могут быть использованы в качестве кэша (правда в списке совместимости значатся только Intel DC P3700). При помощи технологии Qtier вы можете организовать многоуровневое хранение данных.


              Не стоит так же забывать, что QTS построен на базе Linux, поэтому может управлять не только дисковыми ресурсами, но и поддерживать стороннее ПО и железо. Благодаря этому систему можно расширить веб-камерами для организации видеонаблюдения, звуковыми картами, 3/4G модемами и т.д. Установленных процессоров Intel Xeon E5-2600 v3 хватит для решения огромного круга задач.

              TDS-16489U оснащен тремя SAS-контроллерами производства LSI

              Но не стоит бояться, что система построена на базе Linux, если вы не подготовленный пользователь. Система ничем не отличается от тех, что устанавливаются на домашние модели компании, соответственно настройка чего-либо сводится к нажатию нескольких кнопок и вводу нескольких параметров, что не должно вызывать каких-то проблем.

              Сам контроллер удобно вынимается сзади, вместе с подключенными проводами, не придётся перекоммутировать при установке плат расширения

              Что же касается программного обеспечения — выбор здесь намного шире. Помимо стандартных репозиториев с ПО, которое позволит сделать из устройства не только универсальное хранилище с блочным, файловым и объектным доступом, многие компании выпускают свои продукты и под QNAP (к примеру, ПО для резервного копирования — Nakivo Backup & Replication). Но не стоит забывать о том, что в системе используются высокопроизводительные процессоры и большой объём оперативной памяти, а это позволяет нам нагрузить систему ещё и чем-то более полезным. За всё время тестирования я не видел, чтобы загрузка процессора превышала 3%, когда я использовал NAS именно как систему хранения, поэтому дополнительное ПО для виртуализации и контейнеризации (Docker/LXC) выглядит очень интересно.

              Мониторинг производительности системы


              Запущенный контейнер в ContainerStation


              Список виртуальных машин в VirtualizationStation3


              Работающая Windows 10. Не требует Java

              Данная модель и позиционируется как «двойной сервер»: сервер хранения и сервер виртуализации в одной коробке. Кто-то идёт по пути разделения этих сущностей, а кто-то наоборот. Обычно такие бюджетные решения строятся на базе младших моделей серверов, которые позволяют установить большое количество дисков. Компания QNAP же пошла от обратного, предложив именно дисковый массив, с возможность запуска виртуальных машин/контейнеров. И это не удивительно, ведь с каждым годом производительность процессоров увеличивается всё сильнее и сильнее, при этом цена на них падает, но далеко не все задачи хранения данных способны утилизировать эти ресурсы. Поэтому вполне логично использовать их под какие-то другие задачи и виртуализация как раз одна из таких задач. Конечно, учитывая всего один контроллер в данной системе, вопрос отказоустойчивости остаётся открытым, но тем не менее, его вполне можно было бы использовать в качестве резервной или тестовой среды, дабы утилизировать имеющиеся вычислительные ресурсы.

              4 отсека 2.5» SSD, 4 порта SFP+, IPMI, 4 USB 3.0 и 2 порта 1Gb/s, два блока питания

              Отказоустойчивость призвана обеспечивать резервная система, синхронизацию с которой можно организовать на системе. Double-Take Availability позволяет обеспечить высокую доступность и восстановление данных.


              Встроенные средства для резервного копирования и репликации, в том числе в облачные сервисы (Amazon S3, Dropbox, Google Drive и тд), позволяют на базе одного решения выполнять полный комплекс обеспечения сохранности и восстановления данных.

              NAS отлично подходит для сферы малого и среднего бизнеса: доступны 16 отсеков для 3,5" жестких дисков, а также четыре отсека для 2,5» SSD, что позволяет организовать хранилище внушительного объёма и производительности. Наличие двух высокопроизводительных процессоров и приличный объём оперативной памяти позволяют решать широкий круг задач при помощи одного устройства. Возможность интеграции устройства как в 1, так и в 10Gb/s сети делает устройство крайне универсальным и может применяться в офисах и «на вырост». Операционная система QNAP QTS позволяет управлять устройством и добавлять функционал при помощи нажатия всего пары клавиш, делая настройку и сопровождение крайне простой. Встроенные возможности резервного копирования увеличивают надёжность хранения данных. Пожалуй единственным минусом я могу назвать наличие лишь всего одного контроллера в системе, но предполагаю, что данное ограничение накладывается операционной системой, пришедшей в корпоративные устройства из домашней линейки. Но у компании QNAP есть и двуконтроллерные решение, построенные на базе операционной системы QES, и мне пообещали в ближайшее время предоставить такую систему — ES1640dc v2. Так что, как это сейчас можно говорить — подписывайтесь и ждите продолжения. Я обязательно сделаю развёрнутый обзор этого решение и сравню операционные системы QTS и QES.
              Original source: habrahabr.ru (comments, light).

              https://habrahabr.ru/post/338652/


              Метки:  

              Comodo Group сообщают о четырехкратном увеличении числа киберугроз

              Понедельник, 25 Сентября 2017 г. 11:53 + в цитатник
              VASExperts сегодня в 11:53 Разработка

              Comodo Group сообщают о четырехкратном увеличении числа киберугроз

                Компания Comodo Group Inc. сообщает, что во втором квартале этого года количество вредоносных программ выросло почти в 4 раза по сравнению с первым кварталом. Согласно отчету, количество заражений увеличилось с 25 млн до 97 млн.

                По данным Лаборатории Касперского, им удалось обнаружить и отразить 45 тыс. атак червя WannaCry в более чем 74 странах. А Petya, новая итерация которого (NotPetya) появилась 27 июня, поразил 2 тыс. компаний с помощью EternalBlue.


                / Flickr / Christoph Scholz / CC

                Чаще всего заражения происходили с помощью троянов — 5,8 млн случаев. За ними следуют черви — 4,5 млн заражений и 2,6 млн традиционных вирусов. Также было выявлено 209 тыс. использований бэкдоров.

                «Инфекции» были зафиксированы в 236 из 253 доменов высшего уровня. Лидерами по числу атак оказались Россия, Индонезия и Филиппины. США же заняли первое место по числу заражений «троянскими конями».

                Как заявляют в Comodo, вирусы и черви чаще всего встречаются в странах со слабой экономикой, поскольку хорошо работают в слабозащищенных сетях. Бэкдоры же встречались чаще в богатых странах и носили целевой характер: в Австралии, Великобритании и Японии.

                Поэтому многие государства усиливают работу в сфере противодействия киберпреступлениям. Холгер Мунк (Holger Muench), президент Федерального ведомства уголовной полиции Германии призывает к ужесточению законов для борьбы с киберпреступностью в даркнете и другими криминальными группировками. А Япония запускает несколько тренировочных центров для подготовки специалистов по безопасности и исследования кибергуроз.

                В США член палаты представителей штата Джорджия Том Грейвс (Tom Graves) внес на рассмотрение законопроект, предоставляющий жертвам продолжающихся кибератак более широкие права, касающиеся ответных действий. В частности, пострадавшие от деятельности хакеров смогут предпринимать агрессивные контрмеры для защиты своей информации, то есть взламывать системы злоумышленников в ответ. Документ также описывает «активные меры киберзащиты», под которыми подразумеваются: установление преступника и передача этой информации правоохранительным органам.

                Россия также принимает участие в мировой борьбе с киберпреступностью, представив проект конвенции ООН «О сотрудничестве в сфере противодействия информационной преступности». Он описывает техническое и правовое взаимодействие сторон в сфере информационно-коммуникационных технологий.

                Чего ожидать к концу года


                Согласно прогнозам РАЭК и отчету об актуальных киберугрозах от Positive Technologies, число и сложность атак будут только расти. Есть даже вероятность еще одной крупной атаки типа DDoS, так как сервисы-вымогатели по сдаче троянов в аренду продолжают набирать популярность. Атаки будут эволюционировать в таких средах, как облачные технологии и мобильное ПО.

                Стоит отметить, что опасность грозит и IoT-технологиям. По данным Nexusguard, рост числа атак на IoT-сети вырос на 380% за последние полгода. Это объясняется как растущей популярностью IoT, так и уязвимостью технологии. Подробнее о других трендах в киберугозах можно почитать здесь и здесь.

                Эксперты отмечают, что основными причинами роста числа атак являются отставание технологий систем безопасности, напряженные отношения между некоторыми странами в рамках мировой политики, дефицит профессионалов ИТ-сферы и совершенствование методов и приемов самих атак (подробнее об этом мы писали здесь).


                / Flickr / Henri Bergius / CC

                Интеллектуальные методы защиты


                Эксперты по безопасности продолжают напоминать о важности соблюдения ИТ-гигиены: надежные пароли, обучение сотрудников, отказ от хранения важной информации в почте. Однако они также убеждены, что высокого уровня защиты можно достигнуть, исключив влияние человеческого фактора. Поэтому они обращаются к возможностям машинного обучения и системам искусственного интеллекта.

                Одним из таких интеллектуальных решений является Deep Instinct, которое использует глубокое обучение для распознавания вредоносного кода. Разработчики отобрали миллионы файлов всех разновидностей, классифицировали их и передали сети для тренировки. Конечным результатом стала модель прогнозирования, которую компания назвала «инстинктом». Решение может обнаруживать уязвимости нулевого дня, новые вредоносные программы и изощренные целевые кибератаки (APT).

                Еще один способ борьбы за безопасность с помощью ИИ был представлен компанией Microsoft. Они создали инструмент для разработчиков — Microsoft Security Risk Detection — который ищет ошибки и уязвимости в ПО, готовящемся к релизу.

                По словам исследователя Microsoft Дэвида Молнара (David Molnar), для проведения фаззинга компании обычно нанимают экспертов по безопасности. Но поскольку объем создаваемого и используемого программного обеспечения увеличился, тестирование усложнилось. При этом важность этой задачи выросла в несколько раз из-за стремительного роста числа кибератак.

                Молнар заявляет, что инструмент автоматизирует тот же алгоритм, которым пользуемся мы с вами, чтобы найти ошибку, только работает в иных масштабах благодаря облаку. Подробнее о нем можно почитать здесь.

                Представители компании также заявили, что Windows Defender в новом обновлении Creators Update для Windows использует возможности искусственного интеллекта для защиты от вредоносного ПО.

                Пусть ИИ пока не способен устранить все потенциальные киберугрозы, он может анализировать поведение программ. Решить задачу моделирования хорошего поведения «вручную» практически невозможно. Искусственный интеллект же может обрабатывать значительные объемы данных и автоматически генерировать базовые модели нормального поведения и различать вредоносный код.

                О Comodo Group

                Comodo Group — американская компания-производитель программного обеспечения. Является одним из крупнейших поставщиков сертификатов, предлагая бесплатные сертификаты для персональной электронной почты. Основана в 1998 году.

                P.S. Несколько материалов по теме из нашего блога:

                Original source: habrahabr.ru (comments, light).

                https://habrahabr.ru/post/338482/


                Метки:  

                Почта России: страшно ли жить после Страшнова?

                Понедельник, 25 Сентября 2017 г. 11:04 + в цитатник
                pokupo сегодня в 11:04 Управление

                Почта России: страшно ли жить после Страшнова?

                  image
                  Ждун или дрон? Бесконечный выбор в российской Матрице.

                  Август-2017. Одно из отделений Почты России. Слева закуток Почты Банка, в котором двое сотрудников играются в своих гаджетах. Вокруг них пустота. Прямо — три окошка Почты. Работает только одно. Очередь человек в 20 к нему. Надо стоять — деться некуда. Терминал электронной очереди сломался через пару недель после установки.

                  Почти каждый посетитель впереди, когда подходит его очередь, напряжённо спрашивает, куда делась его посылка, должна была прийти давным-давно. Кассир извиняется, говорит, что уже несколько месяцев работает одна за десятерых, и даже посылки месячной давности ещё не отсортированы. Или не извиняется, а просто кричит, потому что задолбали с одним и тем же вопросом, который её начальству надо задать. За окошком на полу видны последствия работы “одной за десятерых”: проходы завалены коробками и пакетами.

                  Это “Почта России”-2017. Страшнов ушёл в июле. Но судя по тому, что проблемы того же Иркутского УФПС (а сценка вначале — как раз из отделения Иркутска) начались весной, Страшнов “ушёл” ещё раньше. Связь, конечно, опосредованная, но тут стоит сказать про большую рыбу, к голове которой пришла прокуратура. И голова в своих беспокойствах про хвост совсем перестала думать, а хвост без головы — шевелиться. Страшно ли жить Почте после Страшнова? Надо разобраться.

                  Для этого взглянем глобальнее на весь почтовый екоммерс, нововведения Дмитрия Страшнова и их результат.

                  1. Коллапс


                  ...стал первой проблемой, с которой столкнулся новый гендир Почты, и поводом, по которому он вообще очутился здесь. Чтобы не допустить подобного, Страшнов стал вкладываться в современные автоматизированные сотртировочные центры. И первый был запущен во Внуково в ноябре того же года. С остальными всё происходило не так быстро и паралича всё-таки избежать не удалось. В 2016 новопостроенный (в 2013) ЛЦ Внуково переоснащён на автоматизированный манер, однако в декабре того же года не выдержал напора (хотя пример довольно спорный, тут нужны очевидцы). Другой пример объективнее — Казанский ЛЦ был запущен в тестовом порядке в декабре 2016, однако запуск провалился. И некоторые примеры помельче — касаемые рядовых ОПС — Рязань, например, или Томск (тут можно чуть ли не каждое ОПС в пример привести).
                  С другой стороны, в аэропорту Толмачево г. Новосибирска внедрена в том же 2016-м японская технология сортировки — на пробки там никто громко не жаловался.
                  С остальными автоматизированными сортировочными центрами как-то не сложилось. Год назад мы уже писали статью в том числе на эту тему. В соответствии с планами Минкомсвязи годичной давности уже должны быть сданы ЛСЦ Ростова, Санкт-Петербурга, и начато строительство Екатеринбургского и Красноярского ЛСЦ. Ни один не сдан — а сроки сдвинуты.
                  А ЛСЦ Хабаровска лучше всех вписывается в “концепцию понедельника” (“в понедельник точно начну бегать/строить/курить”) — взгляните на новости за 2014, 2015, 2016, 2017. В последней радует заголовок — “В Хабаровске в 2018 году начнут строить логистический центр “Почты России”. Ждём очередной новости о радужных перспективах.

                  Но! Несмотря на оставшуюся проблему со скоростью доставки, Почта России продемонстрировала успешное продвижение среди операторов доставки EMS. Шестое место из 198. Рядом с тройкой первых операторов (Финляндия, Молдавия и Латвия) наша огромная Почта выглядит несуразно. Для сравнения — положение сервисов стран, сравнимых по размеру территорий: США — 35, Китай — 94. За счёт чего же сделан такой рывок?

                  1. Это всего лишь итоги первого квартала 2017 года, в конце года наверняка будет откат. Но с приходом Страшнова Почта каждый год только прибавляла (с 92-го, через 81-е к 59-му). Проблема только в том, что на англоязычных ресурсах EMS никакого рейтинга найти не удалось. Может, плохо искали. Может, рейтинг не так популярен в остальном мире. Остаётся принять на веру данные ПР о крутизне этого рейтинга.
                  2. Экспорт — за счёт увеличения количества экспортных отправлений EMS на 25%. А это один из основных показателей рейтинга.
                  3. Собственно скорость доставки. К которой можно добавить скорость ответов на претензии и скорость передачи данных о статусах почтовых отправлений в систему отслеживания.

                  Видно, что внимание направлению EMS было уделено должное. В целом по скорости всё.
                  Нет — для контраста вот ещё — китайский цех разбора посылок — кто эти инопланетяне и где тогда мы?

                  2. Субсидии


                  “… развращают любое предприятие” — золотые слова Дмитрия Страшнова, сказанные уже после прожитых на Почте лет. Ни одна копейка, доставшаяся на известном блюдечке, не ценится так, как мокрая от чёрного пота. Позиция всех дотационных, субсидируемых и инвестируемых компаний и просто любых подобных лиц (позиция внутренняя, конечно, — негласная), как правило, такая: “Зачем развиваться? Дадут же ещё?”.

                  И Почта бы жила так же. Если бы в конце 2014 года государство не прекратило её субсидировать. Страшнов говорит, что новость оказалась для них неожиданной. Потому что стратегия развития, разработанная в 2013-м Минкомсвязи, предполагала постепенный отказ от субсидий, начиная с 2018-го — заканчивая 2023-м. Хотя если Страшнов ставил цель выйти на IPO, то о субсидиях надо было забывать как можно раньше. Кризис рубля и нефтедобычи поспособствовал.
                  Чтобы ликвидировать дефицит, пришлось повышать цены на тарифы. Начали с подписок и пересмотра соглашений с издательскими домами. Те бунтовали, ненавидели, судились, но в конце концов соглашались.
                  И по итогам 2015 года вместо дефицита Почта получила чистую прибыль в 1,5 млрд. рублей. То есть отыграла и 5,5 млрд. положенных субсидий и в хороший плюс вышла. Насколько велика здесь именно роль повышения тарифов на подписку, не совсем понятно, но говоря о прекращении субсидирования, Страшнов упоминает именно этот шаг. Хотя долю среди доходов Почты подписка занимает скромную (всего 3%). Минутка арифметики помогает выяснить, что из 140 млрд. рублей доходов за 2014-й год, подписка принесла примерно 4,2 млрд. Чтобы получить дополнительный доход в 7 млрд. только за счёт подписки, нужно было увеличить её тарифы примерно в 2,7 раза. Читаем новости — всё сходится — в 2,5 раза в конце 2014-го и +10% в начале 2015-го. Хорошо, когда всё сходится. Плохо, когда только на бумаге.

                  image

                  С тех пор прибыль планомерно растёт. Цены на услуги, правда, тоже. Но основным фактором всё-таки считается рост интернет-торговли в целом, и трансграничной торговли, в частности (читай, с Китаем). И соответственно рост обрабатываемых отправлений.

                  Итого: 2014 год — прибыль 1,2 млрд (при живом ещё субсидировании), 2015 — 1,5 млрд, 2016 — 1,7 млрд. рублей.

                  image

                  Почти наверняка 2017-й год тоже закончится прибылью. Тем не менее, первым стратегическим решением новоиспечённого главы Николая Подгузова становится ходатайство Правительству о возвращении государственной поддержки (впрочем, пока неудачное). Что за этим скрывается? Искажение показателей прежним руководством, и на самом деле на Почте сейчас пустые амбары и два самолёта? Или же неумение Подгузова строить бизнес предприятия так, как это делал Страшнов? Покажет только время. Или чистосердечные признания. Да. Точно время.

                  3. Почта.Маркет


                  … Страшновым воспринимался как неуместное явление. Для глобального рынка, во всяком случае. Внутри соперничество с рядом интернет-гигантов будет тяжёлым и проигрышным. Снаружи Амазон, Алибаба и другие. Остаются только две перспективы. Предоставление площадкой услуг для отдалённых регионов. И связующее звено между заграницей и Россией, т.е. инструмент проникновения зарубежных площадок на российский рынок.
                  В целом состояние почтового маркетплейса в настоящее время можно оценить удовлетворительно.
                  Функционал неплох. Баги встречаются, но исправляются. Ассортимент дороговат по сравнению с конкурентами. Всего на площадке на 2016 год имелось более 100 тыс. товаров. И оформлено более 500 тыс. заказов.
                  Развитие видно. Например, из последнего — это внедрение торговли смартфонами и планшетами и договор с Samsung. Есть развитие. Но тестовый период по сути не закончен. И китайские конкуренты пока не видят смысла проникать в Россию через Почту.Маркет. Может, дождёмся. Хотя надо ли?

                  4. Direct mail, CRM и остальное


                  Сервис таргетированной почтовой рассылки. Когда есть миллионная база подписчиков, рано или поздно придёт мысль, что по ней можно рассылать ещё и рекламу. До выхода Почты на этот рынок объём direct mail оценивался в 600 млн. рублей. К 2020-му году эксперты предрекали пятикратный рост этого рынка за счёт участия Почты.
                  Опять же пару лет Почта России уже тестирует этот сервис. Но о кратном увеличении ФГУП пока не хвастается. Хотя понятно — копеечка в бюджет падает. Например, в Тюмени.
                  Ещё одним небольшим достижением при Страшнове стала разработка CRM-системы федерального масштаба вместо существовавших ранее локальных проектов. Ушло на это пока около 100 млн. рублей. Внедрив систему (по плану 1 октября 2017), станет проще взаимодействовать с юридическими лицами и осуществлять ту же рассылку.
                  К технологичным новинкам периода Страшнова можно добавить онлайн-услуги курьера и посылок, модернизацию сайта. И, конечно, трекинг-сервис, творящий с посылками не поддающиеся логике вещи, но жизнь всё же облегчающий.

                  5. Почта Банк и блокчейн


                  Почтовый банк — одно из главных достижений Страшнова сотоварищи. 15 лет об этом говорили. В 2016-м наконец-то совместно с группой ВТБ преобразовали Лето-банк в “Почта Банк”. Стратегия развития предусматривает скачок в тройку банков-лидеров к 2023 году. За два года банк уже поднялся по финансовым показателям с 69-го на 41-е место (по народному рейтингу занимает 15-е место). За 2016-й чистая прибыль составила 1,2 млрд. рублей. Рост активов, кредитного портфеля, клиентских пассивов, развитие digital-каналов, мобильного приложения — вот все цифры.
                  Почта России (за счёт дочернего ООО “Почтовые финансы”) владеет минус одной акцией плюс 50% банка. То есть очередные лишние 600 млн.рублей за 2016-й год у Почты есть.

                  Из положительных моментов взаимодействия с ПР можно выделить проект выдачи посылок через банковские окна. Увеличено в 10 раз присутствие в регионах (более 6 тыс. точек).

                  Из отрицательных — видимо, пенсии выдавать на Почте будут ещё долго. И остальные финансовые услуги, которые можно было бы перекинуть в Почта Банк, пока ещё оказываются ПР.

                  Про блокчейн можно добавить только то, что глава Почта Банка Дмитрий Руденко в 2016-м заикнулся о возможности внедрения этой технологии. Пару дней назад эти слова повторил уже Николай Подгузов применительно к самой Почте. Однако как в банке до активных шагов дело не дошло, так и слова Подгузова можно считать пока чистым популизмом.

                  6. Премия и самолёты


                  — А помнишь, были времена, когда самолётов на Почте ещё не было? Молодец Страшнов, что решился. С него этот авиапарк начался. Только вот летают медленно, да крюка через Йоханнесбург дают.
                  — Да нет, это не при Страшнове. Тот не мог. Он только и сделал, что премию себе выписал в 100 млн., а затем ушёл…- утрированный диалог между любителями вести утрированные диалоги лет эдак через 10.
                  Сколько бы позитивного не внёс Страшнов в Почту, навсегда запомнится именно главный его косяк. Косвенно он не виноват. Это всё министерство связи намышковало. Но по-человечески его не поймёт ни один из нынешних и будущих рядовых почтальонов и людей. Да — топ-менеджер, да — управляет огромным предприятием, причём достаточно прогрессивно управляет, да — в том же Газпроме или Роснефти премии в разы больше. Но если у простого почтальона зарплата минимальная — то морального права принимать такую премию у руководителя нет. Вот и выходит, что формальное право на премии Страшнов и его заместители имеют, но вернуть должны. Тем более что начислялась она в 2014-м году фактически не с прибыли, а с дотаций, которые покрывали убытки.

                  Что ни говори, а собственные самолёты давно были нужны Почте. Тем интереснее позиция главы профсоюза ПР Татьяны Соколовой (с жалобы которой началась вторая проверка Генпрокуратуры по незаконной покупке самолётов, и которая, если вникнуть, олицетворяла и возглавляла борьбу старой системы с группой управленцев Страшнова). Если кратко разобрать основные тезисы, то самолёты, во-первых, достались втридорога (кредит в Альфа-банке под 13%), во-вторых, вовсе не нужны, а в-третьих, Страшнов — контрабандист. Почта ответила, что самолёты взяты в субсидированный кредит по госпрограмме “Развитие авиационной промышленности” (ставка 2,7%). Собственные самолёты экономят время, исключая лишнее взаимодействие с кем бы то ни было. А Страшнов не контрабандист, а Дмитрий Евгеньевич.
                  Генпрокуратура же отправила проверенные материалы по известному маршруту в Следственный комитет, где скорее всего, уже с мая пылится постановление об отказе в возбуждении дела. С подшитым за ним постановлением об отмене постановления об отказе. И с новым постановлением об отказе. И так до бесконечности, если понадобится.

                  7. Перед зависшим IPO


                  Наследство Николаю Подгузову досталось интересное. С одной стороны, целая россыпь инноваций, каких Почта не видела никогда до этого. Вектор в сторону модернизации всех элементов системы Страшнов держал крепко. С другой, — так и не дошедшие до приятных (или хотя бы удовлетворяющих) цифр зарплаты почтальонов, никуда не исчезнувшие проблемы с коллапсами, и это алчное эхо от премии, которое будет громыхать рядом с Почтой ещё очень много зим.
                  Единственное, чего самому Страшнову не хватило в работе, и что он считает своей главной неудачей — это так и несостоявшееся акционирование Почты России. Причём готовность к выходу на биржу, по его мнению, была ещё в 2015-м году. Страшнов добился финансовой прозрачности предприятия, регулярно публикуя подробные отчёты. Долю в Почте России готов был купить китайский Алибаба. На многочисленных конференциях Страшнов предлагал десятки разнообразных выгодных вариантов, возникающих после проведения IPO, японскую, немецкую и другие модели развития. Не вышло. Про причины он говорит следующее:

                  1. Непринятие законов о связи и акционировании
                  2. Отсутствие политического спонсорства
                  3. Не поговорил с Путиным на эти темы.

                  Политолог-социолог Вячеслав Смирнов заметил в сторону Страшнова следующее: в России силовой блок значительно влиятельнее экономического. А у Страшнова в этом кругу отсутствовали сколь-нибудь значимые покровители. От того такое пристальное внимание со стороны силовых структур.
                  Поэтому продолжит Почта развиваться с позиции (скорее под контролем) силы, а не ума. А имеющим силу ни ум, ни развитая Почта России не нужны.
                  Original source: habrahabr.ru (comments, light).

                  https://habrahabr.ru/post/338610/


                  Переход с ASP.NET к ASP.NET Core 2.0

                  Понедельник, 25 Сентября 2017 г. 10:28 + в цитатник
                  Wellsoft сегодня в 10:28 Разработка

                  Переход с ASP.NET к ASP.NET Core 2.0

                  • Tutorial

                  Эта статья является переводом справочного руководства по переносу приложений из ASP.NET в ASP.NET Core 2.0. Ссылка на оригинал
                  В силу некоторых причин, у нас возникла необходимость перейти с ASP.NET в ASP.NET Core 1.1., о том, как это у нас получилось, читайте тут.


                  Содержание


                  1. Требования
                  2. Выбор Фреймворка
                  3. Различия в структуре проекта
                  4. Замена Global.asax
                  5. Хранение конфигураций
                  6. Встроенный механизм Dependency Injection
                  7. Работа со статическими файлами

                  Требования


                  • .NET Core 2.0.0 SDK или более поздняя версия.


                  Выбор фреймворка


                  Для работы с ASP.NET Core 2.0 проектом, разработчику предстоит сделать выбор – использовать .NET Core, .NET Framework или использовать сразу оба варианта. В качестве дополнительной информации можно использовать руководство Choosing between .NET Core and .NET Framework for server apps (вкратце можно сказать что .NET core является кроссплатформенной библиотекой, в отличие от .NET Framework) для того чтобы понять, какой Фреймворк для вас окажется наиболее предпочтительным.
                  После выбора нужного Фреймворка в проекте необходимо указать ссылки на пакеты NuGet.
                  Использование .NET Core позволяет устранить многочисленные явные ссылки на пакеты, благодаря объединенному пакету (мета пакету) ASP.NET Core 2.0. Так выглядит установка мета пакета Microsoft.AspNetCore.All в проект:


                  
                    
                  

                  Различия в структуре проекта


                  Структура файла проекта .csproj была упрощена в ASP.NET Core. Вот некоторые значительные изменения:
                  • Явное указание файлов является необязательным для добавления их в проект. Таким образом, уменьшается риск конфликтов в процессе слияния XML, если над проектом работает большая команда
                  • Больше нет GUID ссылок на другие проекты, что улучшает читаемость
                  • Файл можно редактировать без его выгрузки из Visual Studio:



                  Замена Global.asax


                  Точкой входа для ASP.NET приложений является Global.asax файл. Такие задачи, как конфигурация маршрута и регистрация фильтров и областей, обрабатываются в файле Global.asax


                  public class MvcApplication : System.Web.HttpApplication
                  {
                      protected void Application_Start()
                      {
                          AreaRegistration.RegisterAllAreas();
                          FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
                          RouteConfig.RegisterRoutes(RouteTable.Routes);
                          BundleConfig.RegisterBundles(BundleTable.Bundles);
                      }
                  }

                  Этот подход тесно связывает приложение и сервер, на котором развернуто приложение. Для уменьшения связности был представлен OWIN как средство, обеспечивающее более правильный путь совместного использования нескольких фреймворков вместе.
                  OWIN позволяет добавить в конвейер запроса только необходимые модули. Среда выполнения использует Startup для конфигурации сервисов и конвейера запросов.
                  Startupрегистрирует набор промежуточных сервисов (middleware) вместе с приложением. Для каждого запроса приложение вызывает поочередно каждый из набора промежуточных сервисов, имеющих указатель на первый элемент связанного списка обработчиков.
                  Каждый компонент промежуточного сервиса может добавить один или несколько обработчиков в конвейер обработки запросов. Это происходит с помощью возврата ссылки на обработчик, который находится в начале списка.
                  И обработчик, закончив свою работу, вызывает следующий обработчик из очереди.
                  В ASP.NET Core, точкой входа в приложении является класс Startup, с помощью которого мы нивелируем зависимость от Global.asax.
                  Если изначально был выбран .NET Framework то при помощи OWIN мы можем сконфигурировать конвейер запросов как в следующем примере:


                  using Owin;
                  using System.Web.Http;
                  
                  namespace WebApi
                  {
                      // Заметка: По умолчанию все запросы проходят через этот конвейер OWIN. В качестве альтернативы вы можете отключить это, добавив appSetting owin: AutomaticAppStartup со значением «false».
                      // При отключении вы все равно можете использовать приложения OWIN для прослушивания определенных маршрутов, добавив маршруты в файл global.asax с помощью MapOwinPath или расширений MapOwinRoute на RouteTable.Routes
                  
                      public class Startup
                      {
                          // Вызывается один раз при запуске для настройки вашего приложения.
                          public void Configuration(IAppBuilder builder)
                          {
                              HttpConfiguration config = new HttpConfiguration();
                  //Здесь настраиваем маршруты по умолчанию, 
                              config.Routes.MapHttpRoute("Default", "{controller}/{customerID}", new { controller = "Customer", customerID = RouteParameter.Optional });
                  //Указываем на то что в качестве файла конфигурации мы будем использовать xml вместо json 
                  
                              config.Formatters.XmlFormatter.UseXmlSerializer = true;
                              config.Formatters.Remove(config.Formatters.JsonFormatter);
                              // config.Formatters.JsonFormatter.UseDataContractJsonSerializer = true;
                  
                              builder.UseWebApi(config);
                          }
                      }
                  }

                  Также при необходимости здесь мы можем добавить другие промежуточные сервисы в этот конвейер (загрузка сервисов, настройки конфигурации, статические файлы и т.д.).
                  Что касается версии фреймворка .NET Core, то здесь используется подобный подход, но не без использования OWIN для определения точки входа. В качестве альтернативы используется метод Main в Program.cs (по аналогии с консольными приложениям), где и происходит загрузка Startup:


                  using Microsoft.AspNetCore;
                  using Microsoft.AspNetCore.Hosting;
                  
                  namespace WebApplication2
                  {
                      public class Program
                      {
                          public static void Main(string[] args)
                          {
                              BuildWebHost(args).Run();
                          }
                  
                          public static IWebHost BuildWebHost(string[] args) =>
                              WebHost.CreateDefaultBuilder(args)
                                  .UseStartup()
                                  .Build();
                      }
                  }

                  Startup должен включать метод Configure. В Configure определяется, какие сервисы будут использоваться в конвейере запроса. В следующем примере (взятом из стандартного шаблона web-сайта), несколько методов расширения используются для настройки конвейера с поддержкой:
                  • BrowserLink
                  • Error pages
                  • Static files
                  • ASP.NET Core MVC
                  • Identity


                  public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
                  {
                      loggerFactory.AddConsole(Configuration.GetSection("Logging"));
                      loggerFactory.AddDebug();
                  
                      if (env.IsDevelopment())
                      {
                          app.UseDeveloperExceptionPage();
                          app.UseDatabaseErrorPage();
                          app.UseBrowserLink();
                      }
                      else
                      {
                          app.UseExceptionHandler("/Home/Error");
                      }
                  
                      app.UseStaticFiles();
                  
                      app.UseIdentity();
                  
                      app.UseMvc(routes =>
                      {
                          routes.MapRoute(
                              name: "default",
                              template: "{controller=Home}/{action=Index}/{id?}");
                      });
                  }

                  В итоге мы имеем разделение среды выполнения и приложения, что дает нам возможность осуществить переход на другую платформу в будущем.
                  Заметка: Для более глубокого понимания ASP.NET Core Startup и Middleware, можно изучить Startup in ASP.NET Core


                  Хранение конфигураций


                  ASP.NET поддерживает сохранение настроек. Например, это настройки, которые используются средой выполнения, где было развернуто приложение. Сам подход заключался в том, что для хранение пользовательских key-value пар использовалась секция в файле Web.config:


                  
                    
                    
                  

                  Приложение получало доступ к этим настройкам с помощью коллекции ConfigurationManager.AppSettings из пространства имен System.Configuration :


                  string userName = System.Web.Configuration.ConfigurationManager.AppSettings["UserName"];
                  string password = System.Web.Configuration.ConfigurationManager.AppSettings["Password"];

                  В ASP.NET Core мы можем хранить конфигурационные данные для приложения в любом файле и загружать их с помощью сервисов на начальном этапе загрузки.
                  Файл, используемый по умолчанию в новом шаблонном проекте appsettings.json:


                  {
                    "Logging": {
                      "IncludeScopes": false,
                      "LogLevel": {
                        "Default": "Debug",
                        "System": "Information",
                        "Microsoft": "Information"
                      }
                    },
                    // Здесь можно указать настраиваемые параметры конфигурации. Поскольку это JSON, все представлено в виде пар символов: значение  
                   // Как назвать раздел, определяет сам разработчик
                    "AppConfiguration": {
                      "UserName": "UserName",
                      "Password": "Password"
                    }
                  }

                  Загрузка этого файла в экземпляр IConfiguration для приложения происходит в Startup.cs:


                  public Startup(IConfiguration configuration)
                  {
                      Configuration = configuration;
                  }
                  
                  public IConfiguration Configuration { get; }

                  А вот так приложение использует Configuration для получения этих настроек:


                  string userName = Configuration.GetSection("AppConfiguration")["UserName"];
                  string password = Configuration.GetSection("AppConfiguration")["Password"];

                  Есть другие способы, основанные на данном подходе, которые позволяют сделать процесс более надежным, например Dependency Injection (DI).
                  Подход DI обеспечивает доступ к строго типизированному набору объектов конфигурации.


                  // Предположим, AppConfiguration - это класс, который представляет строго типизированную версию раздела AppConfiguration
                  services.Configure(Configuration.GetSection("AppConfiguration"));

                  Заметка: Для более глубокого понимания конфигураций ASP.NET Core, можно ознакомится с Configuration in ASP.NET Core.


                  Встроенный механизм Dependency Injection


                  Важной целью при создании больших масштабируемых приложений является ослабление связи между компонентами и сервисами. Dependency Injection – распространенная техника для решения данной проблемы и ее реализация является встроенным в ASP.NET Core компонентом.
                  В приложениях ASP.NET разработчики использовали сторонние библиотеки для внедрения Injection Dependency. Примером такой библиотеки является Unity .
                  Пример настройки Dependency Injection с Unity — это реализация UnityContainer, обернутая в IDependencyResolver:


                  using Microsoft.Practices.Unity;
                  using System;
                  using System.Collections.Generic;
                  using System.Web.Http.Dependencies;
                  
                  public class UnityResolver : IDependencyResolver
                  {
                      protected IUnityContainer container;
                  
                      public UnityResolver(IUnityContainer container)
                      {
                          if (container == null)
                          {
                              throw new ArgumentNullException("container");
                          }
                          this.container = container;
                      }
                  
                      public object GetService(Type serviceType)
                      {
                          try
                          {
                              return container.Resolve(serviceType);
                          }
                          catch (ResolutionFailedException)
                          {
                              return null;
                          }
                      }
                  
                      public IEnumerable GetServices(Type serviceType)
                      {
                          try
                          {
                              return container.ResolveAll(serviceType);
                          }
                          catch (ResolutionFailedException)
                          {
                              return new List();
                          }
                      }
                  
                      public IDependencyScope BeginScope()
                      {
                          var child = container.CreateChildContainer();
                          return new UnityResolver(child);
                      }
                  
                      public void Dispose()
                      {
                          Dispose(true);
                      }
                  
                      protected virtual void Dispose(bool disposing)
                      {
                          container.Dispose();
                      }
                  }

                  Создаем экземпляр своего UnityContainer, регистрируем свою службу и устанавливаем разрешение зависимости для HttpConfiguration в новый экземпляр UnityResolver для нашего контейнера:


                  public static void Register(HttpConfiguration config)
                  {
                      var container = new UnityContainer();
                      container.RegisterType(new HierarchicalLifetimeManager());
                      config.DependencyResolver = new UnityResolver(container);
                  
                      // Опустим остальную часть реализации
                  }

                  Далее производим инъекцию IProductRepository там, где это необходимо:


                  public class ProductsController : ApiController
                  {
                      private IProductRepository _repository;
                  
                      public ProductsController(IProductRepository repository)  
                      {
                          _repository = repository;
                      }
                  
                  }

                  Поскольку Dependency Injection является частью ядра ASP.NET Core, мы можем добавить свой сервис в метод ConfigureServices внутри Startup.cs:


                  public void ConfigureServices(IServiceCollection services)
                  {
                      //Добавляем сервис приложения
                      services.AddTransient();
                  }

                  И далее инъекцию репозитория можно осуществить в любом месте, как и в случае с Unity.
                  Заметка: Подробности можно посмотреть в Dependency Injection in ASP.NET Core


                  Работа с статическими файлами


                  Важной частью веб-разработки является возможность обслуживания статики. Самые распространенные примеры статики — это HTML, CSS, JavaScript и картинки.
                  Эти файлы нужно сохранять в общей папке приложения (или например в CDN) чтобы в дальнейшем они были доступны по ссылке. В ASP.NET Core был изменен подход для работы с статикой.
                  В ASP.NET статика хранится в разных каталогах.
                  А в ASP.NET Core статические файлы по умолчанию хранятся в «web root» (/ wwwroot). И доступ к этим файлам осуществляется с помощью метода расширения UseStaticFiles из Startup.Configure:

                  public void Configure(IApplicationBuilder app)
                  {
                      app.UseStaticFiles();
                  }

                  К примеру, изображения находящееся в папке wwwroot/images будет доступно из браузера по адресу http:///images/.
                  Заметка: Если был выбран .NET Framework, то дополнительно нужно будет установить NuGet пакет Microsoft.AspNetCore.StaticFiles.
                  Заметка: Для более подробной ссылки на обслуживание статических файлов в ядре ASP.NET см. Introduction to working with static files in ASP.NET Core.

                  Original source: habrahabr.ru (comments, light).

                  https://habrahabr.ru/post/338298/


                  Метки:  

                  [Перевод] Развертывание кода ES2015+ в продакшн сегодня

                  Понедельник, 25 Сентября 2017 г. 10:13 + в цитатник
                  ollazarev сегодня в 10:13 Разработка

                  Развертывание кода ES2015+ в продакшн сегодня

                  • Перевод
                  Большинство веб-разработчиков, с которыми я общаюсь сейчас, любят писать JavaScript со всеми новейшими функциями языка — async/await, классами, стрелочными функциями и т.д. Однако, несмотря на то, что все современные браузеры могут исполнять код ES2015+ и изначально поддерживают упомянутый мной функционал, большинство разработчиков по-прежнему транспилируют свой код на ES5 и связывают его с полифиллами, чтобы удовлетворить небольшой процент пользователей, все еще работающих в старых браузерах.

                  Это отвратительно. В идеальном мире мы не будем развертывать ненужный код.


                  При работе с новыми API-интерфейсами JavaScript и DOM мы можем условно загружать полифиллы, т.к. мы можем выявить поддержку этих интерфейсов во время выполнения программы. Но с новым синтаксисом JavaScript сделать это намного сложнее, поскольку любой неизвестный синтаксис вызовет ошибку синтаксического анализа (parse error), и тогда наш код вообще не будет запущен.

                  Хотя в настоящее время у нас нет подходящего решения для создания функционала выявления нового синтаксиса, у нас есть способ установить базовую поддержку синтаксиса ES2015 уже сегодня.

                  Решим это с помощью тега script type="module".

                  Большинство разработчиков думают о script type="module" как о способе загрузки модулей ES (и, конечно же, это так), но script type="module" также имеет более быстрый и практичный вариант использования — загружает обычные файлы JavaScript с функциями ES2015+, зная, что браузер может справиться с ними!

                  Другими словами, каждый браузер, поддерживающий script type="module" также поддерживает большинство функций ES2015+, которые вы знаете и любите. Например:

                  • Каждый браузер, поддерживающий script type="module", также поддерживает async/await
                  • Каждый браузер, поддерживающий script type="module", также поддерживает классы.
                  • Каждый браузер, поддерживающий script type="module", также поддерживает стрелочные функции.
                  • Каждый браузер, поддерживающий script type="module", также поддерживает fetch, Promises, Map, Set, и многое другое!

                  Осталось только предоставить резервную копию для браузеров, которые не поддерживают script type="module". К счастью, если вы в настоящее время генерируете ES5-версию своего кода, вы уже сделали эту работу. Все, что вам теперь нужно — создать версию ES2015+!

                  В остальной части этой статьи объясняется, как реализовать эту технику, и обсуждается, как возможность развертывания кода ES2015+ изменит способ создания модулей в будущем.

                  Реализация


                  Если вы уже используете сборщик модулей (module bundler), например webpack или rollup для генерации своего кода на JavaScript, продолжайте по-прежнему это делать.

                  Затем, в дополнение к вашему текущему набору (bundle), вы создадите второй набор, как и первый; единственное различие будет заключается в том, что вы не будете транспилировать код в ES5, и вам не нужно будет подключать устаревшие полифиллы (legacy polyfills).

                  Если вы уже используете babel-preset-env (что должны), то второй шаг будет очень прост. Все, что вам нужно сделать, это изменить список браузеров только на те, которые поддерживают script type="module", и Babel автоматически не будет делать ненужные преобразования.

                  Иными словами, это будет вывод кода ES2015+ вместо ES5.

                  Например, если вы используете webpack, и вашей основной точкой входа является скрипт ./path/to/main.js, тогда конфигурация вашей текущей версии ES5 может иметь следующий вид (обратите внимание, так как это ES5, я называю набор (bundle) main-legacy):

                  module.exports = {
                    entry: {
                      'main-legacy': './path/to/main.js',
                    },
                    output: {
                      filename: '[name].js',
                      path: path.resolve(__dirname, 'public'),
                    },
                    module: {
                      rules: [{
                        test: /\.js$/,
                        use: {
                          loader: 'babel-loader',
                          options: {
                            presets: [
                              ['env', {
                                modules: false,
                                useBuiltIns: true,
                                targets: {
                                  browsers: [
                                    '> 1%',
                                    'last 2 versions',
                                    'Firefox ESR',
                                  ],
                                },
                              }],
                            ],
                          },
                        },
                      }],
                    },
                  };
                  

                  Для того, чтобы сделать современную версию для ES2015+, все, что вам нужно — это создать вторую конфигурацию и настроить целевую среду только для браузеров, поддерживающих script type="module". Вот как это может выглядеть:

                  module.exports = {
                    entry: {
                      'main': './path/to/main.js',
                    },
                    output: {
                      filename: '[name].js',
                      path: path.resolve(__dirname, 'public'),
                    },
                    module: {
                      rules: [{
                        test: /\.js$/,
                        use: {
                          loader: 'babel-loader',
                          options: {
                            presets: [
                              ['env', {
                                modules: false,
                                useBuiltIns: true,
                                targets: {
                                  browsers: [
                                    'Chrome >= 60',
                                    'Safari >= 10.1',
                                    'iOS >= 10.3',
                                    'Firefox >= 54',
                                    'Edge >= 15',
                                  ],
                                },
                              }],
                            ],
                          },
                        },
                      }],
                    },
                  };
                  

                  При запуске эти две конфигурации выведут на продакшн два JavaScript-файла:

                  • main.js (ES2015+ синтаксис)
                  • main-legacy.js (ES5 синтаксис)

                  Следующим шагом будет обновление вашего HTML для условной загрузки ES2015+ пакета (bundle) в браузерах, поддерживающих модули. Вы можете сделать это, используя script type="module" и script nomodule:

                  
                  
                  
                  
                  
                  
                  

                  Внимание! Единственная засада (gotcha) здесь — браузер Safari 10, который не поддерживает атрибут nomodule, но вы можете решить это, встроив JavaScript-сниппет в ваш HTML до использования любых тегов script nomodule. (Примечание: это было исправлено в Safari 11).

                  Важные моменты


                  По большей части описанная выше техника «просто работает», но перед ее реализацией важно знать несколько деталей о том, как загружаются модули:

                  1. Модули загружаются как script defer. Это означает, что они не выполняются до тех пор, пока документ не будет распарсен. Если какую-то часть вашего кода нужно запустить раньше, лучше разбить этот код и загрузить его отдельно.
                  2. Модули всегда запускают код в строгом режиме (strict mode), поэтому, если по какой-либо причине часть вашего кода должна быть запущена за пределами строгого режима, ее придется загружать отдельно.
                  3. Модули обрабатывают объявления верхнего уровня переменных (var) и функций (function) отлично от обычных сценариев. Например, к var foo = 'bar' и function foo() {…} в скрипте можно получить доступ через window.foo, но в модуле это не будет работать. Убедитесь, что в своем коде вы не зависите от такого поведения.

                  Рабочий пример


                  Я создал webpack-esnext-boilerplate, чтобы разработчики могли увидеть реальное применение описанной здесь техники.

                  В этот пример я намеренно включил несколько продвинутых фич webpack, поскольку хотел показать, что описанная мною техника готова к применению и работает в реальных сценариях. К ним относятся хорошо известные передовые практики, такие как:

                  • Разделение кода (Code splitting)
                  • Динамический импорт (Dynamic imports, условная загрузка дополнительного кода во время выполнения программы)
                  • Asset fingerprinting (для эффективного длительного кэширования)

                  И так как я никогда не буду рекомендовать то, что не использую сам, я обновил свой блог, чтобы использовать эту технику. Вы можете проверить исходный код, если хотите увидеть больше.

                  Если для создания пакетов (bundles) продакшн вы используете инструмент, отличный от webpack, этот процесс более или менее одинаковый. Я решил продемонстрировать описанную мной технику с помощью webpack, поскольку в настоящее время он является самым популярным инструментом для сборки, а также и самым сложным. Я полагаю, что если описанная мной техника может работать с webpack, она может работать с чем угодно.

                  Игра стоит свеч?


                  По-моему, определенно! Экономия может быть значительной. Например, ниже приведено сравнение общих размеров файлов для двух версий кода из моего блога:
                  Версия Размер (minified) Размер (minified + gzipped)
                  ES2015+ (main.js) 80K 21K
                  ES5 (main-legacy.js) 175K 43K

                  Устаревшая ES5-версия кода более чем в два раза превышает размер (даже gzipped) версии ES2015+.

                  Большие файлы занимают больше времени для загрузки, но они также занимают больше времени для анализа и оценки. При сравнении двух версий моего блога, время, затраченное на parse/eval, также было стабильно вдвое дольше для устаревшей ES5-версии (эти тесты выполнялись на Moto G4 с использованием webpagetest.org):
                  Версия Parse/eval time (по отдельности) Parse/eval time (среднее)
                  ES2015+ (main.js) 184ms, 164ms, 166ms 172ms
                  ES5 (main-legacy.js) 389ms, 351ms, 360ms 367ms

                  Хотя эти абсолютные размеры файлов и время parse/eval не особенно большие, поймите, что это блог, и я не загружаю много скриптов. Но для большинства сайтов это не так. Чем больше у вас скриптов, тем больше будет выигрыш, который вы получите, развернув код на ES2015+ в своем проекте.

                  Если вы все еще настроены скептически, и считаете, что размер файла и различия во времени выполнения в первую очередь связаны с тем, что для поддержки устаревших сред требуется много полифиллов, вы не совсем ошибаетесь. Но, к лучшему или худшему, сегодня в Интернете это очень распространенная практика.

                  Быстрый запрос данных HTTPArchive показывает, что из лучших сайтов по рейтингу Alexa, 85 181 включают в себя babel-polyfill, core-js или regenerator-runtime в своих пакетах (bundles) продакшн. Шесть месяцев назад их число было 34 588!

                  Реальность транспилируется, и в том числе полифиллы быстро становятся новой нормой. К сожалению, это означает, что миллиарды пользователей получают триллионы байтов, без необходимости отправленные через сеть в браузеры, которые изначально были бы вполне способны запускать нетранспилированный код.

                  Пришло время собирать наши модули как ES2015


                  Главная засада (gotcha) для описанной здесь техники сейчас состоит в том, что большинство авторов модулей не публикуют ES2015+ версии исходного кода, а публикуют сразу транспилированную ES5-версию.

                  Теперь, когда развертывание кода на ES2015+ возможно, пришло время изменить это.

                  Я полностью осознаю, что такой шаг сопряжен с множеством проблем в ближайшем будущем. Сегодня большинство инструментов сборки публикуют документацию, рекомендующую конфигурацию, которая предполагает, что все модули написаны на ES5. Это означает, что если авторы модулей начнут публиковать исходный код на ES2015+ в npm, то они, вероятно, сломают некоторые сборки пользователей и просто вызовут путаницу.

                  Проблема заключается в том, что большинство использующих Babel разработчиков настраивают его так, чтобы код в node_modules не транспилировался. Однако, если модули опубликованы с исходным кодом ES2015+, возникает проблема. К счастью, она легко исправима. Вам просто нужно удалить исключение node_modules из конфигурации сборки:

                  rules: [
                    {
                      test: /\.js$/,
                      exclude: /node_modules/, // удалите эту строку
                      use: {
                        loader: 'babel-loader',
                        options: {
                          presets: ['env']
                        }
                      }
                    }
                  ]
                  

                  Недостаток заключается в том, что если такие инструменты как Babel должны начать транспилировать зависимости (dependencies) из node_modules, в дополнение к локальным зависимостям, это замедлит скорость сборки. К счастью, эту проблему можно отчасти решить на уровне инструментария с постоянным локальным кэшированием.

                  Несмотря на удары, мы, скорее всего, пройдем путь к тому, что ES2015+ станет новым стандартом публикации модулей. Я думаю, что борьба стоит своей цели. Если мы, как авторы модулей, публикуем в npm только ES5-версии нашего кода, мы навязываем пользователям раздутый (bloated) и медленный код.

                  Публикуя код в ES2015, мы даем разработчикам выбор, что в конечном счете приносит пользу всем.

                  Заключение


                  Хотя script type="module" предназначен для загрузки модулей ES (и их зависимостей) в браузере, его не нужно использовать только для этой цели.

                  script type="module" будет успешно загружать единственный файл Javascript, и это даст разработчикам столь необходимое средство для условной загрузки современного функционала в тех браузерах, которые могут его поддерживать.

                  Это, наряду с атрибутом nomodule, дает нам возможность использовать код ES2015+ в продакшн, и наконец-то мы можем прекратить отправку транспилированного кода в браузеры, которые в нем не нуждаются.

                  Написание кода ES2015 — это победа для разработчиков, а внедрение кода ES2015 — победа для пользователей.
                  Original source: habrahabr.ru (comments, light).

                  https://habrahabr.ru/post/338612/


                  Метки:  

                  Вебинар «Fujitsu World Tour 2017 – Строим цифровое будущее вместе»

                  Понедельник, 25 Сентября 2017 г. 09:48 + в цитатник
                  FeeAR сегодня в 09:48 Администрирование

                  Вебинар «Fujitsu World Tour 2017 – Строим цифровое будущее вместе»

                    Привет Хабр! 19 сентября в Москве прошла конференция Fujitsu World Tour 2017. На вебинаре*, посвящённом конференции, который состоится 28 сентября в 12:00 (МСК) мы:
                    • расскажем об анонсах в ключевых направлениях: бизнес-ориентированный ЦОД и построение катастрофоустойчивых ИТ-систем
                    • проанализируем современные тенденции в сфере корпоративной мобильности
                    • поделимся практическим опытом оптимизации и развития ИТ-инфраструктуры на базе продуктов Fujitsu

                    Регистрируйтесь сейчас!

                    Спикеры: Евгений Тарелкин, менеджер по развитию бизнеса, сегмент х86 серверов, Fujitsu и Сергей Грибанов, менеджер по развитию бизнеса, сегмент клиентские системы, Fujitsu

                    После вебинара у вас будет возможность задать вопросы и обсудить текущие проблемы с нашим экспертом.

                    *Предварительная регистрация и подтверждение организаторов обязательны.
                    Original source: habrahabr.ru (comments, light).

                    https://habrahabr.ru/post/338646/


                    Метки:  

                    [recovery mode] Быстрый пул для php+websocket без прослойки nodejs на основе lua+nginx

                    Понедельник, 25 Сентября 2017 г. 09:37 + в цитатник
                    romy4 сегодня в 09:37 Разработка

                    Быстрый пул для php+websocket без прослойки nodejs на основе lua+nginx

                    nginx + lua

                    Кратко: nginx не умеет пулить websockets, а php работает per request. Нужна прослойка которая будет держать открытыми вебсокеты и при поступлении данных соединяться с php (через тот же fastcgi) и отправлять обратно ответ.

                    Тема, как оказалось, не нова, исходники тянуться аж из 2014, но, тем не менее, информации о трюке, про который здесь пойдёт речь, крайне мало. Можете погуглить "websockets php". Усугубляется тема ещё тем, что найденные примеры реализации (два, точнее) не работают, включая тот, что в документации :)


                    Вот где-то внутри чувствовал, знал, что есть. Мне настолько давно хотелось иметь этот Middleware внутри Nginx, чтобы не использовать разные довольно медленные php библиотеки (раз и два) и обойти стороной однопоточность nodejs. А вебсокетов хочется много (и как можно больше), и чтобы лишние затраты на прослойку были поменьше. Так вот, дабы не плодить кучу машин с nodejs (в будущем при высоких нагрузках так и поступают обычно), воспользуемся тем, что предоставляет Nginx с некоторыми пристройками в виде lua + resty. Nginx+lua можно установить из пакета nginx-extras или же собрать самому. От Resty нам понадобятся только websockets. Скачиваем и закидываем содержимое каталога lib куда-нибудь себе в пути (у меня это /home/username/lib/lua/lib, а по-хорошему надо бы в /usr/local/share/lua).

                    Стандартно nginx+websockets работает так:
                    1. Клиент соединяется с nginx
                    2. Nginx проксирует в upstream/открывает прокси поток с другим сервером (Middle Server на основе nodejs + sockets.io например), обслуживающим websockets.
                    3. Middle Server сервер кидает socket соединение в какой-нибудь слушатель событий типа epoll и ждёт данных.
                    4. При получении данных, Middle Server сервер, в свою очередь, открывает Fastcgi соединение с php, ожидает и забирает ответ. Отправляет его в socket. Возвращает socket снова в ожидание данных.
                    5. И так по кругу, пока не прийдёт специальный фрейм закрытия websocket.

                    Всё просто, кроме накладных расходов на ресурсы и однопоточность этого решения.

                    В предлагаемой схеме MiddleServer превращается в middleware внутри nginx. К тому же нет никакого ожидания Fastcgi, всю работу делает тот же epoll, к которому nginx доверяет открытый сокет, а тем временем поток nginx'a может заняться другими делами. Схема позволяет одновременно работать с кучей вебсокетов раскиданными по потокам.

                    Здесь приведу только упрощённый код, который относится к задаче без остальных настроек хостинга. Я не старался сделать правильными все заголовки за ненадобностью оных.
                    lua_package_path "/home/username/lib/lua/lib/?.lua;;";
                    
                    server {
                        # магия, которая держит вебсокет открытым столько, сколько нам надо внутри nginx
                        location ~ ^/ws/?(.*)$ {
                             default_type 'plain/text';
                             # всё что надо здесь для веб сокета - это включить луа, который будет его хендлить
                             content_by_lua_file /home/username/www/wsexample.local/ws.lua;
                       }
                    
                       # а это магия, которая отдаёт ответы от php
                       # я шлю только POST запросы, чтобы нормально передать json payload
                       location ~ ^/lua_fastcgi_connection(/?.*)$ {
                           internal; # видно только подзапросам внутри nginx
                            fastcgi_pass_request_body       on;
                            fastcgi_pass_request_headers    off;
                    
                            # never never use it for lua handler
                            #include snippets/fastcgi-php.conf;
                    
                            fastcgi_param  QUERY_STRING       $query_string;
                            fastcgi_param  REQUEST_METHOD     "POST"; # $request_method;
                            fastcgi_param  CONTENT_TYPE       "application/x-www-form-urlencoded"; #вместо $content_type;
                            fastcgi_param  CONTENT_LENGTH     $content_length;
                    
                            fastcgi_param  DOCUMENT_URI       "$1"; # вместо $document_uri
                            fastcgi_param  DOCUMENT_ROOT      $document_root;
                            fastcgi_param  SERVER_PROTOCOL    $server_protocol;
                            fastcgi_param  REQUEST_SCHEME     $scheme;
                            fastcgi_param  HTTPS              $https if_not_empty;
                    
                            fastcgi_param  GATEWAY_INTERFACE  CGI/1.1;
                            fastcgi_param  SERVER_SOFTWARE    nginx/$nginx_version;
                    
                            fastcgi_param  REMOTE_ADDR        $remote_addr;
                            fastcgi_param  REMOTE_PORT        $remote_port;
                            fastcgi_param  SERVER_ADDR        $server_addr;
                            fastcgi_param  SERVER_PORT        $server_port;
                            fastcgi_param  SERVER_NAME        $server_name;
                    
                            fastcgi_param SCRIPT_FILENAME "$document_root/mywebsockethandler.php";
                            fastcgi_param SCRIPT_NAME "/mywebsockethandler.php";
                            fastcgi_param REQUEST_URI "$1"; # здесь вообще может быть что угодно. А можно передать параметр из lua чтобы сделать какой-нибудь роутинг внутри php обработчика.
                            fastcgi_pass unix:/var/run/php/php7.1-fpm.sock;
                            fastcgi_keep_conn               on;
                           
                    }
                    


                    И код ws.lua:
                        local server = require "resty.websocket.server"
                    
                        local wb, err = server:new{
                            -- timeout = 5000,  -- in milliseconds -- не надо нам таймаут
                            max_payload_len = 65535,
                        }
                    
                        if not wb then
                            ngx.log(ngx.ERR, "failed to new websocket: ", err)
                            return ngx.exit(444)
                        end
                    
                        while true do
                            local data, typ, err = wb:recv_frame()
                    
                            if wb.fatal then return
                            elseif not data then
                                ngx.log(ngx.DEBUG, "Sending Websocket ping")
                                wb:send_ping()
                            elseif typ == "close" then
                                -- send a close frame back:
                                local bytes, err = wb:send_close(1000, "enough, enough!")
                                if not bytes then
                                    ngx.log(ngx.ERR, "failed to send the close frame: ", err)
                                    return
                                end
                                local code = err
                                ngx.log(ngx.INFO, "closing with status code ", code, " and message ", data)
                                break;
                            elseif typ == "ping" then
                                -- send a pong frame back:
                    
                                local bytes, err = wb:send_pong(data)
                                if not bytes then
                                    ngx.log(ngx.ERR, "failed to send frame: ", err)
                                    return
                                end
                            elseif typ == "pong" then
                                -- just discard the incoming pong frame
                    
                            elseif data then
                    
                                -- здесь в пути передаётся реальный uri, а json payload уходит в body
                                local res = ngx.location.capture("/lua-fastcgi-forward"..ngx.var.request_uri,{method=ngx.HTTP_POST,body=data})
                    
                                if wb == nil then
                                    ngx.log(ngx.ERR, "WebSocket instaince is NIL");
                                    return ngx.exit(444)
                                end
                    
                                wb:send_text(res.body)
                    
                            else
                                ngx.log(ngx.INFO, "received a frame of type ", typ, " and payload ", data)
                            end
                    end
                    


                    Что ещё можно с этим сделать? Замерить скорость и сравнить с nodejs :) А можно внутри lua делать запросы в Redis, MySQL, Postgres… проверять куки и прочие токены авторизации, обрабатывать сессии, кешировать ответы в memcached и потом быстро-быстро отдавать другим клиентам с одинаковыми запросами внутри websocket.

                    Известные мне недоработки: максимальный размер пакета данных по вебсокету 65Кб. При желании можно дописать разбитие на фреймы. Протокол не сложный.

                    Тестовый html (ws.html):
                    HTML тут
                    
                    
                    
                    	
                    
                    
                    
                    
                    
                    Message:




                    Тестовый php (mywebsockethandler.php):
                    PHP тут

                    https://habrahabr.ru/post/338614/


                    Метки:  

                    Квантовый компьютер IBM научили моделировать сложные химические элементы

                    Понедельник, 25 Сентября 2017 г. 09:30 + в цитатник
                    it_man сегодня в 09:30 Разработка

                    Квантовый компьютер IBM научили моделировать сложные химические элементы

                      В сентябре корпорация IBM объявила о новом серьезном достижении в области квантовых вычислений. Согласно данным компании, подготовленный исследователями алгоритм позволил работать с крупнейшей на сегодняшний день моделью молекулы, созданной на квантовом компьютере. Подобные эксперименты говорят о том, что квантовые процессоры, наконец, приближаются к возложенной на них практической задаче.

                      По словам Дарио Гила (Dario Gil), вице-президента исследований в сфере ИИ и IBM Q в IBM Research, она заключается в повышении нашего уровня знаний о явлениях природы.


                      / Flickr / IBM Research / CC

                      По большому счету, в создании симуляций сложных естественных процессов и заключается предназначение квантовых вычислений. Как известно, Google и IBM открыли доступ к своим облачным платформам квантовых вычислений для членов академической среды. Это означает, что исследователи со всего мира смогут присоединиться к работе над уникальными квантовыми задачами.

                      В работе над проектом по моделированию исследовательская группа IBM использовала квантовый процессор с семью кубитами. Объектами выступили гидрид лития и гидрид бериллия. «Квантовый подход» хорошо подошел для этой задачи — алгоритмы для химического моделирования действительно исправно работают на квантовых процессорах, по словам Робина Блюм-Кохута (Robin Blume-Kohout) из Sandia National Laboratories, лаборатории министерства энергетики США.

                      Тем не менее, наиболее точные симуляции в рамках аналогичных задач сегодня выполняют на классических компьютерах, что подтверждают и сами исследователи. Они оговариваются, что их процессор не лишен изъянов. Главный вызов для них — выведение квантовых вычислений на новый уровень и работа над высокоэффективными квантовыми алгоритмами. Команда уверена в том, что с развитием технологии квантовое моделирование сможет применяться для все более сложных химических задач. Тогда индустрия выйдет за рамки классических вычислений, а моделирование поможет в поиске новых лекарств и источников энергии.

                      В 1985 году Дэвидом Дойчем (David Deutsch) из Оксфордского университета были описаны первые попытки моделирования различных состояний с помощью квантовых вычислений. Однако первый жизнеспособный алгоритм был оформлен Питером Шором (Peter Shor) из Массачусетского технологического института почти 10 лет спустя.

                      Как отмечает научный журналист Филип Бол (Philip Ball) после этого идея сделать квантовый компьютер быстрее традиционного затмила изначальную цель, которая заключалась в изучении квантовой природы различных явлений.

                      Эксперимент IBM — это продолжение тех исследований, которые уже были проведены ранее. Например, летом 2016 года группа исследователей, возглавляемая профессорами Маркусом Рейером (Markus Reiher) и Маттиасом Троером (Matthias Troyer) из Швейцарской высшей технической школы Цюриха, прибегла к квантовым вычислениям в процессе изучения сложной химической реакции.

                      Корпоративные решения в этой сфере еще только набирают обороты. В гонке с IBM участвует лаборатория Google, которая уже пытается коммерциализировать свой продукт. Ряд исследователей получил доступ к платформе, и согласно официальным заявлениям корпорации, система продемонстрирует свое превосходство над действующими суперкомпьютерами уже к концу 2017 года.

                      Разрешение основных вопросов химического моделирования, по словам журналиста Филипа Бола, возможно, лежит в адиабатических квантовых вычислениях (AQC). Этот подход является основой D-Wave One — первого коммерческого квантового компьютера. Хотя его достоинства в качестве эффективного инструмента для квантовых вычислений ставятся под сомнение, исследователи со всего мира стараются получить к нему доступ.


                      / Flickr / Steve Jurvetson / CC

                      В прошлом году исследователи Google развили идею и представили прототип устройства, которое сочетает в себе AQC с «цифровым» подходом. На основе такого принципа работы в 2016 году Google удалось провести моделирование для молекулы водорода.

                      Так или иначе, во всех описанных случаях исследователи сталкивались с проблемой масштабирования. Для ее решения специалисты активным образом прорабатывают и совершенствуют алгоритмическую составляющую. Эту тему мы постараемся рассмотреть в одном из следующих материалов нашего блога на Хабре.

                      P.S. Несколько материалов из нашего блога на Хабре:


                      P.P.S. Материалы из «Первого блога о корпоративном IaaS»:

                      Original source: habrahabr.ru (comments, light).

                      https://habrahabr.ru/post/338616/


                      Метки:  

                      Инженерные системы наших дата-центров и их мониторинг, часть вторая

                      Понедельник, 25 Сентября 2017 г. 09:30 + в цитатник
                      NobleD5 сегодня в 09:30 Администрирование

                      Инженерные системы наших дата-центров и их мониторинг, часть вторая

                        Продолжение публикации, здесь первая часть



                        В этой заключительной части я расскажу о программной составляющей нашей системы мониторинга.

                        4. Trace Mode и с чем его едят
                        5. Крутые метрики и все-все-все
                        6. Подводя итоги

                        Trace Mode и с чем его едят


                        Повторюсь, изначально в в первом дата-центре выраженного мониторинга не было, а необходимость в нем была. И воплощать эту потребность решили сперва на базе уже строящегося «Миран-2», который планировался еще и модульным. Проектировщики и интеграторы предложили в качестве SCADA использовать отечественный Trace Mode. Данный продукт на тот момент мог удовлетворить все хотелки в плане мониторинга, был относительно простым в дальнейшей разработке (ежели бы такая необходимость возникла… и она-таки возникла) и стоил вроде бы не очень больших денег. В общем, неплохой вариант для простой системы.


                        АРМ дежурного ЦОД «Миран-2». Кликабельно

                        Trace Mode являет собой вполне классической образчик SCADA, имеет в себе ядро-сервер, опрашивающий циклично все необходимые железки по сети и клиент-консоли на АРМах дежурных, которые всю жизненную информацию от сервера и выводят, в виде различных мнемосхем. Такой вариант исполнения был использован для мониторинга «Миран-2» в целом. Для модульных ЦОД внутри (их пока у нас два) — был использован вариант с «тонкими» клиентами (java-апплет в браузере).


                        Фото панели с «тонким» клиентом в браузере и панели с клиент-консолью. Кликабельно

                        Кратко расскажу о внутренней структуре проектов. Есть условно два уровня:

                        • нижний уровень, опрос устройств. Осуществляется «Источниками/Приемниками» — некие структурные шаблоны, которые определяют различные протоколы, технологии и интерфейсы (Modbus RTU/TCP-IP, SNMP, DDE, OPC etc.), содержат настройки связи. В общем, являются софтварным отражением периферии.
                        • верхний уровень, тэги. В Trace Mode они называются «Каналами». Эти шаблоны уже определяют тип параметра, получаемого от «Источников» (дискретный/аналоговый), задают для него масштабирование, аварийные/предаварийные пределы (для аналоговых сигналов), назначают привязку к словарям аварийных сообщений, наконец, «каналы» же устанавливают будет ли данный параметр архивироваться или нет. Соответственно, к различным графическим элементам на мнемосхемах эти «каналы» можно привязать для оперативного мониторинга.



                        Trace Mode IDE. «Источники/Приемники». Кликабельно


                        Trace Mode IDE. «Каналы». Кликабельно

                        Это и есть ядро SCADA.
                        Конечно же в Trace Mode есть также возможность писать подпрограммы на общепринятых промышленных языках (ST, LD, FBD), создавать отчеты, рассылать SMS и E-mail.

                        На заметку.
                        Все продукты в семействе Trace Mode защищены HASP-ключами. Для работы в IDE требуется свой ключ, лимитирующий в проекте количество источников данных (e.g. лицензия на 128, 256, 512… N устройств). Для работы МРВ требуется свой ключ. Он лимитирует максимальное количество «каналов» в скомпилированном проекте; в подмножество каналов, помимо самих каналов, входят и вызовы программ, шаблонов экранов. Также ключ определяет доступность некоторых технологий, у нас, в частности, возможность запуска OPC-сервера Trace Mode. Для клиент-консолей, которые используются в АРМах, ключ лимитирует число экранов (в проекте дюжина  мнемосхем, а ключ на десять? Два экрана перестанут вызываться). «Тонкие» клиенты? Ну вы поняли, ограничения на кол-во одновременных подключений, шаблонов документов...

                        Изначально, мониторинг от интегратора был довольно прост. Самый минимум: данные о состоянии вводных и вспомогательных автоматов, данные по энергопотреблению, показания температурных датчиков и датчиков влажности, состояние кондиционеров и ИБП, а также (один из важнейших параметров) — по-стоечный расчет энергопотребления.

                        Т.к. система, конечно же, была неидеальна и страдала от всяческих недоработок, ее нужно было поддерживать и совершенствовать. Здесь на сцене и появляется ваш покорный слуга. Основной моей задачей стало свести количество ошибок в системе к минимуму, а в последующей перспективе — добавить новых глюков фич, которые должны облегчить эксплуатацию всей инженерной инфраструктуры наших дата-центров.

                        Перво-наперво, система мониторинга была «причесана и вылизана», а именно: исправлены всяческие «очепятки», приведены в соответствие порядок чисел (200 градусов Цельсия в холодном коридоре превращаются в 20,0), найден консенсус, в чем же мы меряем потребление в стойках — в кВт или все-таки в кВА. Спойлер!

                        Вторым эшелоном шли исправления стратегические. Например, в первоначальной версии проекта первого модульного ЦОД не было никакой индикации о состоянии его ИБП, в норме ли они или уже отключились в аварийном состоянии из-за поломки. Был расширен список возможных аварийных ситуаций и сообщений о многих узлах ЦОД. Мнемосхемы обрастали различными значками, индикаторами и дополнительными параметрами, в общем, всем, что должно было помочь понять — здоров или нет дата-центр.


                        Основная мнемосхема ЦОД «Миран-2»


                        Основная мнемосхема ЦОД «Миран-1»


                        Мнемосхема состояния ИБП узла связи «Миран-2»


                        Мнемосхема ДГУ-1 «Миран-2»


                        Всплывающая мнемосхема модульного ЦОД «Модуль-2»

                        Огромные панели с мигающими мнемосхемами — это очень круто и хорошо, но дежурный все же человек, которому свойственно уставать, забывать и не замечать. Системы мобильных ЦОД не имеют, к сожалению, журнала тревог, поэтому было решено реализовать рассылку аварийных писем, как службе главного инженера, так и в тикетную систему нашей тех.поддержки. В последствии, к этому еще добавилась и оповещающая сирена.

                        Крутые метрики и все-все-все


                        После года шлифовки проектов Trace Mode, был достигнут некий удобоваримый компромисс между «хотим красиво, современно и круто» и «реализовали как смогли и как получилось». В целом, система справлялась с мониторингом и оповещением по текущему состоянию, но хотелось иметь и возможность простейшего анализа климатики и энергопотребления.

                        Т.к. системы модульных ЦОД были оснащены только лишь «тонкими» клиентами и графиков и трендов они не поддерживали (опять же), хоть какой-то анализ был выполнен в виде суточных отчетов на E-mail`ы службы главного инженера (с простейшими табличками, заполненных мин/максами значений по датчикам температур и энергопотребления стоек). Наглядность, впрочем, все равно оставляла желать лучшего. Ко всему прочему, еще одним камнем преткновения стала нестабильная работа собственных архивов Trace Mode, из которых эти данные извлекались.

                        Перебрав несколько вариантов решения всего этого безобразия, было решено остановиться на варианте с отгрузкой данных из Trace Mode во внешнюю БД для дальнейшей обработки.

                        Когда я уже хотел приступать к реализации вышеозначенного варианта, наш главный инженер наткнулся на просторах интернета на сайт grafana. Дружно повздыхав над красотой графиков,  мы сошлись на том, что-де реализовать подобное под наши нужды на текущей платформе — затруднительно. Тем не менее, grafana крепко засела у меня в голове и я стал искать любые гайды с описанием реализованных решений с ее участием. Переломными стали несколько статей на хабре: 1 и 2  (Хабр окрыляет помогает!) с упоминанием демона collectd и его плагинов.

                        Теперь уже вполне себе вызрела идея как все это реализовать под наши нужды.



                        Под все это дело был выделен скучающий от низкой нагрузки blade-сервер и развернута тестовая Ubuntu со всем необходимым набором софта. После этого настал черед заполнить конфиг collectd на предмет что и как ему опрашивать. Ниже некоторые выдержки из конфига. Ничего особенного, все строго в соответствии с документацией по демону и плагинам:

                        Содержимое файла конфигурации для collectd
                        # Config file for collectd(1).
                        #
                        # Some plugins need additional configuration and are disabled by default.
                        # Please read collectd.conf(5) for details.
                        #
                        # You should also read /usr/share/doc/collectd-core/README.Debian.plugins
                        # before enabling any more plugins.
                        
                        Hostname "graphite"
                        FQDNLookup true
                        #BaseDir "/var/lib/collectd"
                        #PluginDir "/usr/lib/collectd"
                        TypesDB "/usr/share/collectd/types.db" "/etc/collectd/my_types.db"
                        Interval 10
                        #Interval 60
                        #Timeout 2
                        #ReadThreads 5
                        
                        LoadPlugin logfile
                        LoadPlugin cpu
                        LoadPlugin disk
                        LoadPlugin memory
                        LoadPlugin modbus  //тот самый плагин
                        LoadPlugin snmp
                        LoadPlugin write_graphite
                        
                        #LoadPlugin email
                        #LoadPlugin sensors
                        #LoadPlugin serial
                        
                        
                            LogLevel "info"
                            File STDOUT
                            Timestamp true
                            PrintSeverity true
                        
                        
                        
                        
                        #DC2 VRU Data -------------------------------------------------
                        
                         
                           RegisterBase 380
                           RegisterType int16
                           Type word
                           Instance "VRU-QF1-Status"
                         
                        
                         
                           RegisterBase 381
                           RegisterType int16
                           Type word
                           Instance "VRU-QF2-Status"
                         
                           RegisterBase 300
                           RegisterType int16
                           Type voltage
                           Instance "VRU1-U-AN"
                         
                        
                         
                           RegisterBase 301
                           RegisterType int16
                           Type voltage
                           Instance "VRU1-U-BN"
                         
                        
                         
                           RegisterBase 302
                           RegisterType int16
                           Type voltage
                           Instance "VRU1-U-CN"
                         
                        
                         
                           Address "XXX.XXX.XXX.XXX"
                           Port    "502"
                           Interval 5
                           
                           
                            Instance "Vars"
                            Collect  "VRU-QF1-Status"
                            Collect  "VRU-QF2-Status"
                        ...
                            Collect  "VRU1-U-AN"
                            Collect  "VRU1-U-BN"
                            Collect  "VRU1-U-CN"
                        ...
                           
                         
                        
                        
                        # DC2_Module1_UPS1 -------------------------------------------------
                         
                          Type "percent"
                          Table false
                          Instance "Load_A"
                          Values ".1.3.6.1.2.1.33.1.4.4.1.5.1"
                         
                        
                         
                          Type "percent"
                          Table false
                          Instance "Load_B"
                          Values ".1.3.6.1.2.1.33.1.4.4.1.5.2"
                         
                        
                         
                          Type "percent"
                          Table false
                          Instance "Load_C"
                          Values ".1.3.6.1.2.1.33.1.4.4.1.5.3"
                         
                        ...
                         
                           Address "XXX.XXX.XXX.XXX"
                           Version 1
                           Community "public"
                           Collect "UPS1_load_A"
                           Collect "UPS1_load_B"
                           Collect "UPS1_load_C"
                        ...
                           Interval 5
                         
                        
                        
                        	
                        		Host "localhost"
                        #		Port "2003"
                        		Prefix "collectd."
                        		Protocol "tcp"
                        #		Postfix "collectd"
                        #		StoreRates false
                        #		AlwaysAppendDS false
                        #		EscapeCharacter "_"
                        	
                        
                        
                        Include "/etc/collectd/collectd.conf.d/*.conf"
                        




                        Дашборд главного ВРУ «Миран-2». Кликабельно


                        Дашборд с наиболее важными параметрами «Модуль-2». Кликабельно


                        Дашборд с климатическими трендами «Модуль-2». Кликабельно


                        Дашборд с трендами по потреблению стоек «Модуль-1». Кликабельно

                        Подводя итоги


                        Итак, текущие плюсы решения на collectd + graphite + grafana в сравнении с Trace Mode:

                        1. Бесплатно (финдир вытирает скупую мужскую слезу счастья).
                        2. Open Source. Можно теоретически добавить недостающую фичу, написав ее самому.
                        3. Доступность. По сути, это страничка в браузере для конечного пользователя, а, следовательно, есть у каждого в гаджете в кармане. В Trace Mode поддержки для гаджетов толком нет.
                        4. Простота и удобство расширения. Достаточно при первоначальной настройке collectd + graphite «скормить» им все необходимые данные — и последующие получившиеся метрики можно редактировать и преобразовывать на лету прямо в grafana. Скажем «Нет!» компиляциям МРВ и клиент-консолей в Trace Mode!
                        5. Очень неплохие возможности по отображению и анализу графиков «из коробки». Trace Mode в этом плане крайне, хм, консервативен.
                        6. Есть оповещения и уведомления об аварийных ситуациях во всех новомодных чатиках, по почте etc. Trace Mode же может рассылать E-mail`ы и за отдельную денежку — SMS (если у вас есть необходимое железо).

                        Минусы:

                        1. Полновесную SCADA подобной связкой не заменить. Никакого управления тех.процессом. Если, конечно, управление Вам необходимо.
                        2. Open Source. Ваш покорный слуга не имеет надлежащей квалификации для дописания хотелок, а посему смиренно ждет и/или просит более умных товарищей в git-сообществе.
                        3. Набор панелей невелик (хоть и расширяется за счет плагинов).
                        4. Движок алертинга пока очень прост, хитрых условий в нем не настроишь. Разработчики обещают расширить функционал.

                        Пока решено оставить систему мониторинга неким гибридом из классической SCADA Trace Mode со своими клиент-приложениями и серверами как скрытого от посторонних ядра с АСУ и АСМ и внешней обертки grafana с красивыми и удобными метриками, доступной всем внутри корпоративной сети. К чему в итоге мы придем — покажет время, разных инженерных задач еще хватает.

                        Буду рад вопросам. Спасибо за внимание!
                        Original source: habrahabr.ru (comments, light).

                        https://habrahabr.ru/post/336222/


                        Метки:  

                        iOS+Kotlin. Что можно сделать сейчас

                        Понедельник, 25 Сентября 2017 г. 09:17 + в цитатник
                        adev_one сегодня в 09:17 Разработка

                        iOS+Kotlin. Что можно сделать сейчас

                          В ветке master проекта Kotlin Native появился пример uikit. Это простое приложение под iOS, которое выводит на экран строку, введённую в поле ввода, и да, 100% кода написано на Kotlin. Выглядит оно так:



                          Стоит ли думать о порте своего приложения уже сейчас?


                          Да, но только если:
                          0). Вам действительно нужна общая кодовая база мобильных приложений.
                          1). Приложение мало завязано на платформу.
                          2). У Вас есть время на написание некоторого количества кода на Kotlin, который в будущем стоит переписать на Objective-C или Swift.

                          Причины пока не портировать


                          ViewController, AppDelegate и даже main-функция в примере написаны на Kotlin. Те файлы, которые написаны на Objective-C нужны только чтобы XCode не выдавал ошибку и не включаются в конечную сборку (я не нашёл способов исправить положение). Т.е. полноценный interop как с Java, видимо, пока что, недоступен. Это совсем не значит, что положение дел не изменится к релизу (сейчас проект на стадии alpha preview, а об этом примере даже поста в блоге не было). Но спектр доступных сейчас возможностей довольно ограничен.

                          Interop


                          Идиоматический подход к написанию мультиплатформенного приложения на Kotlin — отдельно написать общую часть, отдельно — часть для каждой платформы. При этом на каждой платформе, по задумке, должны быть легко доступны все библиотеки, под неё написанные. В случае с Java работает хорошо. В случае с iOS дела сейчас обстоят следующим образом:

                          @ExportObjCClass
                          class KotlinViewController : UIViewController {
                          
                              constructor(aDecoder: NSCoder) : super(aDecoder)
                              override fun initWithCoder(aDecoder: NSCoder) = initBy(KotlinViewController(aDecoder))
                          
                              @ObjCOutlet
                              lateinit var label: UILabel
                          
                              @ObjCOutlet
                              lateinit var textField: UITextField
                          
                              @ObjCOutlet
                              lateinit var button: UIButton
                          
                              @ObjCAction
                              fun buttonPressed() {
                                  label.text = "Konan says: 'Hello, ${textField.text}!'"
                              }
                          }

                          То есть вполне неплохо. К каждому внешнему классу добавляем аннотацию @ExportObjCClass, к каждому графическому элементу из storyboard — @ObjCOutlet и @ObjCAction для каждого action. Классы на Objective-C доступны по их оригинальным именам.

                          Если нужно вызвать Kotlin из Objective-C/Swift


                          В этой статье описано, как это можно сделать. Через некоторое количество прослоек, с ручным преобразованием типов 2 раза, но зато можно звать Swift из Kotlin и Kotlin из Swift.

                          Overhead


                          В теории, вес приложения должен увеличиться примерно на 100 кб (отсюда).
                          Вместо GC будет использоваться ARC, так что особой разницы в производительности со Swift быть не должно.

                          Обратная совместимость


                          Судя по докладам участников команды разрабатывающей язык, обратная совместимость — один из их основных приоритетов. Насколько это хорошо — судить вам. Лично я считаю, что это намного лучше, чем у Swift и, в целом, язык хорош и большинство паззлеров выглядят надуманными. Но есть 1 вещь, которая, по моему мнению, может быть «бомбой замедленного действия», при этом не может быть исправлена с соблюдением обратной совместимости.

                          inline


                          Для реализации сопрограмм, которые делают так, чтобы синхронный и асинхронный код выглядели почти одинаково в язык было введено всего одно новое ключевое слово suspend, чем разработчики заслуженно гордятся. Но для того чтобы методы-расширения (forEach, map...) работали так же быстро, как и обычный for (и для вывода общих типов во время исполнения программы), было введено целых 3 (inline, crossinline, noinline). Они явно не делают код читаемее. JIT теряет часть возможностей для оптимизации (подкаст об этом), а опыт C показывает, что разработчики не умеют правильно пользоваться такими возможностями языка. В целом, не понимаю, почему то же самое нельзя было сделать аннотацией. Для меня inline выглядит как плохое решение достойной проблемы.

                          Заключение


                          — На Kotlin скоро можно будет писать под все 3 основные платформы (Android, iOS, Web).
                          — Скорее всего, будет хорошая совместимость с Objective-C и Swift. Возможно лучше, чем та, что есть между этими языками. Учитывая опыт JetBrains в разработке компиляторов и IDE, в это можно поверить.
                          — У Kotlin легковесный Runtime языка под Android и Web. Под iOS, судя по всему, тоже будет не тяжёлым.
                          — Уже сейчас можно что-нибудь написать.
                          Original source: habrahabr.ru (comments, light).

                          https://habrahabr.ru/post/337958/


                          Метки:  

                          [Перевод] Overview of Cryptoeconomics. Перевод статьи

                          Понедельник, 25 Сентября 2017 г. 08:24 + в цитатник
                          NikMelnikov сегодня в 08:24 Разработка

                          Overview of Cryptoeconomics. Перевод статьи

                          • Перевод
                          От переводчика: это научный текст, немного вырванный из контекста, но очень и очень полезный. Хотел опубликовать его у себя в блоге, но решил сначала поделиться с сообществом здесь. Если есть предложения, как какие-то предложения можно перевести лучше, пишите, пожалуйста в личку.

                          Наука о криптографии существует уже тысячелетия, но в формальной и систематизированной форме — всего пару десятилетий, и может быть определена как исследование коммуникации в состязательной среде (Rabah, 2004).


                          Аналогичным образом, мы можем определить криптоэкономику как концепцию, которая идет на один шаг дальше, т.е., изучение экономического взаимодействия в состязательной среде (Davidson, De Filippi & Potts,2016; Ernst, 2016). Чтобы отличить себя от традиционной экономики, которая, безусловно, изучает, как экономическое взаимодействие, так и противодействие, криптоэкономика обычно фокусируется на взаимодействиях, которые происходят по сетевым протоколам. Отдельные области криптоэкономики включают:
                          • Онлайн доверие и репутацию систем;
                          • Крипто-токены/ криптовалюты, и, в целом, цифровые активы;
                          • Смарт-контракты;
                          • Согласованные алгоритмы;
                          • Алгоритмы анти-спама и алгоритмы, устойчивые к атакам Сибиллы;
                          • Активизированные рынки для вычислительных ресурсов;
                          • Децентрализованные системы социального обеспечения /соц. помощи/ основного дохода; Децентрализованное управление (как для коммерческих, так и для некоммерческих организаций).


                          В течение нескольких последних лет мы стали свидетелями роста криптоэкономики,
                          что в значительной мере связано с увеличением количества криптовалют и цифровых токенов, которые привносят новые и интересные аспекты в такую науку как криптография (Potts, Davidson & De Filippi, 2016). Немногим ранее криптография была, по большому счету, простой вычислительной и информационной теоретической наукой, безопасность которой считали наиболее близкой к абсолютной.

                          Как только деньги попадают в поле зрения, идеальный мир математики должен взаимодействовать с реальным миром и социальными структурами общества, экономическими стимулами, относительной верой и многими уязвимостями, которые возможно только уменьшить,
                          но не удалить полностью. В то время, как криптографы привыкли к допущению типа «этот алгоритм гарантированно будет нерушимым при условии, что основные математические проблемы остаются неизменными», мир криптоэкономики вынужден довольствоваться неясными эмпирическими факторами, такими как, сложность при большом количестве атак, достаточное количество бескорыстных, а также заинтересованных в прибыли сторон, уровень концентрации различных ресурсов, и даже учитывать социально-культурные условия (Ernst, 2016; Davidson, De Filippi & Potts, 2016).

                          Напротив, в традиционной прикладной криптографии, меры безопасности склоняются к виду:
                          — Никто не может выполнить более чем 279 вычислительных шагов;
                          — Факторные операции остаются неизменными (т.е. полиномиальными) (Rabah,2005);
                          — Принятые n-ные корни композитных модулей неизменны;
                          — Задача дискретного логарифма эллиптической кривой не может быть решена быстрее, чем в 2n/2 раза;

                          С другой стороны, в криптоэкономике, основные меры безопасности от которых мы зависим выглядят примерно следующим образом (Ernst, 2016):
                          — Никто из лиц, контролирующих более 25% всех вычислительных ресурсов не могут вступать в сговор;
                          — Никто из лиц, контролирующих более 25% всех денежных ресурсов не могут вступать в сговор;
                          — Сумма вычислений определенного доказательства рабочей функции, которая может быть выполнена с заданной суммой денежных средств, не является сверхлинейной за пределами точки, которая расположена достаточно низко.
                          — Существует незначительное количество альтруистов и такое же незначительное количество сумашедших или политических оппонентов, поэтому большинство пользователей могут быть смоделированы как экономически рациональные.
                          — Количество пользователей в системе великое множество, и в любой момент пользователи могут появляться и исчезать, в то же время, по крайней мере, некоторые из них будут постоянными.
                          — Цензура невозможна, и передача сообщений между двумя узлами происходит относительно быстро.
                          — Достаточно легко сгенерировать множество IP-адресов, которые дают неограниченную
                          пропускную способность сети.
                          — Большинство пользователей анонимны, в связи с этим негативная репутация и появление долгов практически неосуществимы.

                          В связи с этим важно отметить, что существуют дополнительные предположения, касающиеся безопасности, характерные для возникающих проблем. Таким образом, нередко, даже невозможно, с уверенностью сказать, что возникшая проблема решена. Правильнее будет сказать, что необходимо будет создавать пути решения, оптимизированные для конкретных эмпирических и социальных реалий, и со временем продолжать их оптимизировать. (Ernst, 2016).
                          Перевод осуществляла Елена Логачева, корректировал Никита Мельников.
                          Original source: habrahabr.ru (comments, light).

                          https://habrahabr.ru/post/338638/


                          Метки:  

                          Библиотека быстрого поиска путей на графе

                          Понедельник, 25 Сентября 2017 г. 07:01 + в цитатник
                          anvaka сегодня в 07:01 Разработка

                          Библиотека быстрого поиска путей на графе

                            Привет, Друзья!


                            Я написал библиотеку поисков путей на произвольных графах, и хотел бы поделиться ей с вами: http://github.com/anvaka/ngraph.path


                            Пример использования на огромном графе:





                            Поиграться с демо можно здесь: https://anvaka.github.io/ngraph.path.demo/


                            В библиотеке используется мало-известный вариант A* поиска, который называется NBA*. Это двунаправленный поиск, с расслабленными требованиями к функции-эвристике, и очень агрессивным критерием завершения. Не смотря на свою малоизвестность у алгоритма отличная скорость сходимости к оптимальному решению.


                            Описание разных вариантов A* уже не раз встречалось на хабре. Мне очень понравилось вот это, потому повторяться в этой статье я не буду. Под катом расскажу подробнее почему библиотека работает быстро и о том, как было сделано демо.


                            Почему библиотека работает быстро?


                            "Как-то не верится что так быстро. Ты точно ниче не считаешь предварительно?"
                            Реакция друга, который первый раз увидел библиотеку.

                            Сразу должен признаться, я не верю что моя реализация — самая быстрая из возможных. Она работает достаточно быстро учитывая окружение в котором находится (браузер, javascript). Ее скорость сильно будет зависеть от размера графа. И, конечно же, то, что сейчас есть в репозитории можно ускорить и улучшить.


                            Статистика


                            Для замера производительности я взял граф дорог из Нью-Йорка ( ~730 000 ребер, 260 000 узлов). Таблица ниже показывает статистику времени, необходимого для решения одной задачи поиска пути из 250 случайно выбранных:


                            Среднее Медиана Min Max p90 p99
                            A* ленивый (локальный) 32ms 24ms 0ms 179ms 73ms 136ms
                            NBA* 44ms 34ms 0ms 222ms 107ms 172ms
                            A*, однонаправленный 55ms 38ms 0ms 356ms 123ms 287ms
                            Дейкстра 264ms 258ms 0ms 782ms 483ms 631ms

                            Каждый алгоритм решал одну и ту же задачу. A* ленивый самый быстрый, но его решение не всегда оптимально. По-сути, это двунаправленный A* который сразу же выходит как только оба поиска встретились. NBA* двунаправленный, сходится к оптимальному решению. В 99% ему понадобилось меньше чем 172 миллисекунды, чтобы найти кратчайший путь (p99).


                            Оптимизации


                            Библиотека работает относительно быстро по нескольким причинам.


                            Во-первых, я изменил структуру данных в приоритетной очереди таким образом, что обновление приоритета любого элемента очереди занимает O(lg n) времени. Это достигается тем, что каждый элемент отслеживает свою позицию в куче во время перестройки очереди.


                            Во-вторых, во время нагрузочных тестов я заметил, что уборка мусора занимает значительное время. Это не удивительно, поскольку алгоритм создает много маленьких объектов когда он ходит по графу. Решается проблема со сборщиком мусора при помощи пула объектов. Это структура данных которая позволяет повторно использовать объекты, когда они уже не нужны.


                            Ну и наконец, алгоритм поиска NBA* имеет очень красивый и жесткий критерий посещения узлов.


                            Признаться, я думаю что это не предел совершенства. Вполне вероятно, если использовать иерархический подход, описанный Борисом удастся ускорить время для еще больших графов.


                            Как работает демо?


                            Создание библиотеки это, конечно, очень интересно. Но мне кажется демо-проект заслуживает отдельного описания. Я усвоил несколько уроков, и хотел бы поделиться с вами, в надежде, что это окажется полезным.


                            Прежде чем начнем. Кто-то меня спросил: "Но ведь это же граф? Как можно карту представить в виде графа?". Легче всего представить каждый перекресток узлом графа. У каждого перекрестка есть позиция (x, y). Каждый прямой участок дороги сделаем ребром графа. Изгибы дороги можно моделировать как частный случай перекрестков.


                            Готовим данные


                            Конечно, я слышал об https://www.openstreetmap.org, но их внешний вид меня не сильно привлекал. Когда же я обнаружил API и инструменты типа http://overpass-turbo.eu/ — это как новый мир открылся перед глазами :). Данные они отдают под лицензией ODbL, которая требует чтобы их упомянули (чем больше людей знают о сервисе — тем лучше становится сервис).


                            API позволяет делать очень сложные запросы, и дает потрясающие объемы информации.


                            Например, такой запрос даст все велодороги в Москве:


                            [out:json];
                            // Сохранить область в переменную `a`
                            (area["name"="Москва"])->.a;
                            // Скачать все дороги внутри a у которых аттрибут `highway == cycleway`
                            way["highway"="cycleway"](area.a);
                            
                            // и объединить дороги с узлами графа (узлы содержат геопозицию)
                            node(w);
                            
                            // Наконец, вернуть результаты
                            out meta;

                            API очень хорошо описано здесь: http://wiki.openstreetmap.org/wiki/Overpass_API


                            Я написал три маленьких скрипта, чтобы автоматизировать получение дорог для городов, и сохранять их в мой формат графа.


                            Сохраняем граф


                            Данные OSM отдает в виде XML или JSON. К сожалению оба форматы слишком объемные — карта Москвы со всеми дорогами занимает около 47MB. Моя же задача была сделать загрузку сайта как можно быстрее (даже на мобильном соединении).


                            Можно было бы попробовать сжать gzip'ом — карта Москвы из 47МБ превращается в 7.1МБ. Но при таком подходе у меня не было бы контроля над скоростью распаковки данных — их бы пришлось парсить javascript'ом на клиенте, что тоже повлияло бы на скорость инициализации.


                            Я решил написать свой формат для графа. Граф разбивается на два бинарных файла. Один с координатами всех вершин, а второй с описанием всех ребер.


                            Файл с координатами — это просто последовательность из x, y пар (int32, 4 байта на координату). Смещение по которому находится пара координат я рассматриваю как иденификатор вершины (nodeId).


                            координаты


                            Ребра графа превращаются в обычную последовательность пар fromNodeId, toNodeId.


                            ребра


                            Последовательность на картинке означает, что первый узел ссылается на второй, а второй ссылается на третий, и так далее.


                            Общий размер для графа с V узлами и E ребрами можно подсчитать как:


                             storage_size = V * 4 * 2 +  # 4 байта на пару координат на узел
                                            E * 4 * 2 =  # 4 байта на пару идентификаторов вершин
                                            (V + E) * 8  # суммарно, в байтах

                            Это не самый эффективный способ сжатия, но его очень легко реализовать и можно очень быстро восстановить начальный граф на клиенте. Типизированные массивы в javascript'e работают быстрее, чем парсинг JSON'a.


                            Сначала я хотел добавить также вес ребер, но остановил себя, ибо загрузка на слабом мобильном соединении даже для маленьких графах станет еще медленнее.


                            В первую очередь мобильные телефоны


                            Когда я писал демо, я думал, что напишу о нем в Твиттер. Твиттер большинство людей читают с мобилок, а потому и демо должно быть в первую очередь рассчитано на мобильные телефоны. Если оно не будет загружаться быстро, или не будет поддерживать touch — пиши пропало.


                            Спустя пару дней после анонса, можно признать логику выше оправданной. Твит с анонсом демо стал самым популярным твитом в жизни моего твиттера.


                            Я тестировал демо в первую очередь на платформах iPhone и Андроид. Для тестов на Андроиде я нашел самый дешевый телефон и использовал его. Это очень сильно помогло с отладкой производительности и удобства использования на маленьком экране.


                            Асинхронность


                            Самая медленная часть в демо была начальная загрузка сайта. Код, который инициализировал граф выглядел как-то так:


                            for (let i = 0; i < points.length; i += 2) {
                                let nodeId = Math.floor(i / 2);
                            
                                let x = points[i + 0];
                                let y = points[i + 1];
                            
                                // graph это https://github.com/anvaka/ngraph.graph
                                graph.addNode(nodeId, { x, y })
                            }

                            На первый взгляд — ничего плохого. Но если запустить это на слабеньком процессоре и на большом графе — страничка становится мертвой, пока основной поток занят итерацией.


                            Выход? Я знаю, некоторые используют Web Workers. Это прекрасное решение, учитывая что все сейчас многоядерное. Но в моем случае, использование web workers значительно бы продлило время, необходимое для создания демо. Нужно было бы продумать как передавать данные между потоками, как синхронизировать, как сохранить жизнь батарее, как быть когда web workers не доступны и т.д.


                            Поскольку мне не хотелось тратить больше времени, нужно было более ленивое решение. Я решил просто разбить цикл. Просто запускаем его на некоторое время, смотрим сколько времени прошло, и потом вызываем setTimeout() чтобы продолжить на следующей итерации цикла событий. Все это сделано в библиотеке rafor.


                            С таким решением у браузера появляется возможность постоянно информировать пользователя о том, что происходит внутри:


                            прогресс


                             Отрисовка


                            Теперь, когда у нас загружен граф, нужно показать его на экране. Конечно, использовать SVG для отрисовки миллиона элементов не годится — скорость начнет проседать после первого десятка тысяч. Можно было бы нарезать граф на тайлы, и использовать Leaflet или OpenSeadragon чтоб нарисовать большую картинку.


                            Мне же хотелось иметь больше контроля на кодом (и подучить WebGL), поэтому я написал свой WebGL отрисовщик с нуля. Там я использую подход "scene graph". В таком подходе мы строим сцену из иерархии элементов, которые можно нарисовать. Во время отрисовки кадра, мы проходим по графу, и даем возможность каждому узлу накопить трансформации или вывести себя на экран. Если вы знакомы с three.js или даже обычным DOM'ом — подход будет не в новинку.


                            Отрисовщик доступен здесь, но я намеренно не документировал его сильно. Это проект для моего собственного обучения, и я не хочу создавать впечатление, что им можно пользоваться :)


                            Батарейка


                            батарейка


                            Изначально, я перерисовывал сцену на каждом кадре. Очень быстро я понял, что это сильно греет телефон и батарея уходит в ноль с примечательной скоростью.


                            Писать код при таких условиях было так же неудобно. Для работы над проектом, я обычно заседал в кофейне в свободное время, где не всегда были розетки. Поэтому мне нужно было либо научиться думать быстрее, либо найти способ не сажать ноутбук так быстро.


                            Я до сих пор не нашел способ как думать быстрее, потому я выбрал второй вариант. Решение оказалось по-наивному простым:


                            Не рисуй сцену на каждом кадре. Рисуй только когда попросили, или когда знаешь, что она поменялась.

                            Может, это покажется слишком очевидным сейчас, но это было вовсе не так сначала. Ведь в основном все примеры использования WebGL описывают простой цикл:


                            function frame() {
                                requestAnimationFrame(frame); // Планируем следующий кадр
                            
                                renderScene(); // рисуем текущий кадр.
                                // Ничего плохого в этом нет, но батарею мы можем так быстро посадить
                            }

                            С "консервативным" подходом, мне нужно было вынести requestAnimationFrame() наружу из функции frame():


                            let frameToken = 0;
                            
                            function renderFrame() {
                                if (!frameToken) frameToken = requestAnimationFrame(frame);
                            }
                            
                            function frame() {
                                frameToken = 0;
                                renderScene();
                            }

                            Такой подход позволяет кому угодно затребовать нарисовать следующий кадр. Например, когда пользователь перетащил карту и изменил матрицу преобразования, мы вызываем renderFrame().


                            Переменная frameToken помогает избежать повторного вызова requestAnimationFrame между кадрами.


                            Да, код становится немного сложнее писать, но жизнь батарее для меня была важнее.


                            Текст и линии


                            WebGL не самый простой в мире API. Особенно сложно в нем работать с текстом и толстыми линиями (у которых ширина больше одного пикселя).


                            текст и линии


                            Учитывая что я совсем новичок в этом деле, я быстро понял, что добавить поддержку текста/линий займет у меня очень много времени.


                            С другой стороны, из текста мне нужно было нарисовать только пару меток A и B. А из толстых линий — только путь который соединяет две вершины. Задача вполне по силам для DOM'a.


                            Как вы помните, наш отрисовщик использует граф сцены. Почему бы не добавить в сцену еще один элемент, задачей которого будет применять текущую трансформацию к… SVG элементу? Этот SVG элемент сделаем прозрачным, и положим его сверху на canvas. Чтобы убрать все события от мышки — ставим ему pointer-events: none;.


                            svg сверху


                            Получилось очень быстро и сердито.


                            Перемещаемся по карте


                            Мне хотелось сделать так, чтобы навигация была похожа на типичное поведение карты (как в Google Maps, например).


                            У меня уже была написана библиотека навигации для SVG: anvaka/panzoom. Она поддерживала touch и кинетическое затухание (когда карта продолжает двигаться по инерции). Для того чтобы поддерживать WebGL мне пришлось чуть-чуть подправить библиотеку.


                            panzoom слушает события от пользователя (mousedown, touchstart, и т.п.), применяет плавные трансформации к матрице преобразования, и потом, вместо того чтобы напрямую работать с SVG, она отдает матрицу "контроллеру". Задача контроллера — применить трансформацию. Контролер может быть для SVG, для DOM или даже мой собственный контроллер, который применяет трансформацию к WebGL сцене.


                            Как понять что кликнуто?


                            Мы обсудили как загрузить граф, как его нарисовать, и как двигаться по нему. Но как же понять что было нажато, когда пользователь касается графа? Откуда прокладывать путь и куда?


                            Когда пользователь кликнул на карту, мы могли бы самым простым способом обойти все точки в графе, посмотреть на их позиции и найти ближайшую. На самом деле, это отличный способ для пары тысяч точек. Если же число точек превышает десятки/сотни тысяч — производительность будет не приемлема.


                            Я использовал квад дерево чтобы проиндексировать точки. После того как дерево создано — скорость поиска ближайшего соседа становится логарифмической.


                            Кстати, если термин "квад дерево" звучит устрашающе — не стоит огорчаться! На самом деле квад-деревья, очень-очень похожи на обычные двоичные деревья. Их легко усвоить, легко реализовать и легко применять.


                            В частности, я использовал собственную реализацию, библиотека yaqt, потому что она неприхотлива по памяти для моего формата данных. Существуют лучшие альтернативы, с хорошей документацией и сообществом (например, d3-quadtree).


                            Ищем путь


                            Теперь все части на своих местах. У нас есть граф, мы знаем как его нарисовать, знаем что было на нем нажато. Осталось только найти кратчайший путь:


                            // pathfinder  это объект https://github.com/anvaka/ngraph.path
                            let path = pathFinder.find(fromId, toId);

                            Теперь у нас есть и массив вершин, которые лежат на найденном пути.


                            Заключение


                            Надеюсь, вам понравилось это маленькое путешествие в мир графов и коротких путей. Пожалуйста, дайте знать если библиотека пригодилась, или если есть советы как сделать ее лучше.


                            Искренне желаю вам добра!
                            Андрей.

                            Original source: habrahabr.ru (comments, light).

                            https://habrahabr.ru/post/338440/


                            Метки:  

                            Поиск сообщений в rss_rss_hh_new
                            Страницы: 1437 ... 1158 1157 [1156] 1155 1154 ..
                            .. 1 Календарь