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

Поиск сообщений в 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 ленты.
По всем вопросам о работе данного сервиса обращаться со страницы контактной информации.

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

Автоматизируем десктопный GUI на Python + pywinauto: как подружиться c MS UI Automation

Четверг, 13 Июля 2017 г. 10:28 + в цитатник

Python библиотека pywinauto — это open source проект по автоматизации десктопных GUI приложений на Windows. За последние два года в ней появились новые крупные фичи:


  • Поддержка технологии MS UI Automation. Интерфейс прежний, и теперь поддерживаются: WinForms, WPF, Qt5, Windows Store (UWP) и так далее — почти все, что есть на Windows.
  • Система бэкендов/плагинов (сейчас их двое под капотом: дефолтный "win32" и новый "uia"). Дальше плавно двигаемся в сторону кросс-платформенности.
  • Win32 хуки для мыши и клавиатуры (hot keys в духе pyHook).

Также сделаем небольшой обзор того, что есть в open source для десктопной автоматизации (без претензий на серьезное сравнение).


Эта статья — частично расшифровка доклада с конференции SQA Days 20 в Минске (видеозапись и слайды), частично русская версия Getting Started Guide для pywinauto.




Начнём с краткого обзора опен сорса в этой области. Для десктопных GUI приложений всё несколько сложнее, чем для веба, у которого есть Selenium. Вот основные подходы:


Координатный метод


Хардкодим точки кликов, надеемся на удачные попадания.
[+] Кросс-платформенный, легко реализуемый.
[+] Легко сделать "record-replay" запись тестов.
[-] Самый нестабильный к изменению разрешения экрана, темы, шрифтов, размеров окон и т.п.
[-] Нужны огромные усилия на поддержку, часто проще перегенерить тесты с нуля или тестировать вручную.
[-] Автоматизирует только действия, для верификации и извлечения данных есть другие методы.


Инструменты (кросс-платформенные): autopy, PyAutoGUI, PyUserInput и многие другие. Как правило, более сложные инструменты включают в себя эту функциональность (не всегда кросс-платформенно).


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


Другой вариант: выделять для тестов только одну машину со стабильными настройками (не кросс-платформенно, но в каких-то случаях годится).


Распознавание эталонных изображений


[+] Кросс-платформенный
[+-] Относительно надежный (лучше, чем координатный метод), но всё же требует хитростей.
[-+] Относительно медленный, т.к. требует ресурсов CPU для алгоритмов распознавания.
[-] О распознавании текста (OCR), как правило, речи не идёт => нельзя достать текстовые данные. Насколько мне известно, существующие OCR решения не слишком надежны для этого типа задач, и широкого применения не имеют (welcome в комменты, если это уже не так).


Инструменты: Sikuli, Lackey (Sikuli-совместимый, на чистом Python), PyAutoGUI.


Accessibility технологии


[+] Самый надежный метод, т.к. позволяет искать по тексту, независимо от того, как он отрисован системой или фреймворком.
[+] Позволяет извлекать текстовые данные => проще верифицировать результаты тестов.
[+] Как правило, самый быстрый, т.к. почти не расходует ресурсы CPU.
[-] Тяжело сделать кросс-платформенный инструмент: абсолютно все open-source библиотеки поддерживают одну-две accessibility технологии. Windows/Linux/MacOS целиком не поддерживает никто, кроме платных типа TestComplete, UFT или Squish.
[-] Не всегда такая технология в принципе доступна. Например, тестирование загрузочного экрана внутри VirtualBox'а — тут без распознавания изображений не обойтись. Но во многих классических случаях все-таки accessibility подход применим. О нем дальше и пойдет речь.


Инструменты: TestStack.White на C#, Winium.Desktop на C# (Selenium совместимый), MS WinAppDriver на C# (Appium совместимый), pywinauto, pyatom (совместим с LDTP), Python-UIAutomation-for-Windows, RAutomation на Ruby, LDTP (Linux Desktop Testing Project) и его Windows версия Cobra.


LDTP — пожалуй, единственный кросс-платформенный open-source инструмент (точнее семейство библиотек) на основе accessibility технологий. Однако он не слишком популярен. Сам не пользовался им, но по отзывам интерфейс у него не самый удобный. Если есть позитивные отзывы, прошу поделиться в комментах.


Тестовый backdoor (a.k.a. внутренний велосипед)


Для кросс-платформенных приложений сами разработчики часто делают внутренний механизм для обеспечения testability. Например, создают служебный TCP сервер в приложении, тесты к нему подключаются и посылают текстовые команды: на что нажать, откуда взять данные и т.п. Надежно, но не универсально.


Основные десктопные accessibility технологии


Старый добрый Win32 API


Большинство Windows приложений, написанных до выхода WPF и затем Windows Store, построены так или иначе на Win32 API. А именно, MFC, WTL, C++ Builder, Delphi, VB6 — все эти инструменты используют Win32 API. Даже Windows Forms — в значительной степени Win32 API совместимые.


Инструменты: AutoIt (похож на VB) и Python обертка pyautoit, AutoHotkey (собственный язык, есть IDispatch COM интерфейс), pywinauto (Python), RAutomation (Ruby), win32-autogui (Ruby).


Microsoft UI Automation


Главный плюс: технология MS UI Automation поддерживает подавляющее большинство GUI приложений на Windows за редкими исключениями. Проблема: она не сильно легче в изучении, чем Win32 API. Иначе никто бы не делал оберток над ней.


Фактически это набор custom COM интерфейсов (в основном, UIAutomationCore.dll), а также имеет .NET оболочку в виде namespace System.Windows.Automation. Она, кстати, имеет привнесенный баг, из-за которого некоторые UI элементы могут быть пропущены. Поэтому лучше использовать UIAutomationCore.dll напрямую (если слышали про UiaComWrapper на C#, то это оно).


Разновидности COM интерфейсов:
(1) Базовый IUknown — "the root of all evil". Самый низкоуровневый, ни разу не user-friendly.
(2) IDispatch и производные (например, Excel.Application), которые можно использовать в Python с помощью пакета win32com.client (входит в pyWin32). Самый удобный и красивый вариант.
(3) Custom интерфейсы, с которыми умеет работать сторонний Python пакет comtypes.


Инструменты: TestStack.White на C#, pywinauto 0.6.0+, Winium.Desktop на C#, Python-UIAutomation-for-Windows (у них исходный код сишных оберток над UIAutomationCore.dll не раскрыт), RAutomation на Ruby.


AT-SPI


Несмотря на то, что почти все оси семейства Linux построены на X Window System (в Fedora 25 "иксы" поменяли на Wayland), "иксы" позволяют оперировать только окнами верхнего уровня и мышью/клавиатурой. Для детального разбора по кнопкам, лист боксам и так далее — существует технология AT-SPI. У самых популярных оконных менеджеров есть так называемый AT-SPI registry демон, который и обеспечивает для приложений автоматизируемый GUI (как минимум поддерживаются Qt и GTK).


Инструменты: pyatspi2.


pyatspi2, на мой взгляд, содержит слишком много зависимостей типа того же PyGObject. Сама технология доступна в виде обычной динамической библиотеки libatspi.so. К ней имеется Reference Manual. Для библиотеки pywinauto планируем реализовать поддержку AT-SPI имеено так: через загрузку libatspi.so и модуль ctypes. Есть небольшая проблема только в использовании нужной версии, ведь для GTK+ и Qt приложений они немного разные. Вероятный выпуск pywinauto 0.7.0 с полноценной поддержкой Linux можно ожидать в первой половине 2018-го.


Apple Accessibility API


На MacOS есть собственный язык автоматизации AppleScript. Для реализации чего-то подобного на Python, разумеется, нужно использовать функции из ObjectiveC. Начиная, кажется, еще с MacOS 10.6 в предустановленный питон включается пакет pyobjc. Это также облегчит список зависимостей для будущей поддержки в pywinauto.


Инструменты: Кроме языка Apple Script, стоит обратить внимание на ATOMac, он же pyatom. Он совместим по интерфейсу с LDTP, но также является самостоятельной библиотекой. На нем есть пример автоматизации iTunes на macOs, написанный моим студентом. Есть известная проблема: не работают гибкие тайминги (методы waitFor*). Но, в целом, неплохая вещь.




Как начать работать с pywinauto


Первым делом стоит вооружиться инспектором GUI объектов (то, что называют Spy tool). Он поможет изучить приложение изнутри: как устроена иерархия элементов, какие свойства доступны. Самые известные инспекторы объектов:


  • Spy++ — входит в поставку Visual Studio, включая Express или Community Edition. Использует Win32 API. Также известен его клон AutoIt Window Info.
  • Inspect.exe — входит в Windows SDK. Если он у вас установлен, то на 64-битной Windows можно найти его в папке C:\Program Files (x86)\Windows Kits\\bin\x64. В самом инспекторе нужно выбрать режим UI Automation вместо MS AA (Active Accessibility, предок UI Automation).

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


  • backend="win32" — пока используется по умолчанию, хорошо работает с MFC, WTL, VB6 и другими legacy приложениями.
  • backend="uia" — новый бэкенд для MS UI Automation: идеально работает с WPF и WinForms; также хорош для Delphi и Windows Store приложений; работает с Qt5 и некоторыми Java приложениями. И вообще, если Inspect.exe видит элементы и их свойства, значит этот бэкенд подходит. В принципе, большинство браузеров тоже поддерживает UI Automation (Mozilla по умолчанию, а Хрому при запуске нужно скормить ключ командной строки --force-renderer-accessibility, чтобы увидеть элементы на страницах в Inspect.exe). Конечно, конкуренция с Selenium в этой области навряд ли возможна. Просто еще один способ работать с браузером (может пригодиться для кросс-продуктового сценария).

Входные точки для автоматизации


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


from pywinauto.application import Application
app = Application(backend="uia").start('notepad.exe')

# Опишем окно, которое хотим найти в процессе Notepad.exe
dlg_spec = app.UntitledNotepad
# ждем пока окно реально появится
actionable_dlg = dlg_spec.wait('visible')

Если хочется управлять сразу несколькими приложениями, вам поможет класс Desktop. Например, в калькуляторе на Win10 иерархия элементов размазана аж по нескольким процессам (не только calc.exe). Так что без объекта Desktop не обойтись.


from subprocess import Popen
from pywinauto import Desktop

Popen('calc.exe', shell=True)
dlg = Desktop(backend="uia").Calculator
dlg.wait('visible')

Корневой объект (Application или Desktop) — это единственное место, где нужно указывать бэкенд. Все остальное прозрачно ложится в концепцию "спецификация->враппер", о которой дальше.


Спецификации окон/элементов


Это основная концепция, на которой строится интерфейс pywinauto. Вы можете описать окно/элемент приближенно или более детально, даже если оно еще не существует или уже закрыто. Спецификация окна (объект WindowSpecification) хранит в себе критерии, по которым нужно искать реальное окно или элемент.


Пример детальной спецификации окна:


>>> dlg_spec = app.window(title='Untitled - Notepad')

>>> dlg_spec


>>> dlg_spec.wrapper_object()

Сам поиск окна происходит по вызову метода .wrapper_object(). Он возвращает некий "враппер" для реального окна/элемента или кидает ElementNotFoundError (иногда ElementAmbiguousError, если найдено несколько элементов, то есть требуется уточнить критерий поиска). Этот "враппер" уже умеет делать какие-то действия с элементом или получать данные из него.


Python может скрывать вызов .wrapper_object(), так что финальный код становится короче. Рекомендуем использовать его только для отладки. Следующие две строки делают абсолютно одно и то же:


dlg_spec.wrapper_object().minimize() # debugging
dlg_spec.minimize() # production

Есть множество критериев поиска для спецификации окна. Вот лишь несколько примеров:


# могут иметь несколько уровней
app.window(title_re='.* - Notepad$').window(class_name='Edit')

# можно комбинировать критерии (как AND) и не ограничиваться одним процессом приложения
dlg = Desktop(backend="uia").Calculator
dlg.window(auto_id='num8Button', control_type='Button')

Список всех возможных критериев есть в доках функции pywinauto.findwindows.find_elements(...).


Магия доступа по атрибуту и по ключу


Python упрощает создание спецификаций окна и распознает атрибуты объекта динамически (внутри переопределен метод __getattribute__). Разумеется, на имя атрибута накладываются такие же ограничения, как и на имя любой переменной (нельзя вставлять пробелы, запятые и прочие спецсимволы). К счастью, pywinauto использует так называемый "best match" алгоритм поиска, который устойчив к опечаткам и небольшим вариациям.


app.UntitledNotepad
# то же самое, что
app.window(best_match='UntitledNotepad')

Если все-таки нужны Unicode строки (например, для русского языка), пробелы и т.п., можно делать доступ по ключу (как будто это обычный словарь):


app['Untitled - Notepad']
# то же самое, что
app.window(best_match='Untitled - Notepad')

Пять правил для магических имен


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


  1. По заголовку (текст, имя): app.Properties.OK.click()
  2. По тексту и по типу элемента: app.Properties.OKButton.click()
  3. По типу и по номеру: app.Properties.Button3.click() (имена Button0 и Button1 привязаны к первому найденному элементу, Button2 — ко второму, и дальше уже по порядку — так исторически сложилось)
  4. По статическому тексту (слева или сверху) и по типу: app.OpenDialog.FileNameEdit.set_text("") (полезно для элементов с динамическим текстом)
  5. По типу и по тексту внутри: app.Properties.TabControlSharing.select("General")

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


app.Properties.child_window(title="Contains:", auto_id="13087", control_type="Edit")

Само дерево элементов - обычно довольно большая портянка.
>>> app.Properties.print_control_identifiers()

Control Identifiers:

Dialog - 'Windows NT Properties'    (L688, T518, R1065, B1006)
[u'Windows NT PropertiesDialog', u'Dialog', u'Windows NT Properties']
child_window(title="Windows NT Properties", control_type="Window")
   |
   | Image - ''    (L717, T589, R749, B622)
   | [u'', u'0', u'Image1', u'Image0', 'Image', u'1']
   | child_window(auto_id="13057", control_type="Image")
   |
   | Image - ''    (L717, T630, R1035, B632)
   | ['Image2', u'2']
   | child_window(auto_id="13095", control_type="Image")
   |
   | Edit - 'Folder name:'    (L790, T596, R1036, B619)
   | [u'3', 'Edit', u'Edit1', u'Edit0']
   | child_window(title="Folder name:", auto_id="13156", control_type="Edit")
   |
   | Static - 'Type:'    (L717, T643, R780, B658)
   | [u'Type:Static', u'Static', u'Static1', u'Static0', u'Type:']
   | child_window(title="Type:", auto_id="13080", control_type="Text")
   |
   | Edit - 'Type:'    (L790, T643, R1036, B666)
   | [u'4', 'Edit2', u'Type:Edit']
   | child_window(title="Type:", auto_id="13059", control_type="Edit")
   |
   | Static - 'Location:'    (L717, T669, R780, B684)
   | [u'Location:Static', u'Location:', u'Static2']
   | child_window(title="Location:", auto_id="13089", control_type="Text")
   |
   | Edit - 'Location:'    (L790, T669, R1036, B692)
   | ['Edit3', u'Location:Edit', u'5']
   | child_window(title="Location:", auto_id="13065", control_type="Edit")
   |
   | Static - 'Size:'    (L717, T695, R780, B710)
   | [u'Size:Static', u'Size:', u'Static3']
   | child_window(title="Size:", auto_id="13081", control_type="Text")
   |
   | Edit - 'Size:'    (L790, T695, R1036, B718)
   | ['Edit4', u'6', u'Size:Edit']
   | child_window(title="Size:", auto_id="13064", control_type="Edit")
   |
   | Static - 'Size on disk:'    (L717, T721, R780, B736)
   | [u'Size on disk:', u'Size on disk:Static', u'Static4']
   | child_window(title="Size on disk:", auto_id="13107", control_type="Text")
   |
   | Edit - 'Size on disk:'    (L790, T721, R1036, B744)
   | ['Edit5', u'7', u'Size on disk:Edit']
   | child_window(title="Size on disk:", auto_id="13106", control_type="Edit")
   |
   | Static - 'Contains:'    (L717, T747, R780, B762)
   | [u'Contains:1', u'Contains:0', u'Contains:Static', u'Static5', u'Contains:']
   | child_window(title="Contains:", auto_id="13088", control_type="Text")
   |
   | Edit - 'Contains:'    (L790, T747, R1036, B770)
   | [u'8', 'Edit6', u'Contains:Edit']
   | child_window(title="Contains:", auto_id="13087", control_type="Edit")
   |
   | Image - 'Contains:'    (L717, T773, R1035, B775)
   | [u'Contains:Image', 'Image3', u'Contains:2']
   | child_window(title="Contains:", auto_id="13096", control_type="Image")
   |
   | Static - 'Created:'    (L717, T786, R780, B801)
   | [u'Created:', u'Created:Static', u'Static6', u'Created:1', u'Created:0']
   | child_window(title="Created:", auto_id="13092", control_type="Text")
   |
   | Edit - 'Created:'    (L790, T786, R1036, B809)
   | [u'Created:Edit', 'Edit7', u'9']
   | child_window(title="Created:", auto_id="13072", control_type="Edit")
   |
   | Image - 'Created:'    (L717, T812, R1035, B814)
   | [u'Created:Image', 'Image4', u'Created:2']
   | child_window(title="Created:", auto_id="13097", control_type="Image")
   |
   | Static - 'Attributes:'    (L717, T825, R780, B840)
   | [u'Attributes:Static', u'Static7', u'Attributes:']
   | child_window(title="Attributes:", auto_id="13091", control_type="Text")
   |
   | CheckBox - 'Read-only (Only applies to files in folder)'    (L790, T825, R1035, B841)
   | [u'CheckBox0', u'CheckBox1', 'CheckBox', u'Read-only (Only applies to files in folder)CheckBox', u'Read-only (Only applies to files in folder)']
   | child_window(title="Read-only (Only applies to files in folder)", auto_id="13075", control_type="CheckBox")
   |
   | CheckBox - 'Hidden'    (L790, T848, R865, B864)
   | ['CheckBox2', u'HiddenCheckBox', u'Hidden']
   | child_window(title="Hidden", auto_id="13076", control_type="CheckBox")
   |
   | Button - 'Advanced...'    (L930, T845, R1035, B868)
   | [u'Advanced...', u'Advanced...Button', 'Button', u'Button1', u'Button0']
   | child_window(title="Advanced...", auto_id="13154", control_type="Button")
   |
   | Button - 'OK'    (L814, T968, R889, B991)
   | ['Button2', u'OK', u'OKButton']
   | child_window(title="OK", auto_id="1", control_type="Button")
   |
   | Button - 'Cancel'    (L895, T968, R970, B991)
   | ['Button3', u'CancelButton', u'Cancel']
   | child_window(title="Cancel", auto_id="2", control_type="Button")
   |
   | Button - 'Apply'    (L976, T968, R1051, B991)
   | ['Button4', u'ApplyButton', u'Apply']
   | child_window(title="Apply", auto_id="12321", control_type="Button")
   |
   | TabControl - ''    (L702, T556, R1051, B962)
   | [u'10', u'TabControlSharing', u'TabControlPrevious Versions', u'TabControlSecurity', u'TabControl', u'TabControlCustomize']
   | child_window(auto_id="12320", control_type="Tab")
   |    |
   |    | TabItem - 'General'    (L704, T558, R753, B576)
   |    | [u'GeneralTabItem', 'TabItem', u'General', u'TabItem0', u'TabItem1']
   |    | child_window(title="General", control_type="TabItem")
   |    |
   |    | TabItem - 'Sharing'    (L753, T558, R801, B576)
   |    | [u'Sharing', u'SharingTabItem', 'TabItem2']
   |    | child_window(title="Sharing", control_type="TabItem")
   |    |
   |    | TabItem - 'Security'    (L801, T558, R851, B576)
   |    | [u'Security', 'TabItem3', u'SecurityTabItem']
   |    | child_window(title="Security", control_type="TabItem")
   |    |
   |    | TabItem - 'Previous Versions'    (L851, T558, R947, B576)
   |    | [u'Previous VersionsTabItem', u'Previous Versions', 'TabItem4']
   |    | child_window(title="Previous Versions", control_type="TabItem")
   |    |
   |    | TabItem - 'Customize'    (L947, T558, R1007, B576)
   |    | [u'CustomizeTabItem', 'TabItem5', u'Customize']
   |    | child_window(title="Customize", control_type="TabItem")
   |
   | TitleBar - 'None'    (L712, T521, R1057, B549)
   | ['TitleBar', u'11']
   |    |
   |    | Menu - 'System'    (L696, T526, R718, B548)
   |    | [u'System0', u'System', u'System1', u'Menu', u'SystemMenu']
   |    | child_window(title="System", auto_id="MenuBar", control_type="MenuBar")
   |    |    |
   |    |    | MenuItem - 'System'    (L696, T526, R718, B548)
   |    |    | [u'System2', u'MenuItem', u'SystemMenuItem']
   |    |    | child_window(title="System", control_type="MenuItem")
   |    |
   |    | Button - 'Close'    (L1024, T519, R1058, B549)
   |    | [u'CloseButton', u'Close', 'Button5']
   |    | child_window(title="Close", control_type="Button")

В некоторых случаях печать всего дерева может тормозить (например, в iTunes на одной вкладке аж три тысячи элементов!), но можно использовать параметр depth (глубина): depth=1 — сам элемент, depth=2 — только непосредственные дети, и так далее. Его же можно указывать в спецификациях при создании child_window.


Примеры


Мы постоянно пополняем список примеров в репозитории. Из свежих стоит отметить автоматизацию сетевого анализатора WireShark (это хороший пример Qt5 приложения; хотя эту задачу можно решать и без GUI, ведь есть scapy.Sniffer из питоновского пакета scapy). Также есть пример автоматизации MS Paint с его Ribbon тулбаром.


Еще один отличный пример, написанный моим студентом: перетаскивание файла из explorer.exe на Chrome страницу для Google Drive (он перекочует в главный репозиторий чуть позже).


И, конечно, пример подписки на события клавиатуры (hot keys) и мыши:
hook_and_listen.py.


Благодарности


Отдельное спасибо — тем, кто постоянно помогает развивать проект. Для меня и Валентина это постоянное хобби. Двое моих студентов из ННГУ недавно защитили дипломы бакалавра по этой теме. Александр внес большой вклад в поддержку MS UI Automation и недавно начал делать автоматический генератор кода по принципу "запись-воспроизведение" на основе текстовых свойств (это самая сложная фича), пока только для "uia" бэкенда. Иван разрабатывает новый бэкенд под Linux на основе AT-SPI (модули mouse и keyboard на основе python-xlib — уже в релизах 0.6.x).


Поскольку я довольно давно читаю спецкурс по автоматизации на Python, часть студентов-магистров выполняют домашние задания, реализуя небольшие фичи или примеры автоматизации. Некоторые ключевые вещи на стадии исследований тоже когда-то раскопали именно студенты. Хотя иногда за качеством кода приходится строго следить. В этом сильно помогают статические анализаторы (QuantifiedCode, Codacy и Landscape) и автоматические тесты в облаке (сервис AppVeyor) с покрытием кода в районе 95%.


Также спасибо всем, кто оставляет отзывы, заводит баги и присылает пулл реквесты!


Дополнительные ресурсы


За вопросами мы следим по тегу на StackOverflow (недавно появился тег в русской версии SO) и по ключевому слову на Тостере. Есть русскоязычный чат в Gitter'е.


Каждый месяц обновляем рейтинг open-source библиотек для GUI тестирования. По количеству звезд на гитхабе быстрее растут только Autohotkey (у них очень большое сообщество и длинная история) и PyAutoGUI (во многом благодаря популярности книг ее автора Al Sweigart: "Automate the Boring Stuff with Python" и других).

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

https://habrahabr.ru/post/323962/


Блокчейн-платформа для сделок торгового финансирования на базе смарт-контрактов

Четверг, 13 Июля 2017 г. 10:19 + в цитатник
Содержание


22 июня 2017 года на Blockchain & Bitcoin Conference в Санкт-Петербурге наш аналитик направления блокчейн, Марина Сманцер, сделала доклад о результатах исследовательского проекта по созданию комплексной платформы для сделок торгового финансирования на основе смарт-контрактов.

20-минутный формат доклада не позволял подробно осветить технические аспекты. Поэтому выход Райффайзенбанка на habrahabr – прекрасная возможность рассказать о наших результатах во всех подробностях.

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

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

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

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

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

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

Кратко о целях и результатах


В конце 2016 года блокчейн попал в зону интересов нашего отдела R&D. Некоторое время заняло погружение в теорию, после которого мы решили, что имеет смысл сделать практическую реализацию.

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

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

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

В упрощенном виде алгоритм расчетов можно представить следующим образом:
  1. Стороны заключают контракт на поставку товара, где указывают аккредитив в качестве способа расчета.
  2. Покупатель подает в свой банк заявление на открытие аккредитива.
    Банк проводит внутренние проверки (например, наличие средств на счете). При положительном результате Банк выпускает аккредитив.
  3. После выпуска аккредитива Банк уведомляет Продавца об открытии аккредитива по определенным условиям. Продавец имеет право отклонить аккредитив.
  4. Когда поставка товара будет произведена, Продавец отправляет указанные в условиях аккредитива документы (счет-фактура, торговая накладная, и т.п.) на рассмотрение в Банк.
  5. Банк проверяет документы и при положительном результате проводит платеж.

В этом описании многие моменты упрощены.


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

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

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

Задачи исследовательского проекта


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

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

Для определения необходимого для реализации платформы функционала были заданы следующие исходные условия:
  • Исполнение сделки сопровождается обменом документами между сторонами
  • Основной расчет по сделке производится через обычные (фиатные) каналы расчета
  • Все операции по сделке должны быть максимально юридически подтверждены исходя из действующих нормативных документов и регуляторных положений (для обеспечения судебной практики «прямо сейчас»)
  • Все операции и сигналы переходов между операциями должны быть максимально автоматизированы


Общая схема платформы и взаимодействие её элементов


Архитектура и взаимодействие компонентов платформы



image

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

Учитывая, что основные расчеты и анализ документов в реалиях сегодняшнего дня выполняются вне смарт-контрактов, непосредственно в смарт-контракты передаются только данные, которые те в состоянии обработать. Остальная информация (обосновывающие и распорядительные документы) прикрепляется в виде обычных (для ручной обработки) или формализованных (для автоматической обработки) документов, подписанных усиленной квалифицированной ЭЦП для обеспечения юридической значимости.

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

С каждым из пользователей платформы связывалась следующая регистрационная информация:
  • Уникальный идентификатор в системе
  • Ethereum-адрес, с которого пользователь направляет транзакции
  • Адрес смарт-контракта, используемого для ведения реестра принадлежащих или направленных пользователю сделок (далее именуется Почтовый ящик)
  • Идентификатор сертификата усиленной квалифицированной ЭЦП и ее открытый ключ

Наиболее простое объяснение концепции оракулов: blockchainhub.net/blockchain-oracles
По DFS не нашлось хорошей статьи, оставлю ссылку на документацию Swarm: swarm-guide.readthedocs.io/en/latest/introduction.html
И Storj: storj.io

Практическая реализация


На рисунке ниже представлена общая схема платформы и основных потоков обмена данными между ее функциональными компонентами (при этом зеленым выделены «чужие» компоненты, фиолетовым — сертифицированные, белым — ПО Банка):

image
В процессе подготовки и исполнения сделки компоненты платформы взаимодействуют следующим образом:
  • Клиентское ПО (например, клиент-банк или мобильный банк). Используется для ввода исходной информации по сделке, создания необходимых смарт-контрактов и управления состоянием смарт-контрактов на ручных этапах бизнес-процесса. Следует отметить, что под ручными этапами бизнес-процесса могут подразумеваться как те этапы, на которых требуется реальные «личные» действия пользователя — например, прикрепление к смарт-контракту документов, так и вообще любые этапы, на которых изменение статуса смарт-контракта производится без использования его внутренней логики — за пределами блокчейна. К последнему случаю можно отнести проверку прилагаемых к смарт-контракту документов по учетным системам банка, которая может происходить автоматически, но снаружи блокчейна.
  • Прикрепляемые к смарт-контракту файлы подписываются усиленной квалифицированной ЭЦП создателя для его (создателя) однозначной юридически значимой идентификации. Далее файлы шифруются с формированием крипто-пакета, доступного к расшифровыванию только участниками сделки. Полученный в итоге крипто-пакет помещается в DFS, при этом на контексте смарт-контракта сохраняется хэш исходного файла, а также адрес (ссылка, манифест) объекта хранения, отданный DFS. Адрес объекта хранения позволяет извлечь крипто-пакет из DFS, расшифровать его (участникам сделки) и обработать надлежащим образом.
  • При обработке транзакций смарт-контракт может пользоваться информацией бродкаст-оракулов, например, контролировать дату получения транзакции по календарю, использовать курсы валют и т.д.
  • При переходе в определенный статус смарт-контракт может направить на исполнение Провайдеру внешних запросов определенный запрос на ожидаемое внешнее событие или распоряжение на исполнение внешнего действия. При наступлении «заказанных» смарт-контрактом внешних событий Провайдер направляет на него транзакцию с информацией о событии. По результатам обработки данной транзакции смарт-контракт может переключится в новое состояние или остаться в прежнем в ожидании наступления последующих событий.
  • Аналогично внешним запросам, при прикреплении к смарт-контракту определенных формализованных документов смарт-контракт может направить их на анализ в Анализатор документов с последующим ожиданием транзакции с результатами анализа.


Реализация смарт-контрактов для аккредитива


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

Упрощенно схема процесса на блокчейне представлена на рисунке, а подробное описание процесса приведено ниже

image
Участники сделки — Покупатель, Продавец и Банк. Покупатель и Продавец заключают контракт на предоставление некоторых услуг или товаров, причем факт их предоставления может быть идентифицирован автоматически. Например, если аккредитив открывается для расчетов при передаче права собственности на недвижимое имущество, долей, акций и т.п., дополнительно будет произведена проверка информации в представленных документах против информации во внешних источниках (например, в едином государственном реестре недвижимости). Дальнейшая операционная поддержка сделки осуществляется через Платформу.
  1. Покупатель создает смарт-контракт «Заявка на аккредитив» (далее «Заявка»), который получает статус New.
    Адрес Заявки помещается в Почтовые ящики Покупателя и Банка.
  2. Покупатель прикрепляет к Заявке формализованный ЭД с описанием реквизитов сделки и необходимые неформализованные документы, например, скан-копию контракта, для расчетов по которому открывается аккредитив.
    После прикрепления всех необходимых документов Покупатель переводит Заявку в статус InBank.
  3. Банк автоматически (для формализованных ЭД) или с использованием экспертов (для неформализованных ЭД) проверяет сделку. Например, направляется запрос в системы банка для проверки клиентских реквизитов в каталоге клиентских данных, для подтверждения остатка по счету и резервирования средств, для иных проверок (например, проверок для целей комплаенс, валютного контроля и т.п.)
    Если у Банка есть какие-либо претензии по содержанию сделки или приложенных документов, он отказывает в приеме Заявки и устанавливает ей статус Rejected.
    Если Банк согласен принять заявку к исполнению ей устанавливается статус Confirmed.
  4. На основе формализованного ЭД, описывающего сделку, в учетных системах Банка выполняются необходимые манипуляции (перевод суммы аккредитива с клиентского счета на «счет покрытия», списание комиссии и т.д.)
    Банк выпускает смарт-контракт «Аккредитив» (далее Аккредитив), который получает статус New.
    Адрес Аккредитива помещается в Почтовые ящики Банка и Продавца.
    В Аккредитиве сохраняется адрес Заявки, что позволяет автоматически «зеркалировать» ключевые статусы Аккредитива на Заявку, чтобы Покупатель мог контролировать состояние сделки.
  5. Банк прикрепляет к Аккредитиву формализованный ЭД (формируется автоматически из условий Заявки) с описанием реквизитов сделки, другие необходимые документы и устанавливает ему статус Released.
    Статус связанной с Аккредитивом Заявки также переключается в Released.
    При переходе в статус Released Аккредитив автоматически помещает в очередь Провайдера внешних запросов два запроса:
    • Запрос контроля истечения срока действия Аккредитива (срабатывает, когда текущая дата превысит срок действию Аккредитива)
    • Запрос ожидания исполнения контракта (конкретный шаблон запроса определяется содержанием сделки)
  6. Продавец после изучения выпущенного Аккредитива может отказаться его принять: в этом случае он переводит его в статус Invalid, аккредитив аннулируется, и дальнейшие манипуляции с ним становятся невозможны.
    Статус связанной с Аккредитивом Заявки также переключается в Invalid.
  7. Если первым сработает событие истечения срока действия Аккредитива — он получает статус Overdue и дальнейшие манипуляции с ним становятся невозможны.
    Статус связанной с Аккредитивом Заявки также переключается в Overdue.
    В этом случае в учетные системы Банка отправляются автоматические распоряжения для инициирования проводок, соответствующих аннулированию аккредитива (возврат покрытия, прекращение обязательства банка и т.п.)
  8. Если первым сработает событие исполнение контракта (или когда Продавец прикрепляет ЭД, предусмотренные условиями Аккредитива) — Аккредитив переключается в статус InBank. При переходе в статус InBank Аккредитив автоматически помещает в очередь Провайдера внешних запросов запрос ожидания исполнения платежа и удаляет из очереди запрос контроля срока истечения.
    Банк на основе формализованного ЭД, приложенного к Аккредитиву, выполняет платеж в пользу Продавца путем передачи распоряжения в свою расчетную систему и исполнения Платежа за пределами блокчейна (информация о фактическом исполнении Платежа возвращается в блокчейн).
  9. После срабатывания события исполнения платежа Аккредитив переключается в статус Closed.
    Статус связанной с Аккредитивом Заявки также переключается в Closed.
  10. Сделка завершена.


Выбор компонентов платформы


При выборе функциональных компонентов платформы мы ориентировались на открытые решения (Ethereum, Swarm, Storj). Это связано со следующими их преимуществами:
  • Наличие развернутой и «самоподдерживающейся» инфраструктуры
  • Открытость для пользователей и возможность контроля операций через альтернативные источники, а не только через предлагаемый банком интерфейс
  • Высокий уровень доверия со стороны пользователей благодаря наличию конкурентных протоколов консенсуса и качественной «не толерантной» сети независимых узлов


image
Таким образом, выбор был сделан в пользу следующих реализаций:
  • Блокчейн и смарт-контракты — Ethereum и язык Solidity;
  • Децентрализованные файловые хранилища — Swarm и Storj.io;
  • Сертифицированные СКЗИ — КриптоПРО и КриптоАРМ;
  • Бродкаст-Оракулы — собственной разработки;
  • Провайдеры внешних запросов — собственной разработки;
  • Анализатор документов — на данном этапе было решено не рассматривать, так как принцип взаимодействия с ним смарт-контракта в общем аналогичен Провайдеру внешних запросов, а ресурсы команды исследования — ограничены.

Инфраструктура


Для развертывания необходимых компонентов:
  • Клиентских частей Ethereum, Swarm и Storj.io,
  • СКЗИ,
  • Провайдера внешних запросов,
  • UI-приложения,
  • Интеграционного ядра платформы

было выделено 2 сервера.

На первом сервере было развернут узел Ethereum базового блокчейна смарт-контрактов. Изначально в качестве базового блокчейна использовалась тестовая сеть Ropsten, но на заключительных этапах мы перешли на более стабильный Rinkeby. Причиной этому стал инцидент в марте с DDOS атакой на Ropsten, в ходе которого несколько дней были проблемы с добавлением транзакций.
На втором сервере была развернута инфраструктура, обеспечивавшая работу с файлами:
  • Узел DFS Storj.io
  • Узел DFS Swarm с поддерживающим узлом Ropston Ethereum
  • СКЗИ КриптоПРО и КриптоАРМ

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

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

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

Интеграция компонентов


Ethereum


Интеграция с блокчейном Ethereum осуществлялась с использованием RPC API JSON-RPC, Management-APIs.

Никаких технических проблем с его использованием не возникало.

В качестве внешнего арбитражного ресурса использовался ropsten.etherscan.io или rinkeby.etherscan.io, в зависимости от используемой тестовой сети.

Swarm


Интеграция со Swarm осуществлялась с использованием HTTP API: Swarm-guide, gist.github.com.

Для загрузки файла в SWARM использовался HTTP-запрос PUT /bzz: $PATH$ ($PATH$ — путь к загружаемому файлу)
в ответ на который приходил 16-ричный идентификатор манифеста файла, который использовался для его извлечения из SWARM.

Для извлечения файла из SWARM использовался HTTP-запрос GET /bzzi:/$MANIFEST$/ ($MANIFEST$ — 16-ричный идентификатор манифеста извлекаемого файла).

Каких-либо проблем с загрузкой/выгрузкой файлов не возникало.

К сожалению, для Swarm не было найдено внешнего средства мониторинга (типа Etherscan для Ethereum), что определенным образом затрудняло оценку успешности манипуляций с файлами.

Storj.io


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

Для загрузки файла использовалась команда storj upload-file $BUCKET$ $PATH$ (где $BUCKET$ — идентификатор «корзины», а $PATH$ — путь к загружаемому файлу).
При успешной загрузке в ответ отдавался идентификатор загруженного файла.

Для выгрузки файла использовалась команда storj download-file $BUCKET$ $FILE$ $PATH$ (где $BUCKET$ — идентификатор «корзины», $FILE$ — идентификатор файла, а $PATH$ — путь к создаваемой локальной копии файла).

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

В качестве внешнего арбитражного ресурса можно использовать https://api.storj.io, который поддерживает API.

КриптоАРМ


Интеграция с КриптоАРМ осуществлялась с использованием предоставляемого им COM-сервиса. Для быстроты реализации интеграционные скрипты были написаны на vbs.

Провайдер внешних запросов


Взаимодействие смарт-контрактов с Провайдером внешних запросов осуществляется по следующей схеме.
image


Подробное описание реализации Провайдера запросов
Перед описанием процесса необходимо определить некоторые основные понятия:
  • Идентификатор запроса — 32-символьное значение (64-значное hex), которое содержит первыми 20 (40 hex) символами адрес контракта, а далее — произвольную информацию, позволяющую контракту при получении ответа построить надлежащую обработку ответа на запрос.
  • Идентификатор ответа — уникальное 32-символьное значение (64-значное hex), позволяющее Провайдеру внешних запросов однозначно идентифицировать ответное сообщение.


Для использования Провайдера внешних запросов смарт-контракт должен поддерживать специальный интерфейс, состоящий из следующих методов: GetExternalRequest, SetExternalResponse и CheckExternalResponse.

Порядок взаимодействия некоторого Контракта, желающего получить «снаружи» ответ на некоторый запрос, с Провайдером внешних событий следующий:
  1. Контракт транзакцией через метод AddRequest передает идентификатор запроса в смарт-контракт RequestsQueue, адрес которого фиксирован.
  2. Провайдер внешних запросов (ПВЗ) периодически опрашивает смарт-контракт RequestsQueue через метод GetRequests и получает актуальный список подлежащих исполнению запросов.
  3. При получении нового запросаПВЗ через метод GetExternalRequest обращяется к Контракту и по идентификатору запроса получает параметры запроса:
    • Периодичность исполнения запроса (например, «DAILY 10:00» или «PERIOD 20»)
    • Идентификатор шаблон запроса (например, «CALENDAR» или «DADATA_NAME_EXISTS»)
    • Дополнительные параметры, если они необходимы (например, наименование организации для шаблона «DADATA_NAME_EXISTS»)
  4. В порядке общего управления очередью ПВЗ с заданной для данного запроса периодичностью производит опрос внешних ресурсов в соответствии с шаблоном запроса и приложенными параметрами.
  5. Если по логике, заложенной в шаблон, считается, что ответ на запрос получен — ПВЗ передает его транзакцией на Контракт через метод SetExternalResponse в привязке с идентификатором запроса и идентификатором ответа.
    При этом Контракт должен выполнить надлежавшую обработку полученного ответа и зафиксировать идентификатор ответа для дальнейшего контроля.
  6. Далее ПВЗ запрашивает у Контракта через метод CheckExternalResponse, был ли получен и обработан ответ и какова дальнейшая судьба соответствующего запроса.
    Контракт может предложить один из следующих вариантов действий:
    • FAIL — ответ не получен, не обработан или некорректен — необходимо повторить запрос и передачу ответа
    • REPEAT — ответ принят, необходимо продолжить исполнение соответствующего запроса с прежними параметрами
    • DELETE — ответ принят, запрос нужно удалить из очереди
    • DELETE_ALL — ответ принят, нужно удалить из очереди все запросы, поступившие от данного Контракт
  7. В том случае, если в предыдущем пункте Контракт прислал ответ DELETE (DELETE_ALL) — ПВЗ через метод DeleteRequest удаляет запрос (запросы) из смарт-контракта RequestsQueue.


При необходимости Контракт сам может удалять ставшие ненужными запросы, транзакционно используя метод DeleteRequest смарт-контракта RequestsQueue.

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

Для исключения «неправомочных» источников запросов смарт-контракт очереди содержит управляемый список адресов, которые должны быть исходными (txn.origin) адресами транзакций, ставящих запрос в очередь.

Описание методов контракта Requests Queue



AddRequest Добавить запрос в очередь Входные параметры:
  • Идентификатор запроса (bytes32)

DeleteRequest Удаление запроса из очереди Входные параметры:
  • Идентификатор запроса (bytes32)
CheckRequest Проверка наличия запроса в очереди Входные параметры:
  • Идентификатор запроса (bytes32)

GetRequest Выдать список идентификаторов запросов из очереди Входных параметров нет.
Выходные параметры:
  • Список идентификаторов (bytes32[])

AddBank Добавить адрес в список уполномоченных адресов Входные параметры:
  • Адрес (address)

CheckBank Проверить наличие адреса в списке уполномоченных адресов Входные параметры:
  • Адрес (address)



Описание необходимых интерфейсных методов Контрактов



GetExternalRequest Выдать параметры запроса Входные параметры:
  • Идентификатор запроса (bytes32)
    Выходные данные (в виде массива bytes32[]):
  • Спецификация тайминга (когда или с какой периодичностью должен исполняться запрос)
  • Имя шаблона реализации запроса
  • Параметры запроса (могут отсутствовать или их может быть несколько)

SetExternalResponse Принять ответ на запрос Входные параметры:
  • Идентификатор ответа (bytes32)
  • Идентификатор запроса (bytes32)
  • Данные ответа (массив bytes32)
CheckExternalResponse Проверить факт обработки ответа на запрос Входные параметры:
  • Идентификатор ответа (bytes32)
  • Идентификатор запроса (bytes32)

Выходные данные:
  • Статус обработки ответа
  • Для статуса обработки ответа возможен один из следующих вариантов:
  • FAIL — ответ не былк получен или обработан
  • DELETE — ответ получен и обработан, запрос из очереди удалить
  • DELETE_ALL — ответ получен и обработан, все запросы данного контракта из очереди удалить
  • REPEAT — ответ получен и обработан, продолжить исполнять запрос



Пример реализации


Ниже приведен пример реализации методов в составе контракта.
Приводимый контракт предполагает вызов двух внешних запросов — просрочки даты (шаблон OVERDUE) и проверки регистрации организации (DADATA_EXISTS_WAIT).
Код, непосредственно не относящийся к работе с внешними запросами, исключен.

Описание хранимых переменных


bytes32 Status ;
bytes32 ExpireDate ;
bytes32 OrgName ;
address Queue ;
bytes32[] Request_1 ;
bytes32[] Request_2 ;
bytes32 Request_id_1 ;
bytes32 Request_id_2 ;
bytes32 Response_id_1 ;
bytes32 Response_id_2 ;


Конструктор контракта


function SomeContract(..., bytes32[] logics)
{
Owner =msg.sender ;
ExpireDate=logics[0] ;
OrgName =logics[1] ;
Status ="New" ;
Queue =0xd9b076d0b559f70782f379582bd3d54b85fc42cb ;
Request_1.length= 3 ;
Request_1[0] ="DAILY 00:10" ;
Request_1[1] ="OVERDUE" ;
Request_1[2] = ExpireDate ;
Request_2.length= 3 ;
Request_2[0] ="PERIOD 10" ;
Request_2[1] ="DADATA_EXISTS_WAIT" ;
Request_2[2] = OrgName ;
}

Регистрация запросов в очереди (при переходе контракта в соотвествующий статус)


function SetStatus(bytes32 status_, ...)
{
address self_addr ;
Status=status_ ;
if(status_=="Released_") {
self_addr=this ;
Request_id_1=bytes32(bytes20(self_addr)) | "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x001" ;
Request_id_2=bytes32(bytes20(self_addr)) | "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x002" ;
Queue.call.gas(0x30000).value(0)(bytes4(sha3("AddRequest(bytes32)")), Request_id_1) ;
Queue.call.gas(0x30000).value(0)(bytes4(sha3("AddRequest(bytes32)")), Request_id_2) ;
}
}

Выдать параметры запроса


function GetExternalRequest(bytes32 request_id_) constant returns (bytes32[] retVal)
{
if(request_id_==Request_id_1) return(Request_1) ;
if(request_id_==Request_id_2) return(Request_2) ;
}


Принять ответ на запрос


function SetExternalResponse(bytes32 response_id_, bytes32 request_id_, bytes32[] response_)
{
if(tx.origin!=Owner) return ;
if(Status!="Released_") return ;
if(request_id_==Request_id_1) {
Response_id_1=response_id_ ;
Status ="Overdue__" ;
}
if(request_id_==Request_id_2) {
Response_id_2=response_id_ ;
Status ="ToBank___" ;
}
}



На пользу сообщества


Для желающих попробовать технологии слежения за внешними событиями и работу с оракулами и провайдерами внешних запросов нами в сети Ethereum Rinkeby развернуты:
  • Календарный оракул (отдает текущую дату по Москве)
  • Портал внешних запросов

Календарный оракул


Календарный Оракул расположен в тестовой сети Rinkeby по адресу 79548a65e3ce179ec8d208c22ee84435dc34058f и выдает текущую календарную дату по Москве в формате YYYY/MM/DD.

Пример обращения к Оракулу:

contract Check_request
{
Calendar Oracle ; // Оракул-переменная
bytes32 Date ;
function Check_request()
{
// Инициализация Оракул-переменной на адрес Оракула
Oracle=Calendar(0x79548a65e3ce179ec8d208c22ee84435dc34058f) ;
// Получение информации из Оракула
Date=Oracle.GetDate() ;
}
}

//
// Описание абстрактного метода с интерфейсами Оракула
//
contract Calendar
{
function GetDate() constant returns (bytes32 retVal) ;
}


Портал внешних запросов


Очередь смарт-контракта Портала внешних запросов расположена в тестовой сети Rinkeby по адресу d9b076d0b559f70782f379582bd3d54b85fc42cb.

Протокол взаимодействия с Порталом внешних запросов описан выше.
На текущий момент публично открыты следующие шаблоны запросов:
  • OVERDUE — контроль просрочки даты, параметр запроса — дата просрочки в формате YYYY/MM/DD
    Транзакция события передается на запрашивавший смарт-контракт в момент обнаружения просрочки даты и содержит текущую дату.
  • WEATHER_TEMP — Запрос текущей температуры (через портал api.openweathermap.org), параметр запроса — название города, например Moscow (подробности можно посмотреть на портале-источники).
    Транзакция события передается на запрашивавший смарт-контракт в момент получения ответа от портала-источника.

Для доступа к очереди Портала внешних запросов необходимо сообщить нам (в комментарии или в личку) адрес счета Ethereum, с которого будут направлены транзакции постановки в очередь.
Желательно прикладывать к транзакции 0.1 Ether (это бесплатно, мы в тестовой сети) для отладки механизма платных услуг. В Rinkeby получить эфир можно только с помощью faucet, из-за отсутствия возможности майнинга ввиду протокола PoA.

Некоторые замечания по опыту интеграции сторонних компонентов


Ethereum


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

Ethereum Solidity


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

Ethereum Swarm


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

Сборка из исходников и развертывание узла Swarm под Windows – крайне нетривиальная задача. Разработчики тестировались и готовили документацию только под linux и OSX, о чем честно признаются.

Storj.io


Крайне сложный для использования, слишком детальный API. Для простой интеграции желательно иметь «укрупненный» API, аналогичный реализованному в Ethereum Swarm — положить файл, извлечь файл.

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

Выводы исследования


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

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

Во-первых, правовые вопросы сделок, осуществляемых через блокчейн, в том числе юридический статус записи в блокчейне.

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

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

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

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

Кейс с аккредитивом позволил нам получить как техническую базу, так и опыт реализации децентрализированных приложений в области ТФ. Сейчас мы смотрим несколько направлений, на которых нам интересно сделать пилот, о них мы расскажем позже.

Stay tuned!
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/332756/


Метки:  

[Перевод] Красный, белый, голубой: восемь правил подбора цветовой палитры, которые должны знать все

Четверг, 13 Июля 2017 г. 10:12 + в цитатник
Взаимодействие человека с компьютером во многом опирается на графические элементы интерфейса, и цвет играет в этом процессе не последнюю роль. Как однажды сказал Pierre Bonnard: «Цвет не просто делает дизайн приятным для глаз, но и подкрепляет его».

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




1. Ограничьте количество цветов


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

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

Как выстроить палитру


Но как выбрать эти два-три цвета? Тут вам поможет цветовой круг.



Такой круг из 12 цветов — один из основных материалов для составления палитры.

Существует целый ряд готовых стандартных схем, которые могут облегчить процесс создания палитры, особенно для новичков:

— Одноцветные палитры



Одноцветные схемы оставляют очень приятное впечатление (особенно если выполнены в оттенках синего или зеленого). Как вы видите на примере Facebook, палитра выглядит очень аккуратно и элегантно.

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

— Палитры из родственных цветов





Родственные цвета — это те, которые располагаются рядом друг с другом на круге.

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

— Палитры из комплементарных цветов



Используя комплементарные (противоположные) цвета, вы легко можете визуально выделить элемент.

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

— Кастомизированные палитры



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

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

2. Черпайте вдохновение в природе


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



Природа создает лучшие сочетания цветов. Из этой фотографии может получиться отличная цветовая схема.

3. Старайтесь придерживаться пропорции 6/3/1


При составлении палитры вам также пригодится вечно актуальное правило от дизайнеров интерьеров: пропорция 6/3/1 позволяет создать идеальный баланс цветов в любом пространстве.

Воплотить эту концепцию в реальность очень просто: ваш опорный цвет должен занимать 60% процентов, дополнительный — 30%, а 10% отводится на акценты. Основная идея состоит в том, что дополнительный цвет должен служить поддержкой для основного, но при этом быть легко различимым на его фоне. На цвет для визуального выделения отдельных областей приходится 10% экрана — можете использовать его для призыва к действию или же любого другого элемента.



4. Сначала проектируйте в черно-белой гамме


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

Вводите цвет в последнюю очередь и очень целенаправленно.



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

5. Избегайте черного цвета


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

Если добавить в ваш комплект тщательно отобранных цветов черный, он «задавит» собой все остальные. Он так сильно бросается в глаза именно потому, что воспринимается как естественный. Многие приложения, которыми мы пользуемся изо дня в день, добавляют в интерфейс якобы черные цвета, которые на самом деле темно-серые. Например, самый темный цвет в верхней панели приложения Asos — не #000000, а #242424. Так что не забывайте регулировать насыщенность.



6. Выстраивайте визуальную иерархию при помощи контраста


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



Благодаря контрасту одна область экрана заметно отличается от остальных.

7. Используйте цвет, чтобы воздействовать на эмоциональное состояние пользователя


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

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

Красный, Оранжевый, Желтый


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

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

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



Зеленый, Синий, Фиолетовый


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

Синий (спокойный, ответственный, заслуживающий доверия, надежный): Синий цвет обычно ассоциируется с чувствами покоя и умиротворения, а также с силой и надежностью, что в общем дает ощущение профессионализма и возбуждает доверие. Синий внушает нам, что мы в безопасности. По этой причине его активно используют банки и IT-компании. У многих титанов на рынке социальных сетей (Facebook, Twitter, LinkedIn) в интерфейсе также фигурирует синий.

Фиолетовый (роскошный, загадочный, романтический, духовный): Фиолетовый исторически считается цветом, связанным с людьми королевских кровей, и намекает, что продукт относится к категории элитных.



Розовый, Черный, Белый, Серый


Розовый (женственность, невинность, юность): Наиболее широко известная ассоциация с розовым цветом — это женственность.

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

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

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



Здесь важно отметить, что значения цветов могут сильно меняться в зависимости от обстоятельств и культурного контекста. Чтобы глубже ознакомиться с этой темой, прочитайте статью Symbolism Of Colors And Color Meanings Around The World.

8. Сделайте дизайн доступным


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

Не используйте цвет в качестве единственного индикатора


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



Слева направо: цветовой круг глазами обычного человека — Цветовой круг глазами человека с дейтеранопическим типом дальтонизма — Цветовой круг глазами человека с протанопическим типом дальтонизма.

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



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

Делайте текст как можно контрастнее


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



Низкая контрастность может стать смертельным ударом для юзабилити.

Отслеживайте контрастность, чтобы быть уверенными, что цвет текста будет в достаточной степени выделяться относительно фонового цвета и даже человек с дальтонизмом или очень слабым зрением сможет различать слова. Коэффициент контраста — это математическое выражение того, насколько один цвет отличается от другого (обычно он записывается так: 1:1, 21:1). Чем больше разница между числами, тем сильнее цвета отличаются по яркости. На W3C рекомендуют следующие соотношения между яркостью текста и изображений:

  • Если текст мелкий, соотношение должно быть не меньше 4.5:1;
  • Для крупного текста (то есть 14 pt и выше для жирного шрифта, 18 pt и выше — для обычного) оптимально соотношение 3:1 и больше.


Но есть хорошая новость: вам не придется проверять все вручную. При помощи Color Contrast Checker рассчитать коэффициент можно в несколько кликов.



Бонус: Инструменты, необходимые для UX дизайнера


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

Adobe Color CC
Adobe Color CC (раннее известный как Kuler) — отличное решение, чтобы находить, редактировать и создавать палитры. Вы можете модифицировать каждый цвет, входящий в состав гаммы, или устанавливать его в качестве опорного буквально в пару кликов. Готовые палитры можно сохранять и добавлять в библиотеку; кроме того, в открытом доступе на сайте находится большое количество цветовых гамм, созданных другими членами сообщества.



Dribbble Search-by-color
Если хотите посмотреть, как другие дизайнеры применяют тот или иной цвет в своих проектах, перейдите по адресу dribbble.com/colors и выберите нужный оттенок.



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



Colorzilla
Colorzilla — расширение для браузеров Google Chrome и Mozilla Firefox, которое включает целую кучу инструментов для работы с цветом, включая пипетку с возможностью захвата цвета, генератор градиентов CSS и функцию просмотра палитр.



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



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



Симулятор дальтонизма в Adobe Photoshop
Photoshop позволяет проверить, насколько ваш дизайн универсально доступен. Достаточно перейти на вкладку View > Proof Setup и выбрать интересующий ваш тип (Protanopia type или Deuteranopia type).



NoCoffee Vision Simulator for Chrome
Чтобы удостовериться, что ваш дизайн доступен для всех, неплохо бы на себе испытать дальтонизм в процессе проектирования. NoCoffee Vision Simulator обеспечивает возможность создать для любого интерфейса симуляцию, показывающую, как она выглядит для людей с цветовой слепотой или слабым зрением. Например, применив фильтр «Deuteranopia» во вкладке «Color Deficiency», вы увидите веб-сайт в тонах серого. Это поможет вам адаптировать интерфейс под потребности людей, у которых проблемы с глазами.



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

https://habrahabr.ru/post/332956/


Метки:  

[Перевод] Почему изменения в новом Phoenix 1.3 так важны

Четверг, 13 Июля 2017 г. 10:10 + в цитатник


Phoenix Framework всегда был классным. Но он никогда не был таким классным, как с новым релизом 1.3 (который сейчас находится в стадии RC2).


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


Давайте начнем!


Перевод выполнен самим автором оригинальной статьи Никитой Соболевым.


Существующие проблемы


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


Директория web — чистая магия


При работе над проектом с использованием Phoenix у вас есть два места для исходного кода: lib/ и web/. Концепция такова:


  • Поместите всю свою бизнес-логику и утилиты внутрь lib/.
  • Поместите всё, что связано с вашим веб-интерфейсом (контроллеры, представления, шаблоны) внутрь веб-каталога web/.

Но понятно ли это разработчикам? Я так не думаю.


Откуда появился этот веб-каталог? Это особенность Phoenix? Или другие фреймворки тоже используют его? Должен ли я использовать lib/ с Phoenix-проектами или он зарезервирован для некоторой глубинной магии? Все эти вопросы появились у меня после моей первой встречи с Phoenix.


До версии 1.2 только директория web/ автоматически перезагружалась. Итак, зачем мне создавать какие-либо файлы внутри lib/ и перезапускать сервер, когда я могу поместить их где-то внутри web/ для быстрой перезагрузки?


Это приводит нас к еще более важным вопросам: относятся ли мои файлы-модели (назовем их моделями в этом конкретном контексте) к web-части приложения или к основной логике? Можно ли разделить логику на разные домены или приложения (например, как в Django)?


Эти вопросы остаются без ответа.


Бизнес-логика в контроллерах


Более того, код шаблона, который идет в Phoenix, предполагает другой способ. Можно получить следующий код в новом проекте:


defmodule Example.UserController do
  use Example.Web, :controller

  # ...

  def update(conn, %{"id" => id, "user" => user_params}) do
    user = Repo.get!(User, id)
    changeset = User.changeset(user, user_params)

    case Repo.update(changeset) do
      {:ok, user} ->
        render(conn, Example.UserView, "show.json", user: user)
      {:error, changeset} ->
        conn
        |> put_status(:unprocessable_entity)
        |> render(Example.ChangesetView, "error.json", changeset: changeset)
    end
  end
end

Что должен делать разработчик, когда пользователю после успешного обновления должно быть отправлено электронное письмо? Контроллер так и просится, чтобы его расширили. Просто поставьте еще одну строку кода перед render/4, что может пойти не так? Но. Только что Phoenix сам подтолкнул нас к неправильному использованию своей кодовой базы: мы пишем бизнес логику в контроллере!


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


Схемы не являются моделями


В какой-то момент без особых причин схемы Ecto стали называться «моделями». В чем разница между «моделью» и «схемой»? Схема — это всего лишь способ определить структуру — структуру базы данных в данном конкретном случае. Модели как концепция намного сложнее схем. Модели должны обеспечивать способ управления данными и выполнять различные действия, как модели в Django или Rails. Elixir как функциональный язык не подходит для концепции «модели», поэтому они были упразднены в проекте Ecto.


Файлы внутри models/ не были организованы. По мере своего роста ваше приложение становится хаотичным. Как эти файлы связаны между собой? В каком контексте мы используем их? Это было трудно понять.


Кроме того, директория models/ рассматривалась как еще одно место для размещения вашей бизнес-логики, что нормально для других языков и фреймворков. Существует уже знакомая концепция «fat models». Но такая концепция, опять же, не подходит для Phoenix по уже названным причинам.


Решения


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


Требования


В этом руководстве предполагается, что у вас есть elixir-1.4, и он работает. Нет? Значит, установите его!


Установка


Для начала вам нужно будет установить новую версию Phoenix:


mix archive.install
https://github.com/phoenixframework/archives/raw/master/phx_new.ez

Создание нового проекта


По завершению установки надо проверить, всё ли на месте. mix help вернет вам что-то вроде этого:


mix phoenix.new       # Creates a new Phoenix v1.1.4 application
mix phx.new           # Creates a new Phoenix v1.3.0-rc.1 application using the experimental generators

Вот тут и проявляется первое изменение: новые генераторы. Старые генераторы назывались phoenix, а новые — просто phx. Теперь нужно меньше печатать. И, что более важно, новое сообщение разработчикам: эти генераторы новые, они будут делать что-то новое для вашего проекта.


Затем нужно создать структуру нового проекта, запустив:


mix phx.new medium_phx_example --no-html --no-brunch

Прежде чем мы увидим какие-либо результаты этой команды, давайте обсудим параметры. --no-html удаляет некоторые компоненты для работы с html, поэтому phx.gen.html больше не будет работать. Но мы строим json API, и нам не нужен html. Аналогично --no-brunch означает: не создавайте brunch-файл для работы со статикой.


Изменения


Веб-директория


Глядя на ваши новые файлы, вы можете задаться вопросом: где находится веб-директория? Ну, вот и второе изменение. И довольно большое. Теперь ваша веб-директория находится внутри lib/. Она была особенной, многие люди неправильно поняли его главную цель, которая состояла в содержании веб-интерфейса для вашего приложения. Это не место для вашей бизнес-логики. Теперь все ясно. Поместите всё внутрь lib/. И оставьте только свои контроллеры, шаблоны и представления внутри новой web-директории. Вот как это выглядит:


lib
+-- medium_phx_example
    +-- application.ex
    +-- repo.ex
    +-- web
        +-- channels
        |   +-- user_socket.ex
        +-- controllers
        +-- endpoint.ex
        +-- gettext.ex
        +-- router.ex
        +-- views
        |   +-- error_helpers.ex
        |   +-- error_view.ex
        +-- web.ex

Где medium_phx_example — имя текущего приложения. Приложений может быть много. Итак, теперь весь код живет в одной и той же директории.


Третье изменение откроется вскоре после просмотра файла web.ex:


defmodule MediumPhxExample.Web do  
  def controller do
    quote do
      use Phoenix.Controller, namespace: MediumPhxExample.Web
      import Plug.Conn
      # Before 1.3 it was just:
      # import MediumPhxExample.Router.Helpers
      import MediumPhxExample.Web.Router.Helpers
      import MediumPhxExample.Web.Gettext
    end
  end

  # Some extra code:
  # ...

end

Phoenix теперь создает пространство имен .Web, которое очень хорошо сочетается с новой файловой структурой.


Создание схемы


Это четвертое и моё любимое изменение. Раньше у нас была директория web/models/, которая использовалась для хранения схем. Теперь концепция моделей полностью мертва. Внедрена новая философия:


  1. схема представляет структуру данных;
  2. контекст используется для хранения нескольких схем;
  3. контекст используется для предоставления публичного внешнего API. Другими словами, он определяет, что можно сделать с вашими данными.

Наше приложение будет содержать только один контекст: Audio. Начнем с создания Audio контекста с двумя схемами Album и Song:


mix phx.gen.json Audio Album albums name:string release:utc_datetime
mix phx.gen.json Audio Song songs album_id:references:audio_albums name:string duration:integer

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


lib
+-- medium_phx_example
    +-- application.ex
    +-- audio
    |   +-- album.ex
    |   +-- audio.ex
    |   +-- song.ex
    +-- repo.ex
    +-- web
        +-- channels
        |   +-- user_socket.ex
        +-- controllers
        |   +-- album_controller.ex
        |   +-- fallback_controller.ex
        |   +-- song_controller.ex
        +-- endpoint.ex
        +-- gettext.ex
        +-- router.ex
        +-- views
        |   +-- album_view.ex
        |   +-- changeset_view.ex
        |   +-- error_helpers.ex
        |   +-- error_view.ex
        |   +-- song_view.ex
        +-- web.ex

Каковы основные изменения в структурах по сравнению с предыдущей версией?


  1. Теперь схемы не принадлежат web/, а директория models/ вообще исчезла.
  2. Схемы теперь разделены контекстом, который определяет, как они связаны друг с другом.

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


defmodule MediumPhxExample.Audio.Album do
  use Ecto.Schema

  schema "audio_albums" do
    field :name, :string
    field :release, :utc_datetime

    timestamps()
  end
end

defmodule MediumPhxExample.Audio.Song do
  use Ecto.Schema

  schema "audio_songs" do
    field :duration, :integer
    field :name, :string
    field :album_id, :id

    timestamps()
  end
end

Всё за исключением самой схемы исчезло. Нет обязательных полей, никаких функций changeset/2 или каких-либо других. Генератор теперь даже не создает belongs_to для вас. Вы сами управляете связями ваших схем.


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


defmodule MediumPhxExample.Audio do
  @moduledoc """
  The boundary for the Audio system.
  """

  import Ecto.{Query, Changeset}, warn: false
  alias MediumPhxExample.Repo

  alias MediumPhxExample.Audio.Album

  def list_albums do
    Repo.all(Album)
  end

  def get_album!(id), do: Repo.get!(Album, id)

  def create_album(attrs \\ %{}) do
    %Album{}
    |> album_changeset(attrs)
    |> Repo.insert()
  end

  # ...

  defp album_changeset(%Album{} = album, attrs) do
    album
    |> cast(attrs, [:name, :release])
    |> validate_required([:name, :release])
  end

  alias MediumPhxExample.Audio.Song

  def list_songs do
    Repo.all(Song)
  end

  def get_song!(id), do: Repo.get!(Song, id)

  def create_song(attrs \\ %{}) do
    %Song{}
    |> song_changeset(attrs)
    |> Repo.insert()
  end

  # ...

  defp song_changeset(%Song{} = song, attrs) do
    song
    |> cast(attrs, [:name, :duration])
    |> validate_required([:name, :duration])
  end
end

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


Использование контроллера


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


defmodule MediumPhxExample.Web.AlbumController do
  use MediumPhxExample.Web, :controller

  alias MediumPhxExample.Audio
  alias MediumPhxExample.Audio.Album

  action_fallback MediumPhxExample.Web.FallbackController

  # ...

  def update(conn, %{"id" => id, "album" => album_params}) do
    album = Audio.get_album!(id)

    with {:ok, %Album{} = album} <- Audio.update_album(album, album_params) do
      render(conn, "show.json", album: album)
    end
  end

  # ...

end

В действии update/2 теперь есть только три осмысленные строчки кода.


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


Контроллеры даже не обрабатывают ошибки. Для работы с ошибками предназначен специальный новый fallback_controller. Эта новая концепция — шестое изменение. Оно позволяет иметь все обработчики ошибок и коды ошибок в одном месте:


defmodule MediumPhxExample.Web.FallbackController do
  @moduledoc """
  Translates controller action results into valid `Plug.Conn` responses.
  See `Phoenix.Controller.action_fallback/1` for more details.
  """
  use MediumPhxExample.Web, :controller

  def call(conn, {:error, %Ecto.Changeset{} = changeset}) do
    conn
    |> put_status(:unprocessable_entity)
    |> render(MediumPhxExample.Web.ChangesetView, "error.json", changeset: changeset)
  end

  def call(conn, {:error, :not_found}) do
    conn
    |> put_status(:not_found)
    |> render(MediumPhxExample.Web.ErrorView, :"404")
  end
end

Что происходит, когда результат из Audio.update_album(album, album_params) не соответствует {:ok, %Album{} = album}? В этой ситуации вызывается контроллер, определенный в action_fallback. И будет выбран правильный call/2, что в свою очередь возвращает правильный ответ. Легко и приятно. Никаких обработок исключений в контроллере.


Заключение


Внесенные изменения весьма интересны. Их много, они все сфокусированы на том, чтобы загубить старые привычки программистов, которые пришли из других языков программирования. И новые изменения стараются пополнить философию Phoenix-Way новыми практиками. Надеюсь, эта статья была полезна и побудила вас использовать Phoenix Framework по максимуму. Заходите ко мне на GitHub.


Благодарим Никиту за подготовку перевода своей собственной оригинальной статьи и с радостью публикуем материал на Хабре. Никита представляет сообщество ElixirLangMoscow, которое организует митапы по Эликсиру в Москве, а также является активным контрибьютером в опенсорс и вносит значительный вклад в наше сообщество Вунш. На сайте вас ждут 3 десятка тематических статей, еженедельная рассылка и новости из мира Эликсира. А для вопросов у нас есть чат в Телеграме с отличными участниками.

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

https://habrahabr.ru/post/332898/


Метки:  

Идентификация коинтегрированных пар акций на фондовых рынках

Четверг, 13 Июля 2017 г. 10:05 + в цитатник

Метки:  

IoT на страже порядка, или как сделать наш мир немного безопаснее

Четверг, 13 Июля 2017 г. 10:00 + в цитатник
Мы любим детективные истории про Шерлока Холмса, Эркюля Пуаро, комиссара Мегрэ, Эраста Фандорина или Настю Каменскую, но давайте попробуем представить, как повлияет на их сюжет наступление эпохи Интернета вещей (IoT). Современные технологии позволяют намного усложнить жизнь преступников – не важно, идет ли речь о взломе дома, угоне автомобиля или онлайн-мошенничестве в банковской сфере, существуют умные решения, предлагающие многоуровневую защиту. И если бы авторы знаменитых детективов писали свои произведения сегодня, им было бы не так уж просто придумать злодея, которому удалось бы уйти от наблюдения или запутать следствие! Познакомимся с самыми хитрыми инновациями, созданными для защиты от преступников.



Защита дома


Существует множество инновационных IoT решений для защиты дома. Системы M2M (machine-to-machine) сигнализаций можно с легкостью устанавливать в любой точке здания, что заметно осложняет жизнь взломщика. Кроме того, все большее распространение сегодня получают умные камеры, благодаря которым люди получают возможность присматривать за своими домами через смартфоны удаленно.

Помимо прочего, развитие Интернета вещей привело к созданию умных замков, которые обеспечивают более высокий уровень безопасности по сравнению с традиционными запирающими механизмами. Например, новые замки мирового производителя умных замков и сейфов Dessmann поставляются со специальными модулями машинной идентификации (Machine Identification Modules) и защищенными элементами (Secure Elements), которые позволяют людям открывать дверь с помощью смартфона.

У пользователей имеется возможность создавать цифровые связки ключей для разных замков и просматривать историю использования созданных ключей. В довершение к этому, система отправляет пользователям уведомление в случае попытки взлома двери и включает сигнализацию. Ожидается, что глобальный рынок умных замков будет стремительно развиваться в ближайшие годы, до 18% каждый год, достигнув 1 млрд долларов к 2024 году, помимо повышения безопасности, создавая дополнительные удобства при совместном использовании жилых и офисных помещений.

Предотвращение угона автомобиля


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

  1. Виртуальные ключи от автомобиля. Такое решение предполагает, что автомобиль привязан к мобильному приложению – это избавляет от необходимости в физических ключах, которые можно где-нибудь позабыть или которые могут быть украдены. Теперь у преступника уже не получится угнать автомобиль, раздобыв ключи или воспользовавшись отмычкой. Но здесь важно помнить, что виртуальные ключи от автомобиля требуют использования надлежащих решений для обеспечения кибербезопасности, ведь в противном случае возникает угроза со стороны другой категории злоумышленников – хакеров! Можно выделить пять основных принципов, которым необходимо следовать при разработке решения виртуальных ключей для автомобиля, которые мы обсуждали ранее.

    Не так давно Daimler AG принял решение об использовании Gemalto Trusted Services Hub для создания цифровых ключей на базе смартфонов в моделях Mercedez-Benz E-Class. Открыть и закрыть автомобили можно будет просто приложив к двери смартфон с поддержкой NFC и установленным приложением. Завести машину также можно будет телефоном, причем даже если он разряжен – просто вставив его в зарядный док и нажав кнопку «Старт».

  2. Развитие Интернета вещей позволило разработать системы нахождения похищенного транспортного средства. Сегодня многие автомобили оснащаются GPS системами и сотовыми модемами, которые помогают отслеживать их местоположение. Даже если преступникам удалось угнать автомобиль, у них вряд ли получится долго оставаться незамеченными.

    Ту же технологию можно использовать для защиты умных велосипедов – например, компания Vanmoof дает гарантию, что найдет велосипед в случае угона (а если не найдет – то вы получите новый), а компания Mobike в Сингапуре и Манчестере использует IoT и M2M решения Gemalto, встроенные в умные замки выдаваемых в прокат велосипедов, не только для их защиты от похитителей, но и для отслеживания технического состояния велосипедов и перераспределения своего «флота» в те места, где на данный момент обнаруживается наибольшая потребность. Пользователи же имеют возможность найти ближайший велосипед через установленное на смартфоне приложение. Это решение уже сертифицировано крупнейшими мобильными операторами в мире, так что расширение на другие рынки потребует от компании значительно меньших затрат.

В помощь полиции умного города


Представьте, что опытный убийца из классического романа Фредерика Форсайта «День Шакала» был пойман с помощью уличного фонаря. Не самый интересный поворот сюжета, не так ли? Однако, к счастью для нас, эта технология сегодня возможна и может стать действенным решением, чтобы остановить преступника. Например, решение ShotSpotter позволяет властям вычислить точку, в которой произошла стрельба, с точностью до 3 метров, а также определить, сколько человек участвует в беспорядках, и затем передать эту информацию сотрудникам полиции. Это решение, построенное на базе технологий Интернета вещей, может быть интегрировано даже в уличные фонари, что позволяет сделать городское окружение более безопасным.

Машинное обучение


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

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

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

https://habrahabr.ru/post/332944/


Метки:  

Как мы ловим Deadlock`и на PostgreSQL и чиним их

Четверг, 13 Июля 2017 г. 09:47 + в цитатник

Предисловие


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




Немного о внутреннем устройстве бэкэнда


  • Основная база данных — PostgreSQL 9.5.
  • Уровень изоляции транзакций — стандартный по умолчанию READ COMMITED.
  • ORM — SQLAlchemy.


Часть 1: Мониторинг


Как проявляется Deadlock


Когда у нас возникает Deadlock, то падает исключение следующего вида:

ERROR: deadlock detected
DETAIL: Process 18293 waits for ShareLock on transaction 639; blocked by process 18254.
Process 18254 waits for ShareLock on transaction 640; blocked by process 18293.
HINT: See server log for query details.
CONTEXT: while updating tuple (0,9) in relation "users"

Первое, на что следует обратить внимание, — это строчка:
HINT: See server log for query details.

Действительно, если мы посмотрим серверные логи, то увидим для этого же места следующее:
ERROR: deadlock detected

И дальше конкретику:
DETAIL: Process 18293 waits for ShareLock on transaction 639; blocked by process 18254.
      Process 18254 waits for ShareLock on transaction 640; blocked by process 18293.
      Process 18293: update users set balance = balance + 10 where id = 2;
      Process 18254: update users set balance = balance + 10 where id = 3;

HINT: See server log for query details.
CONTEXT: while updating tuple (0,9) in relation "users"

И, наконец, запрос, на котором произошла ошибка:
STATEMENT: update users set balance = balance + 10 where id = 2;


Логирование запросов при этом не обязано быть включено.

Круто. Но первая глобальная проблема для любого более-менее серьёзного проекта — то, что у вас нет доступа к серверным логам вследствие политики безопасности. Иногда вообще нет никакого доступа. А иногда можно попросить участок, но надо ждать. Иногда это 30 минут, иногда день.
А хотелось бы получать такую информацию сразу. В особенности, если у вас в проекте есть Sentry, и большинство ошибок команда разработки получает сразу.
Как-то подкрутить сервер, чтобы он такую информацию выдавал обычным клиентам — нельзя. Вследствие политики безопасности разработчиков базы. Но, если у вашего пользователя к базе доступ обычный, без всяких там ограничений на выполнения служебных функций и без Row-Level security policies, то организовать себе доступ к подобной информации всё же можно.

Ручной захват


Мы можем преобразовать наши классы так, чтобы вручную получать похожую информацию. И даже больше. Для этого после отлова исключения о дэдлоке, нам необходимо:
  • Временно ничего не откатывать в текущем соединении с базой и вообще ничего не трогать там.
  • Создать ещё одно соединение и выполнить в нём простейший запрос:
    SELECT * FROM pg_stat_activity;

  • Результаты положить в доступное для просмотра разработчиками хранилище. Например отправить по Sentry как ошибку.

Пример: реализация сбора информации по дедлоку на SQLAlchemy
db.py
from contextlib import contextmanager

from sqlalchemy import create_engine
from sqlalchemy.exc import OperationalError

engine = create_engine('postgresql+psycopg2://postgres:12345678@localhost/postgres')


def log_pg_stat_activity():
    '''Log, write or send through Sentry pg_stat_activity'''
    debug_conn = engine.connect()
    for process in debug_conn.execute('''
        SELECT pid, application_name, state, query FROM pg_stat_activity;
    ''').fetchall():
        print(process)

@contextmanager
def connection():
    conn = engine.connect()
    try:
        yield conn
    except OperationalError as ex:
        log_pg_stat_activity()
        raise


@contextmanager
def transaction():
    with connection() as conn:
        with conn.begin() as trans:
            try:
                yield conn
            except OperationalError as ex:
                if 'deadlock detected' in ex.args[0]:
                    log_pg_stat_activity()
                    # Log exception
                    print(ex)
                    trans.rollback()
                else:
                    raise


deadlock_test.py
from multiprocessing import Process
from time import sleep
from threading import Thread

from sqlalchemy.orm import sessionmaker

from db import transaction


def process1():
    with transaction() as tran:
        tran.execute('UPDATE users SET balance = balance + 10 WHERE id = 3;')
        sleep(1)
        tran.execute('UPDATE users SET balance = balance + 10 WHERE id = 1 RETURNING pg_sleep(1);')


def process2():
    with transaction() as tran:
        tran.execute('UPDATE users SET balance = balance + 10 WHERE id = 1;')
        sleep(1)
        tran.execute('UPDATE users SET balance = balance + 10 WHERE id = 3 RETURNING pg_sleep(1);')


if __name__ == '__main__':
    p1 = Thread(target=process1)
    p2 = Thread(target=process2)
    p1.start()
    p2.start()
    sleep(4)


Или на github.

В данном случае у нас есть высокая вероятность того, что мы увидим какой именно запрос сломал нам транзакцию, вычислив его по PID и посмотрев текущий query.

Но бывает и так, что вычислив соединение по PID и посмотрев на query вы можете увидеть совсем не тот query, который устроил нам дедлок, а какой-нибудь следующий за ним по логике. Ведь пока вы ловили исключение и открывали соединение, нужный нам запрос для отлова мог и завершиться. Всё что мы можем здесь сделать — это работать через pgBouncer или его аналоги для минимизации времени установления соединения и использовать application_name.

application_name


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

Сказано, сделано!

Пример: реализация установки application_name через SQLAlchemy
db.py
from traceback import extract_stack
@contextmanager
def transaction(application_name=None):
    with connection() as conn:
        if application_name is None:
            caller = extract_stack()[-3]
            conn.execution_options(autocommit=True).execute("SET application_name = %s", '%s:%s' % (caller[0], caller[1]))
        with conn.begin() as trans:
            try:
                yield conn
            except OperationalError as ex:
                if 'deadlock detected' in ex.args[0]:
                    log_pg_stat_activity()
                    # Log exception
                    print(ex)
                    trans.rollback()
                else:
                    raise


Или на github.

Вуаля. Теперь можно быстро открывать файлы в нужных местах и смотреть код.
pid application_name state query
1 8613 deadlock_test.py:10 idle in transaction (aborted) UPDATE users SET balance = balance + 10 WHERE id = 1 RETURNING pg_sleep(1);
2 8614 deadlock_test.py:17 active UPDATE users SET balance = balance + 10 WHERE id = 3 RETURNING pg_sleep(1);
3 8617 active SELECT pid, application_name, state, query FROM pg_stat_activity;


Думаем о серверных логах


Вся эта магия, описанная сверху хороша, но теоретически может не сработать. Иногда вам всё же не обойтись без серверных логов, поэтому необходимо сделать ещё два шага:
  • Обговорить понятную процедуру получения нужных участков серверных логов в разумное время с заинтересованными сторонами.
  • Делать их в требуемом вами формате, изменив log_line_prefix в postgresql.conf. На машине разработчика например можно так: log_line_prefix = 'APP:%a PID:%p TR:%x '.


Часть 2: Как бороться с дедлоками




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

Несколько практик избегания deadlock`ов


Частый случай №1: Классический дедлок


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

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

Всё что тут можно сделать: или выстраивать начисления в цепочку, но это медленно, или позволять начислениям падать и пробовать начислить чуть позже.

Частый случай №2: Сам себе злобный буратино (ССЗБ)


У нас походовая игра. Раз в ход происходит пересчёт баланса игроков, учитывая большое количество совершённых ими игровых действий. На время изменения баланса мы блокировали другие изменения через SELECT… FOR UPDATE. Хотя мы блокировали не сразу всех, а чанками по 100, всё равно иногда уходили в дэдлок с процессом, который начисляет бонусы за бой, который не останавливается на время расчёта хода.

Так вот, оказалось, что мы были неправы. SELECT… FOR UPDATE — слишком мощная блокировка, необходимая только если выполняются 2 условия:
  • Условный id текущей таблицы используется как внешний ключ в другой.
  • Этот же условный id может быть изменён/удалён в результате дальнейших действий.

Возьмём пример:
--P1: 
BEGIN;
--P2: 
BEGIN;
--P1: 
SELECT id FROM clans WHERE id=1 FOR UPDATE;
--P2: 
INSERT INTO users(clan_id, name) VALUES(1, 'Alpha');

P2 в данной ситуации повиснет, поскольку мы даём СУБД понять, что запись с id=1 может перестать существовать. Однако в P1 мы не делаем ничего такого, только хотим защитить баланс клана от изменений. Поэтому, когда мы изменили FOR UPDATE на FOR NO KEY UPDATE, мы перестали ловить дэдлоки.

Бонус №1


SELECT… FOR UPDATE в примере выше вызван явно. Но вы получите аналогичный эффект, если затронете своими изменениями уникальный ключ, на который ссылается внешний ключ из других таблиц. А любой UPDATE, который не затрагивает своими изменениями подобные ключи, вызовет блокировку аналогичную SELECT… FOR NO KEY UPDATE. Я вам рекомендую ознакомиться с этими особенностями в статье «Явные блокировки» в списке литературы ниже.

Бонус №2


Вернёмся к ещё одной любопытной детали из первоначальной ошибки:
CONTEXT: while updating tuple (0,9) in relation "users"
Что за тупл спросите вы? Это физический адрес строчки в таблице, из-за которой возник конфликт. Дело в том, что в каждой таблице есть служебные поля, которые запросом SELECT * не выбираются. Однако стоит явно указать к примеру ctid среди полей, как мы увидим этот самый тупл:
SELECT ctid, id, nickname, balance FROM public.users;

Пользы от него немного в случае дедлока, ибо разблокированный процесс скорее всего обновит конфликтную строчку, и у неё изменится этот ctid (поскольку любой UPDATE в PostgreSQL на самом деле INSERT, а старая строчка помечается как невидимая и позже будет удалена автовакуумом). Но знать стоит, вдруг когда-нибудь пригодится.

Что почитать


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

https://habrahabr.ru/post/323354/


Метки:  

О защите персональных данных на российском и европейских рынках

Четверг, 13 Июля 2017 г. 09:47 + в цитатник
С 1 июля вступили в силу последние поправки в российское законодательство о персональных данных. Они диктуют новые правила по обработке и хранению личной информации пользователей. Последние изменения встают в один ряд с чередой дополнительных требований к операторам данных. Эта статья рассказывает о новых реалиях и оптимальных решениях в области работы с личной информацией в России и Европе.


/ фото Thomas Leuthard CC

Персональные данные в европейской практике


Международный институт защиты персональных данных сравнительно юн в правовом смысле. Общие положения неприкосновенности частной жизни сформировались к 80-м годам прошлого века. Они базируются на принципах Всеобщей декларации о правах человека от 1948 года. Базовые положения обработки персональных данных вышли в свет в 1981 году в форме Конвенции Совета Европы. Ратификация этого договора Россией состоялась в 2005 году.

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

С ростом объема передаваемой личной информации в сети, кража личности набирала популярность как основной инструмент киберпреступников. По результатам исследования Javelin Strategy & Research, общий ущерб от онлайн-мошенничеств с использованием персональных данных в 2016 году стал рекордным за всю историю. Он составил $16 млрд по сравнению с $15 млрд годом ранее.

В 2013 году Служба Великобритании по предотвращению мошеннических действий (CIFAS) сообщила о более чем 50-процентном росте числа случаев незаконного использования сведений физических лиц. В одно время с этим Европарламент ужесточил закон о защите персональных данных. Это стало базой для разработки предложенного годом ранее Общего Регламента по защите данных (GDPR).

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

Технологический прогресс и глобализация в корне изменили способ сбора, использования и хранения данных. В 2014 году Европарламент продемонстрировал решительную поддержку GDPR — 621 голос за и 10 против в ходе пленарного заседания. 28 января 2016 года 47 стран Совета Европы, а также европейские учреждения, агентства и департаменты отметили 10-й ежегодный Европейский день защиты данных, а спустя четыре месяца окончательно утвердили регламент. Он должен вступить в силу 25 мая 2018 года.

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

В формулировку основного понятия в вопросе — «идентифицирующая информация» — были внесены корректировки. Согласно GDPR, если личность может быть теоретически установлена каким-либо способом, данные являются персональными. Иными словами, европейским компаниям, хранящим и обрабатывающим данные, отныне придется внимательно следить за своими действиями — идентифицировать личность возможно не только на основе очевидной информации, такой как ФИО.

/ фото Blue Coat Photos CC

GDPR также ужесточает санкции для нарушителей. Штрафы могут достичь до 4% годового оборота компании или суммы в 20 млн евро.

В соответствии с GDPR, обработчики персональных данных должны будут соблюдать ряд конкретных обязательств, в том числе вести документацию, применять соответствующие стандарты безопасности, назначать сотрудников по защите данных, соблюдать правила международной передачи данных и сотрудничать с национальными надзорными органами. Обработчики будут нести прямую ответственность за соблюдение этих правил. Разработчик антивирусного ПО ESET делает вывод, что новые требования, вероятно, приведут к увеличению стоимости услуг по обработке данных.

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

Чтобы подготовиться ко всем изменениям, у компаний и правительственных органов, работающих с персональными данными европейских граждан, остается меньше года. Немецкая исследовательская компания Bitkom Research установила, что по состоянию на июнь 2017 года 20% из двухсот IT-компаний еще не начинали готовиться к реализации требований регламента, и лишь одно из трех предприятий приступило к осуществлению первых подготовительных шагов.

Тем временем крупные компании уже предприняли стратегические меры. Была сформирована коалиция лидеров облачных вычислений, обслуживающих миллионы европейских клиентов. Она получила название CISPE (поставщики облачных инфраструктурных услуг в Европе). К коалиции присоединились такие компании, как IBM, Alibaba Cloud, Amazon Web Services, Microsoft Cloud. Вместе они встречают поток предприятий, в спешке переносящих свою IT-инфраструктуру в облака.

Самостоятельная подготовка ко всем требованиям GDPR весьма затратна. По результатам исследования информационной компании Veritas Technologies, в среднем компании прогнозируют расходы на сумму более $1,4 млн. Рост затрат и связанная с этим миграция касается в первую очередь обработки данных. Теперь предприятиям, деятельность которых включает в себя сбор персональных данных, важно найти надежного облачного провайдера, удостовериться, что поставщик облачного хостинга может обеспечить необходимый уровень безопасности, вести журналы инцидентов и соблюдать прочие требования GDPR.

Персональные данные в России


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

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

/ фото Wikimedia Commons CC

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

21 страна ЕС в настоящее время имеет законы, требующие от компаний хранить определенные типы (в первую очередь личных) данных на местном уровне. Основной кодекс CISPE по своей сути направлен на хранение европейских данных внутри Европы, однако отток в облака IT-компаний в Европе все же связан с требованиями к обработчикам данных. В России та же тенденция берет свое начало в обращении с персональными данными на территории страны. При этом обрабатывать их можно и за рубежом, но хранить и собирать обязательно следует в пределах страны. Ответственность за исполнение этого требования лежит на операторах данных.

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

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

Еще один вариант — услуга хостинга персональных данных. Такую услугу предлагает и поставщик облачных решений «ИТ-ГРАД». Решение обеспечивает общие меры защиты хранения информации и соблюдение дополнительных требований, продиктованных законодательством. Поставщик гарантиует соблюдение всех подзаконных актов и предоставляет уже аттестованное оборудование, которое согласовано с положениями ФЗ.

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

Компании, пользующиеся услугами облачного хостинга, получают ряд преимуществ. Это защищенный хостинг с использованием сертифицированного оборудования от hardware-производителей (NetApp, Cisco, IBM и др.), а также применение программно-аппаратного комплекса шифрования.

Получается, что услуга «хостинг ПДн» упрощает процедуру приведения инфраструктуры в соответствие с требованием 152-ФЗ, поскольку основную часть работ берет на себя провайдер. Клиент избавляется от необходимости аттестации собственной информационной системы, от затрат на создание и владение защищенной ИТ-инфраструктурой и от юридической ответственности.

P.S. Еще несколько материалов по теме из Первого блога о корпоративном IaaS:

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

https://habrahabr.ru/post/332396/


Метки:  

Уведомления о пропущенных звонках с Asterisk на Битрикс24

Четверг, 13 Июля 2017 г. 00:45 + в цитатник
Случается, что звонок с офисной АТС приходит на мобильный. И пропускается и на нём тоже.
Причины для этого у каждого свои, но последствия одни и те же — ты смотришь на городской номер офиса и думаешь, а кто же это звонил?



На Хабре уже не раз обсуждалась эта тема. Уведомления отправляли и на почту, и с помощью СМС, в последнее время модно делать это телеграм-ботами, однако я буду использовать Битрикс24.

Почему именно его? Добро пожаловать под кат!

Собственно, всё довольно просто — push-уведомления на телефонах подкупающе удобны и хочется использовать что-то, что доставит минимум хлопот при постоянном использовании.

Почему не Телеграм? Просто в нём реализуется только общий чат, а хотелось бы некоторой приятной приватности.

Почему не СМС? Оставив за скобками вопрос небесплатности, скажу о том, что не хочется плодить точки обслуживания при приёме или увольнении сотрудника. Хочется, чтобы отключил его в AD и всё, нет у него больше корпоративных сервисов.

Почему не почта (уж она-то по предыдущему пункту вполне годится)? Потому что сотрудники внезапно не используют её на телефонах (ну, у нас и массово во всяком случае).

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

В общем, к делу!
Предупреждение: код ниже может нанести вам моральные травмы. Сразу соглашаюсь с тем, что он ужасен, всё-таки я не кодер.

Для начала соорудим приёмник со стороны портала, на который будут приходить уведомления и который будет слать их в IM:
send_from_pbx.php


примечание: здесь всё довольно просто. Сначала дёргаем БД, получая соответствие «внутренний номер — ID пользователя в битриксе», затем отправляем сообщение от учётной записи, специально заведённой для Астериска. В принципе, можно отправлять и от имени системы, дело вкуса.

Теперь, на сервере Астериска выложим небольшой скрипт:
send.sh
#! /bin/bash

date=`date +%H:%M`

curl --cookie-jar cookies.txt 'https://portal.domain.ru/?login=yes' -H 'Host: portal.domain.ru' \
        --data 'AUTH_FORM=Y&TYPE=AUTH&backurl=%2F&USER_LOGIN=asterisk&USER_PASSWORD=perasperaadastra&USER_REMEMBER=Y' > /dev/null 2>&1

curl --cookie cookies.txt --data "message=Вам не смогли дозвониться. Абонент $1 звонил вам в $date&number=$2" \
        https://portal.domain.ru/send_from_pbx.php > /dev/null 2>&1


Здесь всё ещё проще. Авторизуемся с помощью curl, а затем им же шлём POST-ом сообщение и номер телефона.

Ну и наконец правим extensions.conf в Астериске. В макрос, который переводит звонки на мобильный в случае недоступности основного телефона дописываем ровно одну строку, вызывающую скрипт, который мы только что написали (на всякий случай приведу здесь весь макрос):
[macro-mobile]
exten => s,1,Set(CDR(userfield)=LOCAL)
exten => s,n,ExecIf($[${LEN(${CALLERID(num)})}=3]?Set(name=${SHELL( mysql asterisk -uasterisk -pperasperaadastra -sse 'SELECT callerid FROM peers WHERE defaultuser=${CALLERID(num)}' )$
exten => s,n,Macro(record,local)
exten => s,n,Dial(SIP/${MACRO_EXTEN},20)
exten => s,n,Dial(SIP/tel_out/${ARG1})
exten => s,n,System(/srv/asterisk/send2bitrix/send.sh "${name} (номер ${CALLERID(num)})" ${EXTEN})

в плане набора этот макрос используется следующим образом:
exten => 100,1,Macro(mobile,79251122333)

Здесь всё так же довольно очевидно. Макрос принудительно выставляет CallerID из БД, включает запись разговоров, пытается дозвониться на основной телефон, затем на мобильный и, потерпев фиаско, вызывает наш скрипт, уведомляющий пользователя о том, кто и во сколько ему звонил.

Вот и всё. Спасибо за внимание!
Если у вы хотите что-то уточнить, высказаться, и уж тем более дать добрый совет — пишите.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/332954/


Метки:  

Метод безъитеративного обучения однослойного персептрона с линейной активационной функцией

Среда, 12 Июля 2017 г. 23:39 + в цитатник
В этой статье не будет ни одной строчки кода, тут будет просто теория метода
обучения нейронных сетей, который я разрабатываю последние пол-года. Реализацию метода планирую в следующей статье.

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

$f_{los}(W)=\sum_{i=1}^n[y_i-(\sum^m_{j=i}w_j\cdot x^i_j)]^2$



Где $W = \{w_1,...w_k\};$, m — количество входов в нейронной сети, n — мощность обучающей выборки, которая состоит из пар: выходного идеального значения «y» для каждого нейрона и входного вектора «x». Так же стоит отметить, что обучать каждый нейрон можно по отдельности.
Сеть будет обучена, если: $f_{los}(W) \rightarrow min$, т.е. если ошибка будет минимальной.

Учитывая, что функция активации линейна, а уравнение функции ошибки квадратичное, то очевидно что максимума такая функция не имеет, а следовательно условие при котором $\frac{\partial f_{los}(W)}{\partial w_i} = 0$, это условие минимума. Давайте для начала определим эту производную и приравняем её к 0.

$\frac{\partial f_{los}(W)}{\partial w_j} = -2\cdot \sum_{i=1}^{n}(y_i-\sum _{j=1}^mw_j\cdot x_j^i)x_k^i = 0 ;$



После ряда преобразований получаем:

$\sum_{j=1}^m(w_j\cdot \sum_{i=1}^{n}x_j^i\cdot x^i_k)=-\sum_{i=1}^{n}x_k^i\cdot y_i;$



Где k — номер уравнения в системе.

Для завершения обучения нам нужно рассчитать вектор весов W. Не сложно заметить что последнее выражение, если его записать для каждого уравнения, представляет собой СЛАУ относительно W. Для решения этой системы я выбрал метод Крамера(метод Гаусса быстрее работает, но он не такой наглядный). Каждый вес нейрона можно записать как:

$\\w_j=\frac{det(A_j)}{det(A)}; \\A= \begin{pmatrix} a_{11} ..... .... a_{1m}\\ ..... ....\\ .. ... .. ..\\ a_{m1} ..... .... a_{mm} \end{pmatrix}; \\B=\begin{pmatrix} b_1\\ ..\\ ..\\ b_m \end{pmatrix}; \\a_{kj} = \sum_{i=1}^nx_j^i\cdot x^i_k; \\b_k = -\sum_{i=1}^ny_i\cdot x^i_k;$



Здесь матрица $A_j$ это матрица «A» в которой j-й столбец заменен на вектор B. Это обучение одного нейрона, в силу того, что нейроны никак не связаны между собой можно обучать их параллельно, независимо друг от друга.

P.S. Если есть замечания по статье, пишите, всегда рад конструктивной критики.
Как Вы считаете, такой метод стоит развивать или нет?

Проголосовало 3 человека. Воздержалось 4 человека.

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

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

https://habrahabr.ru/post/332936/


Метки:  

Каким должен быть идеальный конфигуратор отчетов

Среда, 12 Июля 2017 г. 23:10 + в цитатник
Поступил от клиента запрос: «xотим конфигуратор отчетов».Чтобы простые сотрудники, далекие от SQL, могли составлять всевозможные, необходимые для них отчеты, по любым данным в системе.
Абстрактно идея понятна. Но вот как же конкретно это должно выглядеть?
Посидев, подумав, изучив что есть на рынке в этом плане, пришло понимание того что эта тема еще далеко не избита и весьма актуальна. Поэтому хочу поделиться примером «конфигуратора отчетов», который получилось реализовать на базе системы «ERP-Платформа».

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

image



Сформировались такие требования к конфигуратору отчетов:

1) Простота
Он для НЕпрограммистов. Простой менеджер, далекий от понимания SQL, сделает отчет по интересующим данным.

2) Универсальность
Отчеты можно формировать по всей системе, по любым пользовательским данным.

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

4) Вложенность
Между таблицами есть связи. Например у задач есть свойства: тип, статус и т.д. Это храниться в специальных справочниках, в основной таблице только ссылки. Т.к. система для НЕпрограммистов, не будем грузить пользователей что такое left join. Пользователь выбрав в поле статус должен получить не число, а статус задачи «в работе» и т.п. Глубина погружения должна быть как минимум на один уровень в связанные таблицы.

5) Поддержка сложных структур
Бывает что отчет сложен, затрагивает много таблиц и даже нельзя обойтись SQL запросом, а нужна серьезная обработка PL/SQL. Для этого в конфигураторе отчетов предусмотрено указание процедуры, которую напишет программист на встроенном редакторе PL/SQL ERP-Платформы.

6) Планировщик и доставка отчета
Часто отчеты формируются на периодической основе. Например пользователь хочет утром увидеть в почте отчеты, ежедневные, еженедельные. ежемесячные и т.п. Нужен планировщик заданий, в котором отчет ставится к автоматическому формированию в заданный момент.
Помимо планировщика нужна система доставки отчета. Это не обязательно письмо. В зависимости от настроек пользователя может быть уведомление в интерфейсе, отправка в telegram, отправка по email и т.п. Доставка по всем каналах коммуникации с пользователем.

7) Диаграммы
Формирование диаграмм по данным отчета

8) Вывод результата
Выводить результат отчета в различных форматы, например Excel или PDF.

Что есть в мире

Начали с изучения того что в мире есть. Изучение показало что все печально.
Сначала была 1С. Да, там все круто, можно сделать почти все что угодно, но где же тут п.1, «для простых людей»? Там черт ногу сломит пока разберется. У менеджера, который в первый раз в жизни это увидит, мозг просто расплавиться. В общем круто — но надо проще и интуитивней.
В популярных crm системах все печально. Толком ничего нет. Единственное что привлекло внимание, конфигуратор отчетов в Битриксе. В целом подходит под определение «для людей» и по началу даже понравился, но в качестве наглядного примера опишу несколько недостатков:

1) Узкость системы: отчеты привязаны только к определенным таблицам с главной функциональностью: «Задачи», «Товары»,«Сделки» и т.п. Всего 6 штук! А вот что делать если хочу проанализировать сколько % рабочего времени в графиках сотрудников занимают задачи такого-то типа, или хочу получить список клиентов у которых не заполнен email в контактах. Чтобы делать отчеты по данным из установленных приложений нет и речи.

2) Условия можно строить только на глубину до 4 вложенности. Неужели было сложно сделать неограниченное?

3) Сортировать можно только по одному из полей. А если надо отсортировать по типу задачи, а внутри типа по дате по убыванию?

4) Отчеты все списком в столбик по алфавиту. Делятся опять же по таблицам «Задачи», «Товары»,… 6 штук. А если мне нужна своя группа, или подгруппа отчетов. Как группировать отчеты по тематике?

5) Нет планировщика для запуска с периодичностью. (я по крайней мере не нашел). При этом в задачах он есть.

В общем система или универсальная но очень сложна, либо для людей — но слишком узка, либо вообще ничего нет.

Поэтому идем своим путем.

Дерево

Для начала решено было организовать построение отчетов в виде древовидной структуры.
image
Любой пользователь может создать свою ветку-узел (можно создавать вложенные ветки, тут нет ограничений), или добавлять свой отчет в другие доступные ему ветки.
При наведении мышки на отчет всплывает описание отчета.
Загромождать статью принтскринами из системы управления деревом не буду. Можно посмотреть здесь.

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

Настройка глобальной структуры

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

По сути необходимо воссоздать Foreign key (про контроль целостности тут не говорю, только связи). Структура ERP-Платформы позволила довольно легко это сделать. В штатный редактор таблиц добавлены необходимые поля:
image
В таблице ставится галочка, что она будет видна в конфигураторе отчетов.
У каждого поля ставится галочка, будет ли оно видно конфигураторе, т.к. не все поля имеет смысл выводить в отчетах, а лишний мусор в списке не к чему.

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

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

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

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

Универсальный справочник

Теперь сделаем необходимое отступление. Что такое «Универсальный справочник»? который я упоминал чуть выше, и которому выделен целый столбец при редактировании полей таблицы.
Для краткости будем называть его УС.

В первую очередь УС — способ существенно снизить трудозатраты на разработку.
Замечено, что большинство справочников состоят из 4 полей:
1) ID параметра
2) Название параметра
3) Числовое значение параметра
4) Текстовое значение параметра

Например. Справочник Валюта:
image
Или справочник Статус задачи:
image
И еще можно привести сотню примеров.

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


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

Он должен иметь такую функциональность:
1) Выбирать поля таблицы, их группировать и сортировать.
2) Составлять условия.
3) Предварительный просмотр результата.

В целом редактор получился такой
image

Поля (столбцы)

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

Вторым полем идет Универсальный справочник. Если в системе с этим полем связан конкретный справочник, то пользователь может указать Н — название, 1 — первый параметр, 2 — второй параметр. И система при формировании отчета, например в поле Статус задачи, вместо цифры 92, покажет текст «В работе» если выбрано Н и т.д. Все очень просто.

Группировка

Так же просто делается группировка. Если нужно просуммировать значение какого-то поля, выбирается SUM в поле Агрегатные. При этом система на все остальные поля сама поставит group by при формировании запроса.
image
Естественно для разных типов данных в списке должны выводятся разные функции. Например в типе данных varchar нет смысла выводить SUM.

Сортировка

Сортировка полей тоже простая и универсальная. В столбце Сортировка выбирается «1» в поле по которому произойдет сортировка, если необходимо отсортировать по еще одному полю напротив него выбирается «2», если в обратном порядке «2 desc» и т.д. Все очень просто.
image
Большое преимущество данного подхода: всегда в доступности полный пул полей и мы можем указать сортировку хоть по всем полям включительно.

Условия

Все состоит и контейнеров с условиями. Контейнер — то что в скобках. Условие — операция сравнения А и Б (А>Б, А=Б, А<=Б...).
Например: (<условие 1> И <условие 2> И <условие 3>) — является контейнером И, соответственно условие (<условие 1> ИЛИ <условие 2> ИЛИ <условие 3>) является контейнером ИЛИ. И вот из такой конфигурации И/ИЛИ и условий внутри можно составлять сколь угодно сложные вереницы условий.

Например если необходимо выбрать данные отвечающие <условие 1> и одному из условий 2 или 3. То необходимо условия 2 и 3 объединить в конейтнер ИЛИ.
(<условие 1> И (<условие 2> ИЛИ <условие 3>))
Здесь в контейнере И находятся <условие 1> и контейнер <ИЛИ>, а внутри контейнера ИЛИ условие 2 и условие 3. Например: ( (A>Б) И ( (A>C) ИЛИ (A=В) ) )
Для пользователя в интерфейсе это будет выглядеть так:
image

Но мало просто создать редактор расстановки условий. Условие может быть константой или переменной. Переменное условие определяется просто — оно не заполнено в конфигураторе. Если значение условия пусто — то надо спросить его у пользователя перед формированием отчета. Если пользователь ничего не введет — ставить null.
Чтобы поле ввода выглядело информативно нужно еще такое свойство как название. Название может совпадать с именем поля, а может и нет.
Например, если мы ввели константу в поле «Дата», но оставили пустым поле «Статус»,
image
то система должна спросить «Статус» у пользователя перед формированием отчета.
image

В общем: значение есть — константа, пусто — спрашивать. Все тут тоже просто.

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

Необходимости как в Битриксе ограничивать 4-мя уровнями вложенности не было. Условия можно ветвить бесконечно.

Процедуры

Конфигуратор отчетов создан для НЕпрограммистов и знание SQL для составления отчета не обязательно. Но в случае сложного отчета, данные которого находятся во многих разных таблицах со сложными связями, программиста придется привлечь.
Такие вещи можно сделать написав процедуру в стандартном PL/SQL конфигураторе, входящем в базовую систему программирования ERP-Платформы, и указав данную процедуру в отчете. В процедуре можно сделать любую PL/SQL конфигурацию, т.е. фактически все что угодно.

В тему программирования конфигурации в ERP-Платформе углубляться не буду, т.к. эта тема выходит далеко за рамки данной статьи. Вкратце — состоит из 2 частей: программирование веб интерфейса и программирование базы данных, ну и соответственно связи между ними. В части программирования базы данных можно создавать таблицы, триггеры, процедуры любой сложности. В рамках конфигуратора отчетов нас здесь как раз интересует часть системы по созданию процедур. Там реализован полноценный PL/SQL (и даже несколько фишек которых нет нигде, например тип данных image, или операторы API в триггерах, позволяющие прямо по событию в БД передавать данные во внешние системы)

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

После того как процедура стала доступна пользователю он её может указать в отчете.
Добавлять поля можно из полей процедуры. Агрегатные функции и функции сортировки доступны.
А вот условия здесь — входные параметры процедуры. Они нужны все и порядок нельзя менять. Поэтому тут редактирование ограничено. Можно только задать константу. Но менять порядок, удалить, добавить новые — не получится.
Пример использования процедуры в отчете в ERP-Платформе
Под спойлером приведу пример, если кому-то будет интересно как выглядит использование процедур в отчете в нашем редакторе.
Вот например процедура «ЗАЯВКА_Список», формирующая список заявок по условиям в интерфейсе списка заявок:
image

Если не заполнено ни одно из условий, т.е. все null процедура выдаст список заявок со статусом «поступила».
Если ее указать в отчете, и выбрать аналогичные поля, как в интерфейсе со списком заяовк

image

То мы получим аналогичный результат в отчете

image

Вот в догонку еще код этой процедуры в PL/SQL интерфейсе ERP-Платформы. Но это уже очень глубоко выходит за рамки данной статьи и не предназначено для простых пользователей. Тут просто в качестве примера, что такие вещи можно делать.
Вообще вся конфигурация ERP-Платформы написана на её же встроенном языке, соответственно все это доступно в конфигураторе отчетов, а так же сами пользователи могут что угодно редактировать и модернизировать.
Процедура ЗАЯВКА_Список
image



Запуск по расписанию

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

В ERP-Платформе есть штатный планировщик заданий, для запуска процедур по расписанию, программируемый в целом аналогично крону. В него получилось встроить и постановку запуска отчетов. Т.е. опять же заюзать штатную функцию системы и не писать новое. Но оказалось не все так просто.
image
С планировщиком самим по себе просто. Ставится отчет аналогично процедуре, только дергается другой скрипт, который дергает отчет и получает из него данные. Но в процессе работы пришло осознание того, что просто слать отчет в html встроенным в письмо совершенно не правильно и пришлось капнуть гораздо глубже.

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

Решение было найдено следующее. Скрипт, который запускается по расписанию и получает данные отчета, делает pdf файл с отчетом и кладет его на диск в аккаунте клиента. Потом через штатную систему уведомлений производит рассылку со ссылкой на этот файл. Т.е. грубо говоря пришло уведомление, отчет такой-то, и ссылка. Пользователь кликает на ссылку и ему открывается файл.
image
Pdf формат универсальный, откроется везде и на телефоне и на компьютере.

Ок, средство доставки есть. Теперь надо сформировать адресатов.

Настройка получателей

Получателей может быть 3 вида:
1) Сотрудник (конкретное лицо)
2) Подразделение (отдел согласно штатному расписанию)
3) Группа (рабочие группы сотрудников)

Соответственно могут быть любые сочетания этих видов. Заполняется эта информация в конфигураторе отчетов в самом отчете.
image
Скрипт формирования отчета на основании этих данных вычислит набор уникальных получателей и проштампует им ссылки на отчет в штатную систему уведомлений.

Далее рассылка уже будет зависеть от индивидуальных настроек пользователей. Минимум — придет в веб интерфейс. Далее если стоит в контактах сотрудника email с типом «для уведомлений» — уйдет туда. Если у сотрудника подключен телеграм, то уйдет и туда.

Но опять же, не факт что отчет должны видеть все, а он лежит на диске. Тут проблема решается просто. Каждая папка на диске имеет систему ограничения прав доступа. Права к папке Отчеты по умолчанию урезаны совсем, даже не чтение. Т.е. ее содержимого никто не сможет посмотреть, но ссылки при этом будут открываться. Если кому-то вдруг понадобится просмотр содержимого этой папки, администратор должен Роли пользователя индивидуально прописать права на нее. Так что проблем с безопасностью тут нет. Работает принцип как в youtube «доступ по ссылке». В списке ничего не увидишь, но посмотреть видео можно зная ссылку.

Графическая часть

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

В нашем конфигураторе на текущий момент предусмотрели 3 виде диаграмм:
1) Линейная
2) Столбчатая
3) Круговая
4)… и в будущем добавлять другие виды по мере фактической потребности.

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

Тут в принципе все понятно и углубляться не будем. Подробнее можно посмотреть здесь.

Форматы вывода данных

Это тоже важный аспект. Как минимум пользователь должен иметь возможность распечатать отчет одним щелчком. Как максимум получить его в виде файла PDF или выгрузить в Excel для дальнейшей обработки.
Все эти варианты конечно предусмотрены и тут в принципе тоже все понятно и ничего нового.

Послесловие

Хотелось бы подвести итоговую мысль под тем, каким должен быть идеальный конфигуратор отчетов.

1) Он не должен гемороить пользователей. Но простота и функциональность вещи противоречивые. У функциональной вещи сложнее эксплуатация. Может даже потребоваться специальное обучение.
Тут можно пойти путем матрешки. На верхнем слое сделать простой, дружелюбный интерфейс, закрывающий большой пул потребностей. При встрече сложной потребности, должна быть дверь во внутренние слои с функционалом, необходимым для её решения.
Так и строили. Внешне — простой интерфейс, но если встретилось сложное: вот редактор PL/SQL — делайте что угодно и подключайте это в виде готового блока в простой интерфейс отчета.

2) Конфигуратор отчетов не должен быть статичным, а расширяться автоматически вместе с расширением системы. Надо делать так, чтобы при расширении системы код самого конфигуратора отчетов не надо было преписывать, а он автоматически подхватывал все изменения.
В системах с возможностью конфигурирования пользователем, например в 1С или в ERP-Плаформе, такой подход является единственным выходм, т.к. какая конфигурация будет у пользователя заранее не известно, и за всеми не угонишься подгонять конфигуратор отчетов под каждую конфигурацию системы.

3) Конфигуратор отчетов должен хорошо выполнять задачу по составлению отчетов, а не заниматься смежными вещами.

Тут концепция аналогична принципу маленьких «острых» утилит в unix подобных системах. Утилита должна уметь блестяще справляться со своей задачей, а не уметь все по чуть чуть. Смежные задачи передавать утилитам блестяще их выполняющим и просто получать от них результат.

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

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

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

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

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

Такой подход в системе позволяет хорошо отладить эти узконаправленные функции в модулях, а так же не тратить лишнее время разработчиков на реализацию частично дублированных функций в различных частях системы.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/331884/


Метки:  

[Перевод] Покажите мне бизнес-проблему, и я постараюсь её избежать

Среда, 12 Июля 2017 г. 23:02 + в цитатник
Автор — Джейсон Фрид, основатель и исполнительный директор Basecamp.



«Один из самых лучших способов избежать неприятностей — упростить их. Когда вы очень усложняете проблему — и только несколько первосвященников в каждом отделе могут притвориться, что понимают её — чаще всего вы обнаружите, что эти первосвященники вообще ничего не понимают… Такая система часто выходит из-под контроля», — Чарльз Мангер

Многие бизнес-проблемы возникают самопроизвольно. Многие раны наносятся самому себе. В конкуренции можно выиграть, но чаще всего конкурент проигрывает себе же.

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

Мы построили наш бизнес на том, чтобы избегать проблем. Это фундаментальная причина, по которой мы способны делать то, что делаем, и продолжаем заниматься этим — с прибылью, и с маленькой командой — на протяжении 18 лет. Всякий раз при принятии важных решений мы оцениваем их последствия. В целом, вопрос стоит так: насколько всё будет плохо, если мы сегодня сделаем это?

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

Вот некоторые примеры, чего мы избегаем:

Роста


Мы специально сохраняем Basecamp небольшим. Обслуживая более 100 000 платных клиентов и несколько миллионов пользователей со штатом чуть более 50 человек. Маленькие фирмы избегают проблем, которые есть у больших компаний. Меньше менеджеров, меньше слоёв управления, меньше потерь при передаче информации, значительно меньше узких мест и формальных процессов, из-за которых людям приходится ожидать разрешения, чтобы приступить к чему-нибудь. В компаниях меньшего размера всё проще и каждый сотрудник ближе к потребителям. Конечно, некоторые маленькие компании не способны на то, что могут сделать большие, но с нашей точки зрения это очень хорошо.

Больших коллективов


Мы специально удерживаем численность штата, в том числе формируем команды намеренно малого размера. Почти каждый проект в Basecamp делает команда из трёх человек или меньше (два программиста, один дизайнер), а существенное количество групп состоит из одного человека (программист или дизайнер). Можем ли мы решать более существенные задачи с большими командами? Может быть — но при этом крупные команды создадут и более крупные проблемы. Орвчинка не стоит выделки. Нам нравится делать много маленьких проектов меньшими группами. Мы по-прежнему добиваемся всех поставленных целей — просто более мелкими шажками. Это облегчает корректировку курса по пути. И, честно говоря, это в любом случае лучший способ работы.

Долговременных целей


Сложно найти что-то более деморализующее, чем долгосрочный проект, которому не видно конца. Хотя время от времени случаются инфраструктурные проекты без конечной даты, но почти всё, что мы делаем, укладывается максимум в шестинедельные циклы (я подробно писал об этом раньше). Многие проекты намеренно сокращены, чтобы уложиться в несколько дней или пару недель. Так что даже если мы выделили 6 недель на какое-то дело и недовольны результатом — мы потеряли всего лишь 6 недель. Мы избегаем всех осложнений, которые появляются при попытках всеми силами дожать сомнительные проекты. Сравните это с проектами, которые описываются фразами «слишком крупный, чтобы провалиться», «будет завершён, когда сделаем», и они продолжаются 6, 8, 12 и более месяцев… Вряд ли вы откажетесь от результатов работы, когда её закончат. Длительные проекты — могилы боевого духа.

Планов и обещаний


Аналогично предыдущему пункту, мы не заглядываем в будущее дальше, чем на 6 недель. У нас есть некоторые идеи общего характера, куда мы хотим развиваться, но эти идеи мы держим в голове и редко записываем. Это неофициальная устная традиция. Также мы редко даём обещания насчёт будущего. Такие обещания — богатый источник головной боли и конфликтов. Легко дать согласие на нечто в будущем, потому что это не требует усилий прямо сейчас. Но когда наступает время, вы вряд ли захотите делать то, что обещали давным-давно. Прошлые обещания — источник большого количества проблем в бизнесе. Мы бежим от таких обещаний как от чумы. На эту тему я тоже раньше писал.

Масштабирования


Масштабирование! Масштабирование! Все хотят масштабироваться А МЫ НЕ ХОТИМ! Мы стараемся избегаем проектов, обязательным условием которых является масштабирование. Мы ищем проекты, которые будут успешными в любом масштабе — малом или большом. Для большинства компаний масштабирование — синоним того, что сейчас концы с концами не сходятся, но со временем они сойдутся. Не бегите впереди паровоза — растите разумными темпами. Выходите в плюс как можно раньше с минимальным количеством клиентов, а не с воображаемым множеством.

Грозных дедлайнов


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

Недопонимания


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

Партнёрства


Большие компании постоянно предлагают нам сотрудничество. Они хотят стать нашими партнёрами. «Нам интересно, хотите ли вы заключить партнёрские отношения по какой-нибудь теме»… Большой красный флаг. Раньше мы поддавались на такие предложения и они всегда заходили в тупик. Большая потеря времени. Это особенно справедливо для несбалансированных ситуаций, когда огромная компания хочет сотрудничать с маленькой. Учитывая все обстоятельства, обычно всё сводится к гигантскому объёму работы для вас (маленькой компании) и крошечному объёму для одного из их сотрудников, которому не нужно самому ничего исполнять. Уходите от этого!

Узких мест


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

Может и странная метафора, но я всё равно скажу… Это как мыть посуду после еды. Если ты кушаешь и сразу после еды моешь посуду, то никогда ничего не нужно мыть потом. Вы избегаете трудностей и дополнительной работы в будущем. Если же сложить грязные тарелки в кучу, то в реальности вы создаёте больше работы для самого себя — или для кого-то другого. Остатки еды засыхают, пригорают, их труднее отчистить. Вы увеличиваете уровень сложности работы. В дальнейшем груда посуды начинает пугать — зачастую она растёт просто потому что каждый боится за неё взяться. Что такое ещё одна грязная тарелка? Просто брошу её туда. Но если вы моете посуду после еды, то всё происходит гораздо быстрее, это как часть процесса приёма пищи (а не отдельная процедура), и вы никогда не будете возвращаться к этой работе — она уже сделана. Будущее свободное и чистое.

Не оставляйте грязную посуду на работе.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/332952/


Вы используете интерфейсы в Go неправильно!.

Среда, 12 Июля 2017 г. 19:48 + в цитатник
С таким громким заголовком я думал сначала написать статью. Нет, на самом деле, вполне возможно, что у вас всё хорошо и эта статья — не про вас. Но очень часто, когда люди приходят из других языков, можно видеть, как они пытаются «притянуть за уши» паттерны из того языка, к которому они привыкли, и они в Go зачастую работают плохо.



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



Разбиение на пакеты


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

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

Как ей это удается? В чём отличие стандартной библиотеки от вашего проекта? Если присмотреться, то можно выделить буквально пару основных пунктов, где в go вещи отличаются от других языков:

1. Пакеты могут быть большими
В пакете «net/http», например, находится больше 40 файлов, и определено больше 20 разных публичных типов. Они все относятся к HTTP и было было бы нелогично разбивать их на несколько пакетов. Типы вроде http.Header, http.Client, http.Server все выглядят логично и нет необходимости в том, чтобы пытаться, к примеру, отделить реализацию клиента от реализации сервера просто ради получения более мелких модулей.

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

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

Использование интерфейсов


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

Пример первого подхода (интерфейс определяется в приемнике)

Вы все знаете пакет fmt. И также наверняка писали что-то вроде следующего:

// файл habr.go
package habr

type Article struct { title string }
func (a *Article) String() string { return a.title }

// файл main.go
package main
import (
	"fmt"
	"habr"
)

func main() {
	a := &habr.Article{title: "Вы используете интерфейсы в Go неправильно!"}
	fmt.Printf("The article: %s\n", a)
}


Обратите внимание на метод String(). В пакете fmt объявлен интерфейс fmt.Stringer, и в этом же пакете принимается реализация этого интерфейса (пусть и в данном случае неявно).

Пакет habr же, в свою очередь, от пакета fmt вообще не зависит и пакет fmt может свободно его импортировать, если пожелает. Это позволяет «мягко» создавать циклические зависимости, без необходимости рефакторить код и перестраивать всю структуру пакетов.

Более подробный пример (и обоснование) можно увидеть по следующим ссылкам (на английском):


Пример второго подхода (выделение интерфейсов в отдельный пакет)

Если интерфейс (или какой-то тип) нужен больше, чем в одном месте и он имеет ценность сам по себе, то его нужно выделить в отдельный пакет. Так появился пакет io — в нём собраны наиболее полезные интерфейсы, константы и переменные, которые так или иначе относятся к вводу-выводу. Чтобы не вносить дополнительных зависимостей при импорте этого пакета, есть отдельный пакет, где собраны удобные функции для работы с интерфейсами из io — пакет ioutil.

Интерфейсы из пакета io получились настолько удачными, что, насколько мне известно, Go — это единственный язык, в котором стандартная библиотека «из коробки» умеет печатать одновременно в файлы, сокеты, HTTP-ответы, байтовые буферы и т.д., причём эта функциональность досталась стандартной библиотеке почти «бесплатно», благодаря хорошо продуманным абстракциям.

Общие рекомендации


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

Ссылки


Скорее всего вы уже читали Effective Go, но если нет, то очень рекомендую :). Также есть две замечательные статьи, в которых описаны «хорошие практики» при программировании на go:

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

https://habrahabr.ru/post/332948/


Метки:  

Tcl/Tk. Разработка графического пользовательского интерфейса для утилит командной строки

Среда, 12 Июля 2017 г. 19:08 + в цитатник
Тестируя облачный токен на различных платформах, меня не покидала мысль о некой несправедливости: почему утилиты конфигурирования токенов PKCS#11 на платформе MS Windows имеют графический интерфейс, а для других платформ его нет. И в первую очередь это касается базовой утилиты p11conf, которая доступна для свободного использования, и является утилитой командной строки, взаимодействие с которой осуществляется через стандартный ввод/вывод.

tkBuilder – графический конструктор


Выходом могло бы быть написание на языке C/C++ GUI для утилиты p11conf, например, с использование библиотеки Qt. Но тут в памяти всплыло, что есть скриптовый язык высокого яровня Tcl (Tool Command Language), который в связке с графической библиотекой Tk (Tool Kit), и позволяет быстро создавать графические интерфейсы для консольных программ или утилит командной строки. Впервые я познакомился с пакетом Tk/Tcl в далеком 1997 году, когда планировалось на нем написать графический интерфейс для системы контроля доступа (СКД). Тогда графические возможности пакета произвели сильное впечатление и, в частности, tetris:
image

Одной из причин, по которой отказались, от использования Tk/Tcl, было отсутствие для него в то время конструктора (дизайнера) аналогичного сегодняшнему, скажем, QT-designer. Освежив в памяти возможности Tk/Tcl, а нас интересовало, помимо графических возможностей, организация взаимодействия с утилитами командной строки, и убедившись, что мы стоим на правильном пути, мы принялись за поиск конструктора. После проведенного анализа имеющихся конструкторов, выбор пал на дизайнер tkBuilder_for_tcl8.4:

image

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

image

но также контролировать его отдельные части:

image

Разработчик всегда может посмотреть и код всего проекта или его части:

image

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

image

Когда проект завершен, то необходимо его сохранить в итоговом tcl файле:

image

О ложках дегтя


Казалось бы все, бери и запускай утилиту. Но без ложки (на самом деле двух ложек) дегтя нельзя. Первая ложка связана с формированием у виджетов (в нашем случае у виджета text) команд xScrollCommand и yScrollComand., а именно:

text .fr1.fr2_list -background #ffffff \
   -yscrollcommand [list .fr1.ysc set] -xscrollcommand [list .fr1.scx set]

Не хотят они попадать в итоговый файл. В итоге их пришлось прописывать ручками. Ну и вторая ложка дегтя классическая. Дизайнер tkBuilder_for_tcl8.4 отказывается работать с русскими буквами (можеть я чего не доглядел). Подчеркиваю это касается только дизайнера и не касается Tk/Tcl, для которого русский язык как дом родной. Здесь тоже пришлось поработать ручками:
#!/bin/sh
# the next line restarts using wish \
exec wish8.5 "$0" ${1+"$@"}
wm title . "GUI P11CONF"
global p11conf
global libpkcs11
set res ""
set libpkcs11 ""
set p11conf "/usr/local/bin64/p11conf"
. configure  -background #18f1d7
frame .fr1 -background #18f1d7
grid .fr1 -column 0 -row 0
button .fr1.b1 -command  {InitTok . .fr1.fr2_list;} -padx 1 -text "Инициализировать" -width 24
grid .fr1.b1 -column 0 -row 0
button .fr1.b2 -command {ChangeUserPin . .fr1.fr2_list; } -padx 1 -text "Сменить USER PIN" -width 24
grid .fr1.b2 -column 0 -row 1
button .fr1.b6_3 -command {ChangeSOPin . .fr1.fr2_list "SO"; } -padx 1 -text "Сменить SO PIN" -width 24
grid .fr1.b6_3 -column 0 -row 2
button .fr1.b7 -command {InfoObj . .fr1.fr2_list "Obj"; } -padx 1 -text "Просмотреть объекты" -width 24
grid .fr1.b7 -column 0 -row 3
button .fr1.b8 -command {InfoObj . .fr1.fr2_list "Clear";} -padx 1 -text "Удалить все объекты" -width 24
grid .fr1.b8 -column 0 -row 4
button .fr1.b9_6 -command {InfoToken . .fr1.fr2_list; } -padx 1 -text "Информация о токене" -width 24
grid .fr1.b9_6 -column 0 -row 5
button .fr1.b0 -command  {InfoMech . .fr1.fr2_list;} -padx 1 -text "Поддерживаемые механизмы" -width 24
grid .fr1.b0 -column 0 -row 6
button .fr1.b3_8 -command {ChangeSOPin . .fr1.fr2_list "Deblock"; } -padx 1 -text "Разблоктровать USER PIN" -width 24
grid .fr1.b3_8 -column 0 -row 7
button .fr1.b4_9 -command {InfoDump . .fr1.fr2_list; } -padx 1 -text "DUMP всех объектов" -width 24
grid .fr1.b4_9 -column 0 -row 8
button .fr1.b5_10 -command {exit} -text "Выход" -width 10

В итоге мы получили поддержку графического интерфейса для утилиты командной строки p11conf через утилиту GUITKP11Conf.tcl:
bash-4.3$ ./GUITKP11Conf.tcl

image

Теперь смело можно работать с токенами PKCS#11, в том числе и с облачным токеном LIBLS11CLOUD, например, просмотреть какие объекты хранятся на нем:

image

Файл проекта GUIP11CONF.tkb и утилиту GUIP11CONF.tcl скачать можно здесь. Утилиту p11conf для различных платформ можно скачать здесь. Маленькое замечание. В утилите GUIP11CONF.tcl прописан путь в утилите p11conf (см. выше):
global p11conf
…
set p11conf "/usr/local/bin64/p11conf"


Скорее всего вам придется поправить путь к утилите p11conf с учетом вашей конфигурации.

Лиха беда начало


В заключение отмечу, что аппетит приходит во время еды, и уже есть желание написать аналогичные графические оболочки для таких утилит командной строки как openssl/lirssl (ССЫЛКА ) или утилит NSS (Network Security Services). Как говорится, лиха беда начало.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/332924/


Метки:  

Опыт внедрения сервиса мобильных платежей Apple Pay в банке

Среда, 12 Июля 2017 г. 18:53 + в цитатник

Метки:  

Заметки маркетолога: Как поставить Ubuntu на RPI и подключить к Azure IoT Hub

Среда, 12 Июля 2017 г. 18:47 + в цитатник
Что делает маркетолог, когда его просят рассказать как использовать технологию, а он не может найти инструкцию? Обычно начинает дёргать всех окружающих технарей, искать в интернете или молча ждать чуда, или плакать. Но, эта история про другого маркетолога. Он просто взял и написал инструкцию сам. Приглашаю под кат оценить труды и поделиться своим мнением.



Передаю слово Андрею (@andvis).

Disclaimer:
Коллеги, не претендую на истину в первой инстанции – просто делюсь своим опытом. Возможно он сэкономит кому-то немного сил и времени. И да, в момент написания статьи я первый раз в жизни видел командную строку Linux.

Давайте договоримся, что ниже речь пойдёт про Raspberry Pi 2 Model B. Именно работая с ним были сделаны все снимки экрана.



Дистрибутив Ubuntu Mate можно скачать, например, здесь.

Имея img файл — создаём загрузочную SDCard – для этого я использовал Win32 Disk Imager, который установил на свою Windows 10.



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




Сразу после установки выполняем несколько команд.

sudo update-rc.d ssh enable


sudo service ssh restart


sudo ifconfig – покажет нам сетевые настройки устройства


Важные команды update и upgrade – чтобы обновить систему.

sudo apt-get update


sudo apt-get upgrade


Далее добавим русский язык для клавиатуры.


Вот так можно поменять комбинацию клавиш переключения (я пару минут потратил, пока нашёл этот пункт).



На одном из форумов прочитал, что надо включить I2C: командой sudo raspi-config.

Тут кстати и настройки SSH можно поменять.





Пока нет под рукой датчиков — пробую сделать хоть что-то… Читаю вот эту статью.

Давайте начнём с того, что нам понадобится подписка Azure! Пробную верcию можно получить здесь.

Наши дальнейшие шаги.

Шаг 1.


Шаг 2.


Шаг 3.


Шаг 4.


Итак, вернёмся на устройство! Команда node -v возвращает ошибку на моём RPI.



Устанавливаем Node.js. Для этого вбиваем команду: sudo apt install nodejs-legacy.



Теперь команда node -v возвращает текущую версию.



На всякий случай.



Теперь нужен NPM — устанавливаем командой: sudo apt-get install npm. Команда не быстро, но отработала!



Далее запускаем: sudo npm install -g npm@2.x.



Теперь надо поставить последнюю версию IoT Hub transport package: sudo npm install -g azure-iot-device@latest.



И ещё одна команда: sudo npm install -g azure-iot-device-http@latest.



Теперь надо установить IoT Explorer: sudo npm install -g iothub-explorer@latest.



Следующим шагом запускаем вот такую команду iothub-explorer login "строка подключения".



Далее подключаем устройство: iothub-explorer create muzeyrpi-1 --connection-string. Успешно!



На портале Azure устройство видно.



Копируем на устройство скрипт JS со странички:
var connectionString = '';
var clientFromConnectionString = require('/usr/lib/node_modules/azure-iot-device-http').clientFromConnectionString;
var client = clientFromConnectionString(connectionString);
var Message = require('/usr/lib/node_modules/azure-iot-device').Message;
var msg = new Message('some data from my device');
var connectCallback = function (err) {
  if (err) {
    console.error('Could not connect: ' + err);
  } else {
    console.log('Client connected');
    var message = new Message('some data from my device');
    client.sendEvent(message, function (err) {
      if (err) console.log(err.toString());
    });
    client.on('message', function (msg) {
      console.log(msg);
      client.complete(msg, function () {
        console.log('completed');
      });
    });
  }
};
client.open(connectCallback);

Сам по себе скрипт не заведётся — надо:
  1. В путях внутри файла надо поменять /usr/lib на /usr/local/lib.
  2. Добавить строку подключения. Причём строку подключения именно к устройству.



Запускается скрипт командой node MyScript.js из папки, где лежит скрипт.



Пробуем ещё раз – на этот раз в Device Explorer (подробнее про него и ссылка на скачивание в статье.



Жмём Enter… :)



Поздравляю – первые сообщения от нашего PRI достигли Azure IoT Hub

Я искренне надеюсь доработать данную инструкцию и сделать её еще более полезной! Например, пройдя вот этот путь (имея на руках датчик BME280 или без оного).

Для тех, кто дочитал


Для того, чтобы вы могли попробовать проделать это упражнение сами, мы с Андреем решили разыграть две штуки Raspberry Pi 3. Их получат первые 2 человека, которые ответят правильно в сообщения сообщества Microsoft Developer на вопрос: «На основе какого микроконтроллера была разработана самая ранняя версия Raspberry Pi?» Общие правила.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/332940/


Метки:  

Пентест-лаборатория «Pentestit Test.Lab 11» — полное прохождение

Среда, 12 Июля 2017 г. 18:42 + в цитатник


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

Эта лаборатория уже 11-я по счету. Описания десятой, девятой, восьмой, седьмой и шестой лабораторий тоже доступны, и могут пригодиться в подготовке к участию, если вы этого еще не сделали.

Как и в прошлый раз, спустя 11 суток лаборатория была пройдена первыми участниками, собравшими 12 токенов, которые подтверждают полное решение очередной задачи, будь то получение доступа в БД, проникновение и повышение привилегий на Linux-сервере или успешное выполнение MiTM-атаки на ноутбук директора виртуальной компании Test.Lab.

Эта статья описывает все этапы прохождения пентест-лаборатории Test.Lab 11 для всех, кому интересно погрузиться в область пентеста или узнать, как решалась конкретная задача.

Disclaimer
Я не являюсь сотрудником или аффилированным лицом компании Pentestit. Этот документ описывает шаги, которые я предпринял, чтобы решить задания в лаборатории. Мои личные рекомендации и предпочтения никаким образом не относятся к официальному мнению компании Pentestit.

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

Подключение к лаборатории


Прежде чем начать, нужно зарегистрироваться в лаборатории, настроить VPN-соединение и подключиться к сети виртуальной компании Test.Lab.

Зарегистрируйтесь здесь, и затем следуйте этим инструкциям для того, чтобы подключиться.

Для проведения тестирования можно установить в виртуальной машине Kali Linux — специальный дистрибутив Линукса для пентестеров, в котором есть все необходимое для работы. Если вы этого еще не сделали, теперь самое время.

Начинаем тестирование


После подключения, нам становятся доступны два шлюза — 192.168.101.10 и 192.168.101.11, за которыми скрывается следующая сеть:


Собираем информацию


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



Из результатов понятно, что нам доступны два сайта (основной сайт компании и CRM, как выяснится позже), почтовые сервисы (доступ к веб почте и SMTP) и SSH на нестандартном порту 2222. Если просканировать все порты, используя ключ -p-, так же можно найти OpenVPN на порту 1194 хоста 192.168.101.10.

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

В начале пентеста нужно постараться определить максимально реалистичные цели для предстоящей атаки. Так, сразу можно отсечь SSH сервер, который требует аутентификацию по ключу и OpenVPN сервер, ожидающий конфигурационный файл для подключения (хотя для OpenVPN варианты возможны, в случае отсутствия других уязвимостей). Кроме того, на почтовые сервера можно попробовать подобрать пароли к учетным записям сотрудников, но для этого нужно знать имена этих записей или имейлы сотрудников.

После беглого анализа сайта компании, доступного на порту 80, становится понятно, что он защищен с помощью web application firewall, так как большинство «агрессивных» запросов зарабатывают нашему IP бан на фаерволле на 2 минуты. Перебор директорий, или активное сканирование с помощью утилит вроде Burp Suite приводит к постоянным ошибкам 403.



Сконцентрируем внимание на CRM системе Vtiger, которая, на первый взгляд, и является тем самым low-hanging fruit, который мы ищем.



Система Vtiger на первый взгляд не защищена WAF-ом, и при этом версия 6.3.0 содержит в себе уязвимость, предоставляющую нам возможность получить шелл на этом сервере:



Но, к сожалению, не все так быстро — как видим, для начала необходимо авторизоваться в CRM, так как уязвимость — authenticated RCE. Изучим ее повнимательнее!

Изучаем CRM


Не найдя ничего интересного в подкаталогах, и установив себе копию Vtiger 6.3.0 (которую можно скачать здесь) понимаем, что имя пользователя по умолчанию — admin. Попробуем воспользоваться Burp Suite чтобы подобрать пароль.

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

Настроив Burp Suite на перебор пароля по большому словарю из SecLists (100 тыс паролей), с пользователем admin, получаем нужный пароль:


И, наконец, заходим в систему:


Судя по паролю и нику администратора в CRM (обратите внимание на него в правом верхнем углу — еще пригодится при взятии почты), становится понятно что администратор — серьезный фанат Звездных Войн.

Используем вышеупомянутую уязвимость для загрузки шелла после аутентификации. Для этого переходим на CRM Settings > Templates > Company Details, и редактируем логотип, меняя его на однострочный шелл, предварительно включив перехват трафика в Burp Suite:


Перехватив запрос в Burp, возвращаем расширение php загружаемому файлу (альтернативой было бы сразу загружать PHP файл, но при этом поменять mime type загружаемого объекта на image/jpeg в Burp Suite). Отдельно нужно отметить, что необходимо убрать вхождения строки «php» в загружаемом шелле, иначе Vtiger CRM отфильтрует такое «изображение» (нам достаточно символов

https://habrahabr.ru/post/332902/


[Из песочницы] Настройка архивирования объектов в SAP ERP для начинающих

Среда, 12 Июля 2017 г. 17:54 + в цитатник
Приветствую всех. Многие знают о необходимости архивирования (резервирования) информационных систем независимо от уровня системы. Для ERP систем это обычно многоуровневое резервирование на уровне операционной системы, на уровне системы управления базами данных, на уровне самой ERP системы.

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

Настроим архивирование на примере документов изменений HR данных. Считаем, что сами документы изменений настроены и результат изменения инфотипа можно просмотреть через транзакцию S_AHR_61016380.

Вся настройка осуществляется в транзакции SARA, где мы должны выполнить ряд вещей:

  1. Настроить пути архивирование, логические и физические файлы
  2. Настроить варианты для программ запуска архивирования
  3. Запланировать сами программы архивирования

Для настройки архивирования документов изменений возьмем стандартный объект PA_LDOC «PA: Long Term Documents». Открываем транзакцию SARA.

image

По кнопке Customizing откроется окно, где мы настраиваем систему архивирования.

Выбираем Technical Settings. Здесь к конкретному объекту архивирования присваивается логический путь для хранения архива. В нашем случае это ARCHIVE_DATA_FILE. Обратите внимание, что для этого объекта стоит галочка «Delete Jobs — Automatically». Система сама удалит архивированные записи из базы данных. Для некоторых объектов это не требуется, поэтому для них предусмотрен отдельный шаг по удалению записей из БД.

image

Теперь нужно настроить путь для хранения архива. В том же меню Customizing выбираем чуть ниже «Cross-Client File Names/Paths».

Сначала определим имя файла. Здесь для каждой операционной системы можно выбрать свой путь. По кнопочке F1 можно увидеть подсказки с макроподстановками.

image

Теперь нужно задать правила формирования имени файла.

image

Сохраняем и выходим в основное меню SARA. Теперь запускаем программу для архивирования. Кнопочка «Write».

image

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

После определения варианта планируем когда нужно запускать архивирование по кнопке «Start Date». Не забываем чуть ниже определить параметры спула (кому отправлять результаты выполнения программы архивирования). Все готово для запуска — поджигаем.

Система запланирует фоновое задание согласно настройке. Я для примера запустил сразу же без ожидания. На сервере образовался архив (у меня два архива, так как я уже запускал архивирование).

image

Для просмотра архивов есть кнопка 'Management'. Для просмотра, что внутри — 'Read'.

image

Важно поставить галочку чтения из архива.

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

image

И вот результат.

image

Всем спасибо.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/332934/


Метки:  

20 материалов о методах успешного привлечения трафика на сайт

Среда, 12 Июля 2017 г. 17:44 + в цитатник


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

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

Классические методы по увеличению трафика


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


О нестандартных методах


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


Методом проб и ошибок


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

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

https://habrahabr.ru/post/332914/


О выборе своего «куска» рынка для студий / агентств — глава из книги «33 точки роста»

Среда, 12 Июля 2017 г. 17:40 + в цитатник

Немножко цифр. Статистика по РФ (2017):


· +12 тысяч исполнителей.


· +5 млн. клиентских бизнесов, включая «микро» и «ремесленников».


· +40% из них планируют наращивать маркетинговые инвестиции.


Выглядит аппетитно, да?


А где среди этого пространства вы? Амбиции съесть весь «пирог», заявляя, что работаете с каждым? Так дело не пойдет. Вы не сможете равно соответствовать каждому запросу. Да и не будет всех потенциальных запросов, так как клиент становится все умнее: бизнес уже понимает, что перспективные ребята уходят в свою нишу. Многорукие «оркестры» — пережиток прошлого.


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


Расскажу то, о чем упоминают лишь вскользь: как именно найти свое место на digital-рынке, какие есть подводные камни на этом пути, и как выбрать себе «камень» по душе.


Во-первых, позиционирование позиционированию — рознь. Оно всегда складывается по-разному:


1. «Естественным» образом. Это когда есть либо какой-то бэкграунд (основатель пришел из соответствующего бизнеса, есть «корни» / знакомые и пр.) либо просто «так сложилось»: в самом начале было пару хороших проектов, на которых удалось набить руку. Данная история имеет право на существование, но она должна быть осознанной. Принцип «делайте, что нравится» — скорее для ремесленников. Бизнес строится по-другому. Да и «что попросили, то и начали делать» — тоже не айс.


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


3. Осознанное позиционирование. Не страшно, что раньше работали «как попало». Главное, что сейчас вы выбираете путь с холодной головой. Правильный ход событий:


  • Задумались.
  • Промониторили рынок.
  • Выявили интересный «кусок».
  • Выявили его плюсы и минусы.
  • Прикинули.
  • Оценили объемы в цифрах.
  • Примерили на себе шкуру целевого «куска»: чем они живут, дышат, какие есть проблемы.
  • Пожили в этой шкуре: стали думать их категориями, говорить их терминами. Если вы из категории продакшна — нужно как минимум поверхностно. Агентствам — обязательно до уровня, когда вас на конференциях будут считать «своими».
  • Прокачались:
  • . Стартовали.
  • · Адаптировали свой продукт
  • · Адаптировали процессы и регламенты.
  • · Адаптировали маркетинг.
  • Начали много и усиленно пахать, ежеквартально делая контрольные замеры с подведением итогов и корректировкой.

Вот как оно бывает по науке.


По каким горизонталям / вертикалям можно позиционироваться?


Чтобы было проще понять, разрежем наш торт вот так:


image


1. ЧТО = ваш продукт:


a. Модель бизнеса:


i. Продакшн — сильные технические навыки. Все (или почти все) своими штатными силами.


ii. Агентство — маркетинг + коммуникации + адаптация под клиента. Штатного продакшна можно вообще не иметь, для этого есть субподрядчики.


b. Услуги. Прежде чем делать громкие заявления о «комплексности», подумайте, насколько вы соответствуете данной претензии. Да и подобный подход сегодня не имеет большой значимости: во-первых, об этом кричит каждый второй; а во-вторых, многие бизнесы хотят уменьшать риски, раскладывая яйца по разным корзинам. Да и вряд ли вы вообще осилите на данном этапе жизни настоящий «комплекс». Лучше сконцентрироваться либо на одной профильной услуге, либо на связке «1 профильная + 2-3 дополнительные» (не вздумайте надеяться, что будете зарабатывать на всем).


2. КОМУ = профиль вашего потенциального клиента:


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


b. Масштаб бизнеса:


i. Малый:


§ В т.ч. ремесленники (одиночки; возможно с «подмастерьями»).


ii. Средний.


iii. Большой.


c. Сфера. Откройте любой справочник и выберите навигацию по категориям компаний, чтобы оценить масштабы. Сразу скажу, что этот подход уже не является лучшим. Причина №1: клиенты не ценят ваши профильные компетенции (убедитесь, когда проиграете тендер какому-нибудь более сильному конкуренту, у которого нет скиллов по данной тематике). Причина №2: этот метод используют те самые «прощупыватели» ниш, кормя рынок некачественными предложениями; благодаря им этот подход начинает изживать себя. Причина №3: в некоторые ниши проход закрыт; вы можете получить запрос от нефтянщиков, но, даже прочитав все книги по теме и посетив всевозможные мероприятия, не станете «своим» на фоне конкурентов.


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


3. КАК = ключевые особенности продукта / процессов:


a. Здесь по определению нельзя расписать все подпункты. Просто приведу примеры:


i. Скоростной запуск – услуги с быстрым стартом / ранними результатами. Типовые решения, упрощающие жизнь сервисы и пр. в помощь.


ii. Глубокая интеграция в бизнес-процессы клиента — вплоть до CRM и даже офиса (полный аутсорс — сдача себя «в аренду»).


iii. Одна из методологий работы:


§ Стандартный «каскад».


§ RAD.


§ Гибкая Agile.


4. ПОЧЕМ = ценообразование:


a. Сегмент:


i. Низкий (будете конкурировать с сервисами; но это не значит, что здесь можно делать продукт на уровне «тяп-ляп»).


ii. Средний (опасно — не входить: мало спроса и много предложений).


iii. Высокий (нужны стальные яйца компетенции).


b. Модель:


i. Фикс (есть шанс «выкатиться» за пределы, если даже обговорен объем работ).


ii. Почасовка (сложно продавать — в глазах клиента цена выше ценности).


iii. Бонусная система — оплата на основе достижения KPI (в большинстве случаев геморрой с расчетами):


§ Лидогенерация (фактически вы «инвестируете» в клиентский бизнес).


Позиционирование можно строить сразу на 2-3 показателей. Только следите, чтобы «кусок» был не слишком маленьким, и чтобы на него не было слишком много «едаков». Иначе не «наедитесь».


Пример плохой ниши:


SMM премиум-сегмента для небольших автомоек, открытых по методу БМ, с оплатой за обращения.


Почему это не есть гуд?


  • В этой нише изначально не нужны премиум-услуги.
  • В итоге может получится низкий чек (даже при условии успешности проекта), так как SMM — это в первую очередь не про лидогенерацию.
  • Низкий LTV.
  • Да и в целом довольно «геморройный» сегмент. Нахлебаетесь, коллеги.

Пример хорошей ниши:


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


Почему это гуд?


  • Тут есть денежка.
  • В 1 проекте есть как минимум 2 заинтересованные стороны.
  • На рынке сложно найти подобное предложение, хотя наличие спроса — очевидно (учитывайте тренд «укрупнения» бизнеса во многих сферах, где создаются целые федеральные сети).
  • Возможен высокий LTV, если наладить систему периодических модернизаций + развертывания сайтов для новых франчайзи + технической поддержки.

Полную версию книги можно скачать здесь: http://www.zarutskiy-k.ru/books/33-growth-tips.pdf

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

https://habrahabr.ru/post/332932/


Метки:  

Поиск сообщений в rss_rss_hh_new
Страницы: 1437 ... 1048 1047 [1046] 1045 1044 ..
.. 1 Календарь