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

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

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

Как мы перевели 400 магазинов на электронные кассы

Пятница, 22 Сентября 2017 г. 15:21 + в цитатник
Svetlana_mvideo сегодня в 15:21 Управление

Как мы перевели 400 магазинов на электронные кассы



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

    Итак, в ноябре 2016 года мы узнали, что грядет внедрение ФЗ-54: к лету 2017 года нам придется поменять все кассы в наших магазинах на новые. Все кассы — это порядка 2,5 тысяч во всех регионах страны: от Владивостока до Калининграда. Знаете, как мне поставили задачу?
    «В РФ появились изменения: федеральный закон 54 — облачная фискализация! Надо соответствовать». И мы приступили к выполнению директивы.

    Вводные данные


    На конец октября 2016 мы знали, что в Роснефти уже внедрили новые кассы, и налоговая даже осталась довольна результатом. Но как это происходило, в каком виде внедрялось — подробностей никто не знал. Погуглив, мы поняли, что никто на рынке пока толком не знает, что делать. Знали не много: есть новый формат фискальных данных, документ страниц на 100, в котором прописаны теги, которые надо передавать в ОФД и налоговую. Есть операторы фискальных данных — сертифицированные организации, договор хотя бы с одной из которых нам придется заключать, а это означает: тендер, долгие муки выбора, а до этого отсутствие тестовой площадки. А еще меняется процедура регистрации и перерегистрации касс: теперь не надо нести ККТ в налоговую, можно сделать всё удаленно через личный кабинет налогоплательщика. Остальная информация была противоречива и расплывчата.

    Не было сомнений только в одном: нам точно придётся это делать, а также модернизировать парк кассовой техники, так как вместо ЭКЛЗ (электронной контрольной ленты защищенной) появляется фискальный накопитель.

    Переход на новые кассы


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

    Вариантов было два:

    • купить новые сертифицированные кассы (а компаний, которые уже сертифицировали ККТ, на тот момент было не так много);
    • сделать кастомные кассы: из старых вынуть ЭКЛЗ, просверлить парочку отверстий, просунуть парочку проводов и подключить фискальный накопитель.

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

    Как осуществлялось внедрение


    Стратегически командой мы решили, что затянем с внедрением ФЗ-54 до последнего, но зато пропустим этап внедрения на версии ФФД 1.0, перерегистрацию с 1.0 на 1.05, и запустимся сразу на версии 1.05. Как показала жизнь — решение было ошибочным и стоило нам почти месяца понапрасну потерянного времени.

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



    Вторая сложность заключалась в том, что наше ПО было написано на Oracle, а у «Атола» на тот момент ещё не было совместимой прошивки. Поэтому пришлось параллельно писать ещё и оболочку для ККТ, которая обеспечивала взаимодействие между кассами и нашим ПО. Эта работа была самой сложной, потому что выполнялась фактически наощупь, методом проб и ошибок. По сути — изобретали велосипед.

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

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

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

    Проблемы с подарочными картами


    Еще одним из велосипедов стали подарочные карты. Как правильно передавать данные по ним в налоговую? При покупке клиентом подарочной карты мы вносим на неё деньги — аванс, — который потом тратится на покупку товаров — зачет аванса. То есть оборот одной суммы денег осуществляется дважды, и согласно новому закону «каждый чек должен быть фискализирован», в этой ситуации мы должны были заплатить налог дважды: первый раз — когда продали карточку с начислением на нее средств, второй раз — когда мы эту карточку используем в оплату. Однако в ФФД 1.0 не была предусмотрена разбивка на кредиты, на авансы, на погашение авансов и прочие оплаты.

    Это было предусмотрено в более поздних версиях ФФД, начиная с 1.05. Именно поэтому мы и выбрали его на этапе старта проекта, но… формат был не согласован налоговой, разъяснений к использованию формата не было, и поэтому прошивка ККТ не готова была поддерживать 1.05 даже в тестовом контуре.

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

    Дополнения к закону и очные ставки с налоговой


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

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

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

    Чеки в интернет-магазине


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

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

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

    Лучше раньше, чем никогда


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

    В нашем нелегком квесте были две очень важные даты:

    • 1 февраля 2017 — начиная с этого дня на учёт запрещалось ставить кассы старого образца.
    • 1 июля 2017 — в этот судьбоносный день все кассы должны были работать на новом программном обеспечении.

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

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

    Добавьте сюда ещё такой фактор, что с 1 февраля по 1 июля у части касс четырёхсот магазинов подходил к концу срок действия ЭКЛЗ. Все магазины пришлось провести через инвентаризацию, выявить по каждой кассе дату завершения срока действия ЭКЛЗ, и, по-возможности, заменить их до вступления закона в силу.

    До Нового Года процесс замены шел на «ура», мы безболезненно заменили ЭКЛЗ у части касс на новые. Тем же самым занимались множество других компаний. Хоть это и было законно, налоговая быстро поняла, что происходит, и прикрыла эту лавочку. Теперь поменять ЭКДЗ или саму кассу можно было только по серьезной причине: если касса сломана или срок ЭКЛЗ подошел к концу.

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

    Сроки поджимали, мы никак не успевали дописать кассовое ПО. Мы понимали, что с середины апреля в ряде магазинов часть касс будут выходить из эксплуатации, а подменного фонда нет. Казалось бы, что тут страшного: в магазине четыре кассы, одну убрали, осталось три, работать можно. Но в один далеко не прекрасный момент в одном из крупных магазинов с 12 кассами вышли из строя 8. Оставшихся четырёх категорически не хватало для своевременного обслуживания покупателей. Час Икс с 1 июля сместился на апрель.

    А вскоре выяснилось, что в марте три магазина встанут совсем, если не заменить кассовые аппараты и не внедрить в этих магазинах новые требования ФЗ-54.



    Аврал и его команда


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

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

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

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

    Уборщица в серверной


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

    Но если касса 30 дней ничего не передаёт в налоговую, то она автоматически блокируется для осуществления продаж.

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

    С этого момента техподдержка стала «любить» нас ещё больше.



    К чему пришли и куда стремимся


    Сейчас у нас все магазины переведены на новые кассовые аппараты. Пока что работаем только с «Атолами», хотя это категорически противоречит нашей политике антимонополизма. Снятые с учёта «Штрихи» проинвентаризировали: те, кому больше 5 лет, утилизировали, а более свежие протестировали и подготовили к модернизации. Напишем для них ПО, вызовем специалистов ЦТО, они вставят фискальные накопители и опломбируют, это будет запасной фонд. Мы уже научены горьким опытом, когда наш предыдущий поставщик просто не стал с нами общаться на тему поставки новых кассовых аппаратов в конце прошлого года, хотя ещё действовал договор поставки. Кроме того, кассовые аппараты могут ломаться (сами, честное слово!), их приходится ремонтировать или менять.

    Текущая версия формата фискальных данных 1.0 действительна только до 1 июля 2018, поэтому скоро начнём работу над переходом на версию 1.05. В ней больше тегов, в частности, есть описание комиссионного товара, понятие разделения форм платежей и многое другое, что так нужно нам. Это позволит использовать в налоговых отчётах большее количество фильтров.

    Сегодня у нас всего две формы оплаты: наличными и электронными средствами оплаты. Ко второй категории относятся авансы, кредиты, банковские карты, Яндекс.Деньги, Киви-кошельки и прочее. Поэтому разобраться на уровне оператора фискальных данных, что есть что — просто mission impossible. А это затрудняет взаимозачёты с налоговой.

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



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

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

    Но это всё ждет нас впереди.

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

    https://habrahabr.ru/post/338472/


    Метки:  

    [Из песочницы] Как я написал свою CMS, и почему не рекомендую вам делать то же самое

    Пятница, 22 Сентября 2017 г. 14:52 + в цитатник
    Cheburator2033 сегодня в 14:52 Разработка

    Как я написал свою CMS, и почему не рекомендую вам делать то же самое

    Работа над программами управления контентом CMS (content management system) полна чудес. Под катом поучительная история Petr Palas. Если у вас все хорошо с английским, то в оригинале текст можно почитать здесь. Enjoy!

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


    В 2000-м я обучался в университете и работал Интранет-разработчиком: публиковал в Интранет контент, написанный на статичном HTML. Это была моя первая «программистская» работа, и я ею наслаждался. Пару недель.

    Потом стало очевидно, насколько мои обязанности являются однообразными и неавтоматизированными. И я начал писать приложение на классическом ASP, которое позволяло бы пользователям самостоятельно управлять контентом. Я и понятия не имел о существовании такой штуки, как Content Management System, и потому изобретал велосипед. В то время существовало всего несколько коммерческих CMS, зачастую стоившие сотни тысяч долларов. Учитывая распространённость и ценовой диапазон этой категории ПО, неудивительно, что не я один пытался уменьшить свои неудобства и повысить эффективность, создавая собственную CMS.

    К 2004-му почти каждое интернет-агентство создавало собственную CMS, нередко кастомизируя под конкретных клиентов. Это приводило к появлению десятков модификаций — кошмар с точки зрения управления. «Это бессмысленно», думал я. К тому моменту я уже написал несколько специализированных CMS и снова заскучал. «А что если написать CMS, которая может быть полезна для любого сайта?» В результате я организовал компанию Kentico Software, чья миссия была очень проста: создать CMS, которую любой разработчик в мире может использовать для создания любого сайта.

    Сюрприз: люди всё ещё пишут собственные CMS!


    13 лет спустя меня ещё поражает количество людей, которые пишут собственные CMS. Существует масса зрелых продуктов, под все виды проектов: от open source до коммерческих систем корпоративного уровня, от лучших в своём классе до универсальных «всё-в-одном».

    Так зачем кому-то до сих пор нужно писать собственную CMS?
    Ответ прост: люди делают это из-за разочарования.

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

    Самописные CMS устарели из-за headless-архитектуры


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

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



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

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

    Причина №1: стандартные CMS ограничивают мой творческий потенциал


    Первое, на что жалуются фронтенд-разработчики, это вмешательство CMS в их HTML-код и необходимость искать обходные решения.

    Но с этим покончено: headless-CMS дают вам полную свободу и никак не влияют на результирующий HTML-код. Для извлечения контента из репозитория вам достаточно лишь с помощью своего любимого языка программирования вызвать соответствующий REST API.
    И более того, вы сами полностью решаете, как будет этот контент отображаться!

    Причина №2: интерфейсы стандартных CMS слишком сложны


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

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

    Причина №3: стандартные CMS слишком дороги


    «Мы не хотели платить Х рублей за коммерческую CMS, поэтому решили написать свою». Если вам не нужно что-то гораздо более простое, чем реальная CMS (вроде управления списком новостей), в долгосрочной перспективе вы не сможете сэкономить с помощью самописной CMS.

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

    Причина №4: стандартные CMS не безопасны


    Для многих организаций обеспечение безопасности CMS является кошмаром. Поэтому некоторые разработчики думают: «Если мы напишем свою CMS, то хакерам будет труднее найти в ней баги».
    Классическое обеспечение безопасности через неясность (security by obscurity).

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

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

    Причина №5: стандартные CMS не вписываются в мою архитектуру


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

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

    К счастью headless-архитектура позволяет легко обращаться к контенту с помощью API и писать свои приложения так, как вам хочется.

    Причина №6: многие клиенты всё ещё пользуются написанной нами CMS


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

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

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

    … и две причины, когда использование самописной CMS оправдано


    Честно говоря, всё же есть ситуации, когда использовать собственную CMS либо целесообразно, либо это вообще единственный возможный вариант:

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

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

    Не пишите свою CMS, пока не возникнет очевидный бизнес-случай


    Люди ВСЕГДА недооценивают объём работы по созданию настоящей CMS.

    Возможно, в первый момент вы подумали: «Что такого сложного в CMS? Я просто возьму задокументированную базу данных и прикручу сверху интерфейс редактирования». Это лёгкий старт, но ещё не настоящая CMS. Когда вы начнёте добавлять уровни, например, моделирование контента, языковые варианты, рабочий процесс, разрешения, доставку контента, поиск и так далее, то обнаружите, что разрабатываете и управляете по-настоящему сложным решением.

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

    https://habrahabr.ru/post/338492/


    Метки:  

    [Из песочницы] Вышла первая версия SignalR для ASP.Net Core 2.0

    Пятница, 22 Сентября 2017 г. 14:45 + в цитатник
    AndreyNikolin сегодня в 14:45 Разработка

    Вышла первая версия SignalR для ASP.Net Core 2.0

    Привет, Хабр!
    14 сентября было объявлено о выпуске первой версии SignalR для ASP.Net Core, в связи с чем я решился перевести заметку, посвященную даному событию, немного её дополнив. Оригинал заметки доступен в блоге на MSDN.


    Что нового?


    SignalR для ASP.Net Core является переписанной с нуля версией оригинального SignalR. Новый SignalR проще, более надёжен и легче в применении. Несмотря на эти внутренние изменения, мы старались сделать API библиотеки наиболее близким к предыдущим версиям.


    JavaScript/TypeScript клиент


    SignalR для ASP.Net Core имеет совершенно новый JavaScript клиент. Он написан с использованием TypeScript и более не зависит от JQuery. Клиент также может использоваться из Node.js с несколькими дополнительными зависимостями.


    Клиент распространяется в качестве npm модуля, который содержит Node.js версию клиента (подключается через require), и также версию для браузера, которую можно встроить используя тег

    https://habrahabr.ru/post/338490/


    Метки:  

    [Перевод] Kali Linux: фильтрация трафика с помощью netfilter

    Пятница, 22 Сентября 2017 г. 14:35 + в цитатник

    Метки:  

    [Перевод - recovery mode ] «Невидимый дизайн»: проектируем вместе с машинами

    Пятница, 22 Сентября 2017 г. 14:29 + в цитатник
    netologyru сегодня в 14:29 Дизайн

    «Невидимый дизайн»: проектируем вместе с машинами

    Ирина Черепанова и Татьяна Жукова из проекта uKit AI, обучающего нейросеть редизайну сайтов, перевели колонку менеджера команд продуктового дизайнер из Airbnb Амбер Картрайт о том, как умные технологии позволяют улучшать известные продукты.

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



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

    Математика и наука — невидимые силы, которые всё больше раскрывают себя и влияют на наши жизни.

    Возьмём пример из прошлого: английский джентльмен прогуливается по саду в начале 18-го века и наблюдает, как яблоко падает с дерева. Он задаётся вопросом, почему оно не упало в сторону или не отскочило вверх от земли. Как это возможно? Какие силы задействованы? Какова их природа? В равной ли степени данное явление распространяется на мелкие предметы вроде яблок и на крупные вроде повозок? Сэр Исаак Ньютон разбирался с этими вопросами на протяжении более 20 лет и вывел закон всемирного тяготения. Он смог описать невидимую силу, которая ощутимо воздействует на нашу повседневную жизнь.

    Недавно, просматривая ленту Facebook, я заметила, что нескольким моим друзьям понравилась компания, которая предлагала в режиме онлайн спроектировать и заказать индивидуальные рамки для постеров и холстов. Я задумалась обо всех тех необрамленных работах, которые лежали у меня в кладовке, и кликнула посмотреть, что предлагают. Почему я заинтересовалась этой рекомендацией? Что привлекло моё внимание? Какого рода информацию они использовали, чтобы персонализировать этот пост? Невидимые силы науки и математики, задействованные здесь, — это алгоритмы соцсети. Рекламное программное обеспечение — это всего лишь зародышевое состояние тех возможностей, которые несет мощь машинного обучения в ближайшие 5–10 лет.

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

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

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

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

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


    Умная модель регрессии цен и её визуализация: модель состоит из трёх частей, варьирующихся в зависимости от предложения хозяина и рыночного спроса.

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

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

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

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

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

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

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

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

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


    Пример: динамика рынка вызывает показ нужных вариаций модулей и сообщений

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

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

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

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

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

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

    Функция «Умные цены», когда алгоритмическая модель советовала владельцам квартир, какую цену аренды лучше выставить для конкретного дня, — пример того, как состав команды помог развитию продукта.

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

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

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

    На выходе мы получили сверхуспешный в плане удовлетворения пользовательских и информационных потребностей продукт: арендодатели получили больше механизмов контроля — смогли выставлять минимальные и максимальные цены, а также выбирать желаемую частоту приёма гостей. Ниже вы можете проследить эволюцию первой версии продукта «Подсказки цен» (Pricing Tips) до нашей текущей функции «Умные Цены» (Smart Pricing).



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

    Многим арендодателям «Умные Цены» пришлись по вкусу, и это результат того, что представители разных отделов были включены в работу, устраивали необходимые обсуждения, когда того требовали обстоятельства.

    Эти мысли — лишь пролог к беседам о «невидимом дизайне». Я продолжу исследовать и писать о своих открытиях — о понимании задач и путей их решения, об использовании инструментов и анализе результатов — по мере того как я буду дальше продвигаться по стезе продуктового дизайна. Чтобы ознакомиться с тем, как на практике развивается «невидимый дизайн», — смотрите детальный разбор пользовательского сценария функциональности «Умные цены», который я презентовала на конференции IxDA в Хельсинки в начале этого года.

    О курсах Нетологии


    1. Бесплатные программы по UI/UX и дизайну:


    2. Программа «Big Data: основы работы с большими массивами данных»

    Для кого: инженеры, программисты, аналитики, маркетологи, дизайнеры — все, кто только начинает вникать в технологию Big Data.

    Формат занятий: онлайн
    Подробности по ссылке ->
    Original source: habrahabr.ru (comments, light).

    https://habrahabr.ru/post/338478/


    Метки:  

    [Перевод] Пол Грэм: банальное и прорывное

    Пятница, 22 Сентября 2017 г. 14:22 + в цитатник
    MagisterLudi сегодня в 14:22 Разное

    Пол Грэм: банальное и прорывное

    • Перевод


    Сентябрь 2017

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

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

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

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

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

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

    Говорят, что “ничто не ново под Луной”. Это не так. Есть некоторые домены, где нет почти ничего нового. Но есть большая разница между “ничего нового” и “ПОЧТИ ничего нового” в масштабах этой деятельности.

    Спасибо Сэму Альтману, Патрику Коллисон и Джессике Ливингстон за чтение черновиков этой статьи.
    Original source: habrahabr.ru (comments, light).

    https://habrahabr.ru/post/338300/


    Метки:  

    Автоматизируем тестирование на проникновение с apt2

    Пятница, 22 Сентября 2017 г. 13:37 + в цитатник
    antgorka сегодня в 13:37 Разработка

    Автоматизируем тестирование на проникновение с apt2

    • Tutorial


    20 сентября состоялся очередной релиз популярного дистрибутива для проведения тестирования на проникновение Kali Linux 2017.2. Среди нововведений мы получили возможность установить из репозитория несколько новых инструментов. В данном тексте мы рассмотрим фреймворк apt2 или Automated Penetration Testing Toolkit.

    Установка и настройка


    После обновления дистрибутива до версии 2017.2 можно приступать к установке фреймворка.
    Напомню, что обновиться до последней версии Kali Linux можно при помощи следующих команд

    apt-get update
    apt-get upgrade
    apt-get dist-upgrade

    Установка происходит стандартно

    apt-get install apt2

    Далее рекомендую провести базовую настройку.

    Изучив официальный github проекта становится ясно, что он интегрируется с Metasploit Framework.

    Для интеграции с Metasploit нужно запустить его RPC сервис.
    Делается это следующим образом

    msfconsole
    load msgrpc

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


    Можно посмотреть на структуру конфигурационного файла в каталоге /usr/share/apt2



    Нас интересует файл default.cfg

    В блоке [metasploit] указываем наш пароль

    [metasploit]
    msfhost=127.0.0.1
    msfport=55552
    msfuser=msf
    msfpass=kqVbTlmr
    msfexploitdelay=20

    Далее вы можете добавить дополнительные ключи для nmap или изменить подсеть. По умолчанию используется SYN сканирование (-sS) и флаг (-A), что указывает Nmap провести определение версий сервисов, типа и версии ОС, выполнить безопасные NSE скрипты и traceroute. В некоторых случаях будет нелишним добавить ключ -Pn, если вы не импортируете в apt2 результат сканирования nmap.

    [nmap]
    scan_target=192.168.1.0/24
    scan_type=S
    scan_port_range=1-1024
    scan_flags=-A

    По умолчанию используется 20 потоков. Это значение можно не менять без необходимости.

    [threading]
    max_modulethreads=20

    Есть настройка для Reponder. Если Вы используете нестандартную конфигурацию, нужно отредактировать блоки [responder] и [default_tool_paths]. Я задам responder_timeout=30, так как не хочу тратить время на этот модуль.

    Далее идет блок [searching]

    [searching]
    file_search_patterns=*.bat,*.sh,*passwd*,*password*,*Pass*,*.conf,*.cnf,*.cfg,*.config

    Здесь можно задать маски файлов, которые нас интересуют, если мы получим доступ, например, к NFS ресурсу. Можно добавить что-то свое или не менять.

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

    [apikeys]
    #apt2_shodan_apikey=CHANGEME
    #apt2_linkedin_apikey=CHANGEME

    Запуск


    При запуске с ключом -h мы получаем список доступных ключей



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

    SAFE_LEVEL может принимать значения от 1 до 5 и указывает apt2 насколько безопасные модули разрешено запускать. Самые безопасный режим — 5. По умолчанию используется 4.

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

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

    и ключ --listmodules покажет доступные модули. Давайте посмотрим на этот список

    apt2 --listmodules

    Получаем список с указанием имени модуля, его типа (используется для EXCLUDE_TYPES), Safety Level и описания. Модули с Safety Level ниже, чем указано ключом -s выполнены не будут, о чем будет дополнительно сказано в выводе apt2 при запуске.



    Давайте запустим apt2 с максимальным уровнем риска против машины 192.168.1.4, на которой работает Ubuntu.

    apt2 -v -v -s 1 -b --target 192.168.1.4

    Нас предупреждают, что модули, требующие API ключи не будут выполнены и начнется сканирование



    Далее начнут запускаться модули



    Посмотреть, что смог собрать apt2 можно в директории /root/.apt2/proofs



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

    Запустим еще одно сканирование в отношении Windows машины 192.168.1.7 и не будем выполнять сканирование nmap через apt2, а загрузим XML файл.

    nmap -n -Pn -A -oX scan1 192.168.1.7
    apt2 -s 1 -b -v -v -f scan1

    Здесь уже запускаются другие модули, например модуль для тестирования на уязвимость к ms08-067.



    Если сервер уязвим, об этом будет сказано в логе

    [!] VULN [ms08-067] Found on [192.168.1.7]

    и уязвимость будет проэксплуатирована через сервис Metasploit RPC автоматически и мы получим сессию



    Далее через сессию уже будут выполнены другие модули.



    И в конце работы программы будет создан отчет о проделанной работе



    firefox /root/.apt2/reports/reportGenHTML_flcgfsqhji.html



    Если вы хотите написать свои модули для apt2, можно изучить имеющиеся в директории /usr/share/apt2/modules и сделать собственные по аналогии. Сам фреймворк написан на python и модули к нему, соответственно, тоже.
    Original source: habrahabr.ru (comments, light).

    https://habrahabr.ru/post/338460/


    Метки:  

    ЭДО. Электронный документооборот что это и как выбирать

    Пятница, 22 Сентября 2017 г. 13:27 + в цитатник
    JustRamil сегодня в 13:27 Управление

    ЭДО. Электронный документооборот что это и как выбирать

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


      Важно понимать, что существует два вида документооборота — внутренний (СЭД) и внешний(ЭДО). ЭДО и СЭД так разделяют в принципе эти понятия в РФ. То есть если вы ищете систему для обмена с контрагентами, то вам нужно искать ЭДО, если для внутреннего документооборота то СЭД.

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


      Зачем нужен электронный документооборот


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


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


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


      Электронный документооборот позволяет избавиться от всех этих неудобств:


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

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


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


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


      Из чего состоит электронный документооборот


      В этом виде документооборота участвуют 4 стороны:


      1. Поставщик товаров или услуг. Сторона, которая генерирует электронный документ.
      2. Покупатель или клиент. Сторона, которая принимает электронный документ.
      3. Компания вендор, которая предоставляет электронную площадку для обмена документами и услуги сервиса для организации электронного документооборота.
      4. Государство. Госорганы, куда компания вендор передает данные о зафиксированных сделках и оформленных документах.

      Как это реализуется на практике:


      1. Поставщик товаров или услуг генерирует электронный документ в собственной учетной системе или сразу в сервисе, предоставляемом вендором.
      2. Документ внутри сервиса мгновенно отправляется компании-адресату, которая получает его либо непосредственно в сервисе, либо в подключенной к сервису собственной программе.
      3. Площадка фиксирует данные о документе.
      4. После «подписания» (подтверждения) стороной получателя данные о документе отправляются в налоговую инспекцию.

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


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


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


      При электронном документообороте:


      • Оригинал документа доставляется мгновенно. Нет необходимости в почтовых отправлениях, помощи курьеров или менеджеров. Нет отсрочек и связанных с ними проблем.
      • Руководитель компании в любой момент может проверить, когда именно был отправлен и получен документ. И в случае проблем, связанным с отсутствием важной документации, у сотрудников нет возможности переложить ответственность за свои ошибки на “плохую работу почты” и другие внешние факторы. Это понимают и сами сотрудники компании. А потому отсрочки и проблемы с отправкой/получением документов после внедрения этой системы становятся крайне редким явлением.

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


      Электронная подпись


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


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


      Площадки (сервисы) документооборота


      Сегодня существует широкий перечень площадок, предоставляющих услуги документооборота. Это Directum, ELMA, DocsVision, WSS Docs, E-COM, Диадок и многие другие. Все они выполняют примерно одинаковые функции:


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

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


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


      Системы ЭДО и EDI: в чем разница?


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


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


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


      Интеграция с 1С и другими учетными системами


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


      Зачем это нужно:


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

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


      Стоимость услуги


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


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


      Часто задаваемые вопросы


      Все ли документы надо отправлять через электронную систему?


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


      Оплата отправки производится за документ или страницу?


      Вы оплачиваете, и система фиксирует отправку именно документа. Даже если в вашем документе много страниц, он считается как одна единица к оплате.


      Насколько это безопасно?


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


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


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


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


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


      Настолько сложно пользоваться электронным документооборотом?


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


      А что делать, если мой клиент не использует площадку, которой пользуюсь я?


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

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

      https://habrahabr.ru/post/338444/


      Метки:  

      Несколько мыслей о человеческом факторе

      Пятница, 22 Сентября 2017 г. 13:16 + в цитатник
      kalinin84 сегодня в 13:16 Управление

      Несколько мыслей о человеческом факторе

        1. Вступление


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


        2. Человеческий фактор


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


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


        Естественно, что в процессе упрощения теряются очень важные характеристики и «ловятся» ложные закономерности. Особенно, если выборка не репрезентативная. Например, большинство людей увидит явную закономерность в следующем упорядоченном множестве натуральных чисел: «1, 2, 3, 4, 5, 6, 7, 8». Цифры явно возрастают, где каждый следующий элемент множества на единицу больше предыдущего. Но это не так. На самом деле это просто часть числа «Пи», затаившееся в первых двухсот миллионах знаков.


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


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



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


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



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



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


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


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


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


        3. Возвращение из мира иллюзий


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



        Они очень похожи. Видимо, они очень сильно связаны. Отобразим это в виде облака точек и решим задачу аппроксимации методом наименьших квадратов. Разве сильной линейной корреляции не наблюдается? Очень маленькая ошибка (расстояние от точки до прямой, которую провели методом наименьших квадратов). Линейный коэффициент корреляции Карла Пирсона чуть-чуть не дотягивает до единицы, и составляет около 0.98. А ещё интересно посмотреть на функции потерь (regression loss). Метрика точности MSE (Mean squared error) составляет приблизительно 10.47. Мне кажется, что MAE (Mean absolute error) тут более интуитивно понятна (она равна приблизительно 2.217).



        Похожи? Кстати, их средние значения тоже очень похоже. И минимум. И максимум. И среднеквадратическое отклонение. И верхний квартиль. И нижний квартиль. Всё очень похоже. Вот только их медианы в три раза различаются. Посмотрите на распределение этих величин:



        И на описательную статистику:



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



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


        Скажем, нас поспросят выбрать алгоритм классификации следующих данных. Данный набор точек нарисован вручную. Если есть потребность в учебных целях быстро и просто рисовать произвольные наборы данных в двухмерном пространстве (вектор из двух предикторов и метка класса), то можно использовать очень простой инструмент (работает в обычном браузере, так как это обычная страница на HTML с JavaScript). Можно как спреем быстро «набрызгать» точки с нужным отклонением от центра курсора. Я делал его для себя, а теперь решил опубликовать. Я добавил его в свой личный сборник полезных фрагментов кода: ссылка на github.



        Пусть факт принадлежности точки к кластеру определяет её класс:



        Руководствуясь каким принципом человек будет разделять эти точки? Известно, что классы линейно разделимы. Формально говоря, существует такая гиперплоскость размерностью на единицу меньше размерности пространства, которая разделит точки обоих классов этого набора данных. По сути, это простая линейная функция (смещение + предиктор коэффициент + второй предиктор второй коэффициент). Следовательно, самый простой перцептрон Розенблатта справиться с этой задачей. Или логистическая регрессия, которая по своей природе использует похожий подход. Кстати, алгоритм ближайших соседей тоже справиться с этой задачей (несколько ближайших точек в этом наборе будут всегда одного класса). Впрочем, тут два явных кластера, что позволяет успешно использовать алгоритмы кластеризации или даже медленный SVM. Тем более, с этой задачей легко справятся алгоритмы на основе ансамбля деревьев решений (критерий разделения «Gini impurity»).



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


                 precision   recal   f1-score  support
        
        0            1.00     1.00     1.00     382
        1            1.00     1.00     1.00     255
        
        avg / total  1.00     1.00     1.00     637

        Но какой алгоритм разделения будет правильный? Они же все справились с задачей. Мы хотим получить вполне конкретный ответ. Так устроено сознание. Нужно сократить меру неопределённости до элементарного (неделимого) решения. Стоит добавить одно условие и задача легко решается: из этих алгоритмов следует выбрать самый быстрый. Отлично! Это понятная и достижимая для нас цель. Мы получили ограничение, которое сделало задачу безальтернативной. Не нужно метаться между вариантами и выбирать лучший. Не будет и сомнений в стиле: «эх, чтобы было, если бы я выбрал другой?».


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


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


        4. Один мысленный эксперимент


        Предлагаю небольшой мысленный эксперимент. Подключим фантазию и постараемся представить описанную далее ситуацию. Нужно чуть больше фантазии, пожалуйста, добавьте ещё. Отлично! Так достаточно. И так. Вот по улице идёт обычный паренёк. Навстречу ему идёт симпатичная девушка. Он хотел бы с ней познакомится, но его нейронные сети начали настоящую баталию. В результате одержали победу те сети, которые отговаривали его это делать. Аргументы в стиле «Она крутая, на такое [плохое слово] внимание не обратит, только опозоришься, если прохожие увидят» оказали своё влияние. Действительно, в силу воспитания и других индивидуальных особенностей истории становления его разума у парня сложилась именно такая картина мира. Для него это реальность. И не важно, говорим мы о парне с девушками или о странных вопросах на собеседовании.


        Продолжим свой мысленный эксперимент. Представим себе, что создан настоящий идеальный искусственный интеллект. Раз персонаж у нас фантастический, то придумаем ему имя, например, «M-49», годится? Добавим ещё парочку щепоток фантазии. И есть у него одно важное задание: помочь парню найти «правильную» девушку. Мудрый М-49 постарается найти в базе данных подходящих кандидатов, из числа возможных для конкретного парня. Но там нет таких кортежей. Проблема: факторы противоречивые. Одно требование исключает другое. Но парень этого не понимает и продолжает настаивать на правильности своего мнения. Тяжело вздыхая грустный М-49 произносит: «Люди принимают плохие решения: по официальной статистике больше половины всех браков расторгается». Молчаливая пауза. На минуту зависнув фантастический интеллект всё-таки очнулся. «Это же люди!» — воскликнет он с большой долей негодования — «Что же ещё от них ждать?!». На этих словах М-49 сделал жест, известный как «facepalm».


        Продолжим фантазировать. Чтобы показать «подопытному» реальную ситуацию наш бравый M-49 придумывает машину времени и отправляется с ним в путешествие. Он с научной точки зрения показывает до смерти испуганному парню историю развития жизни на Земле, а более подробно останавливается на его биологическом виде. После этого он знакомит «подопытного» с историей его детства и семьи. Обосновывает различные значимые для него ситуации, объясняет почему мозг родителей принял решение именно так сказать или поступить. Как воспитывали самих родителей, учителей в школе и их родителей. Самое важное: он показывает ему примеры тех парней, которые в аналогичных обстоятельствах (или даже более худших) успешно справились с поставленной задачей. Его модель реальности постепенно начинает меняться.


        Разумеется, процесс мышления невероятно сложный. Происходит масса сложных химических реакций и физических взаимодействий. Это не просто запись информации, а формирование клеток и связей, т.е. некоторой плоти (физической сущности). Наш замученный «подопытный» вспомнил картинку с нейроном: аксоны, дендриты, нейромедиаторы (нейротрансмиттеры). Нейронная сеть просто так не формируется и не растворяется. Он вспоминал, как М-49 рассказывал ему про сложность изменения сетей в зависимости от масштаба. Есть глобальные убеждения, а есть «маленькие». Локальные убеждения легко меняются после чтения документации или специального обучения. Для человека будет несложно увидеть в «EXPLAIN» свою ошибку и сделать очередной «git commit -a -m "ID-1 fix something"» с новой миграцией, которая модифицирует индексы.


        Исправление важных для выживания убеждений (по субъективному мнению) невероятно сложный процесс. Это не просто большие затраты ресурсов, но и сильное сопротивление мозга. Он боится, что новые модели будут ещё хуже. А вдруг это приведёт к ещё более плохим последствиям, а «убежать из опасной территории» будет нельзя? С этими ярлыками я жив, хоть и страдаю, а с новыми вдруг совсем помру? Как говорят: «Гладко было на бумаге, да забыли про овраги». Разум человека предназначен для моделирования реальности с целью нахождения точек комфорта (где больше положительных подкреплений), а также для целей избегания точек с отрицательными подкреплениями.


        А весьма измотанный приключением «подопытный» всё не успокаивался. Он обдумывал свою жизнь, вспоминая и переосмысливая истории своей жизни. Допустим, одна нейронная подсеть мозга говорит «иди и познакомься с девушкой», а вторая блокирует — «нет, у тебя не получится, только опозоришься». Вот и внутренний конфликт, напряжение, невроз, ОКР и т.д. Естественно, а что ещё ожидать от одновременного нажатия на газ и тормоз? Вот только теперь он знал, что неуверенность — это защитный механизм. Если человек только учится управлять автомобилем, то он не уверен. Вместо работы над уверенностью ему необходим реальный опыт вождения. Его мозг научится управлять автомобилем и получит положительные подкрепления, следовательно, станет уверенным водителем. И так во всех сферах жизни. Пропадёт явная неопределённость — он будет понимать какие последствия будут у действий и осознавать, что они желанны для него.


        Парень, хоть и падал от усталости, но крепко задумался. Конечно, он понимал, что реальность сложнее. Намного сложнее. Именно по этой причине М-49 общался с ним сильно упрощая картину — он говорил понятными парню словами. Но думал парень не об этих словах. Думал «подопытный» о той выгоде, которая сулит ему. «Раз М-49 смог придумать машину времени, значит может придумать как получить деньги, власть и безнаказанность!» — рассуждал парнишка. Его мозг уже смоделировал ситуацию, и он увидел новую зону комфорта, а прежняя жизнь уже казалась ему набором отрицательных подкреплений. Со временем М-49 обзавёлся новым телом — платформой боевого робота-призрака, это как приведение, только боевой робот с огромными возможностями. Потом М-49 добавил себе ещё несколько подобных тел, создав распределённую отказоустойчивую систему.


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


        Старик думал о парне и о М-49. Он был в шоке, от того, как парнишка начал распоряжается появившимися возможностями. С каждым днём «подопытный» всё больше и больше «борзел» от своей безнаказанности. Стэнфордский тюремный эксперимент на его фоне просто меркнет. Старик был уверен, что Филиппу Зимбардо такое даже в страшных снах не снилось. В представлениях старика всё произошедшее с парнишкой стало похоже на эксперимент Милгрэма, так как за мнением провокатора М-49 послушно следовал «подопытный». Но парнишка уже получил своё могущество. Ему этого достаточно, чтобы быть в новой зоне комфорта и стимулировать свой разум таким нейромедиатором, как дофамин.


        Неожиданно ход мысли старика прервался: одна девушка села на лавку рядом, а её подруга просто остановилась, не отрывая глаз от телефона. Севшая на скамейку девушка сняла сапог и сказала подруге: «Ощущение, что ногу промочила, но носки сухие». Странные татуировки с монстрами были на её теле. А из наушников, которые она сняла, доносилась странная детская песенка: «я начинаю свой разбег» — других слов старик не разобрал. Но самым забавным показалось старичку другое: рисунки на её ногтях сильно напоминали маскот «FreeBSD». Да, этот весёлый Beastie, красовавшийся на логотипе в далёком 2022 году, когда старикан был совсем юным школьником.


        Чудной старик был обычным человеком. За свою жизнь много сомнений и страхов он испытал. Но он прожил обычную жизнь и мало о чём жалел, кроме как о нескольких упущенных возможностях. Его время безвозвратно ушло, заставляя уступить место новому поколению. Нет, он совсем не ощущал себя неудачником. И даже не считал, что мог бы прожить иную жизнь. В его системе ценностей такая жизнь была правильной и нормальной. Он не стремился к другому. Он просто сидел на скамейке и переживал за других людей. А потом очень неожиданно произнёс хриплым голосом: «Транзакция машины времени не закомичена, пока идёт грязное чтение истории, нужно сделать роллбэк». Старикан хромая ушел прочь с весьма решительным видом. Он понял, что зря разделил себя на две версии — вторая версия была настоящим злодеем, она находилась в другом потоке времени вместе с M-49, отстававшем от этого на несколько десятков лет. И сейчас должна грянуть музыка Карла Орфа — тут как нельзя кстати стихотворение «O Fortuna» из «Carmina Burana». Аплодисменты. Занавес.

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

        https://habrahabr.ru/post/338474/


        Метки:  

        Литература на выходные: 15 материалов по структурированию кода для разработчиков

        Пятница, 22 Сентября 2017 г. 13:07 + в цитатник
        it_man сегодня в 13:07 Разработка

        Литература на выходные: 15 материалов по структурированию кода для разработчиков

          Одним из параметров оценки кода служит его чистота. Создатель языка моделирования UML Гради Буч (Grady Booch) писал:

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

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

          / Flickr / Robert Gourley / CC

          Литература


          • «Совершенный код» Стив Макконнелл (Steve McConnell)

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



          • «Программист-прагматик. Путь от подмастерья к мастеру»
            Эндрю Хант (Andrew Hunt), Дэвид Томас (David Thomas)

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



          • «Способ мышления — Форт» Лео Броуди (Leo Brodie)

          Форт — это язык программирования, который широко использовался в астрономии в 70-х годах прошлого века. Несмотря на узкую тематику, разработчики найдут в книге основы построения кода. Даже для тех, кто не собирается изучать Форт, «фортанский» способ мышления поможет иначе взглянуть на рефакторинг. Переизданная в 2004 году версия предоставлена автором Лео Броуди (Leo Brodie) в общее пользование.



          • «Ruby. Объектно-ориентированное проектирование» Сэнди Метц (Sandi Metz)

          Сэнди Метц (Sandi Metz) подготовила практическое руководство по объектно-ориентированному проектированию с прицелом на разработчиков, знакомых с основами, но пока не сформировавших философию кода. Автор использует понятные и приближенные к реальной жизни примеры. Сэнди — фанат экономичных тестов и разумных подходов. На её сайте сказано: «Если код вас добивает, и никакой радости не осталось, — эта книга ваше решение».



          • «Рефакторинг. Улучшение существующего кода»
            Мартин Фаулер (Martin Fowler), Кент Бек (Kent Beck), Джон Брант (John Brant) и др.

          Еще один нестареющий фолиант, появившийся в конце 90-х. Список авторов «Рефакторинга» возглавляет Мартин Фаулер (Martin Fowler), который вместе с Кентом Беком (Kent Beck) стоит у основ методологии экстремального программирования. Это совершенно новый взгляд на процесс разработки, который показывает, каким должен быть код и как он должен создаваться. Книга содержит примеры рефакторинга с подробным описанием. Многие из них не потеряли своей актуальности, а часть была автоматизирована и теперь используется в современной разработке.



          • «Искусство программирования для Unix» Эрик Реймонд (Eric Raymond)

          Название книги не зря перекликается с фундаментальной работой Дональда Кнута (Donald Knuth) 1968 года «Искусство программирования». На страницах своей книги Эрик Реймонд (Eric Raymond) пытается донести не только практические рекомендации, но и философию — понимание кода в Unix. Он делает это с помощью множества примеров. В них прослеживается уважение к плодам работы талантливых людей, трудившихся над Unix, и философия «чистого кода».



          • «Практика программирования»
            Брайан Керниган (Brian Kernighan), Роб Пайк (Rob Pike)

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



          • «Чистый код. Создание, анализ и рефакторинг» Роберт К. Мартин (Robert Martin)

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



          • «Шаблоны игрового программирования» Роберт Нистром (Robert Nystrom)

          «Это книга, которую я хотел бы прочитать, когда начинал создавать игры», — так описывает ее сам автор. На первый взгляд, «Шаблоны» — это узкоспециализированная литература для геймдева от ветерана Electronic Arts. Однако при ближайшем рассмотрении книга позволяет переосмыслить работу с кодом и сделать его чище. Кто-то из пользователей Reddit даже решил изучить C++ после прочтения книги.



          • «Читаемый код, или Программирование как искусство»
            Дастин Босуэлл (Dustin Boswell), Тревор Фаучер (Trevor Foucher)

          По словам Николаса Закаса (Nicholas C. Zakas), автора работ по JavaScript, «книга погружает в процесс гигиены кода как ничто другое». В книге рассказано, как именование переменных, функций и классов помогает командам разработки. Основные акценты: структурирование кода и комментариев, балансирование между эффективностью и читаемостью.



          / Maxpixel / Code Data Programming / CC

          Несколько статей, блогов и инструментов


          • Статья «Как написать неподдерживаемый программный код» — своего рода классика. В формате «вредных советов» читателю предлагается узнать о том, какой код точно нельзя считать чистым.
          • У чистого кода есть некоторые формальные признаки. Проверить, не исходит ли от кода «дурной запах», можно с помощью таблицы профессора Мики Мянтюля (Mika M"antyl"a). Признаки хорошего кода можно найти в блоге Coding Horror.
          • Как советуют пользователи Quora, чтобы постичь искусство чистого кода, стоит как можно больше читать хороший код. Для этого подойдут такие платформы, как GitHub, Codeplex, Google Code. Кстати, различные платформы предоставляют готовые инструменты для улучшения кода. Например, GitHub Code Review.
          • Процесс очистки кода можно частично автоматизировать. Существуют инструменты для статистического анализа. В случае с C, C++ для этого разработан PC-lint. Упростить код позволяет SourceMonitor.
          • Рекомендации по структурированию кода также зачастую дают разработчики платформ. Например, такой документ есть у Microsoft для C.



          P.S. Наши дайджесты:


          P.P.S. О чем еще мы пишем в нашем блоге:

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

          https://habrahabr.ru/post/337836/


          Метки:  

          [Перевод] ECMAScript 6. Регулярные выражения с поддержкой Unicode

          Пятница, 22 Сентября 2017 г. 12:57 + в цитатник
          ollazarev сегодня в 12:57 Разработка

          ECMAScript 6. Регулярные выражения с поддержкой Unicode

          • Перевод

          В ECMAScript 6 представлены два новых флага для регулярных выражений:


          1. y включает режим «липкого» сопоставления.
          2. u включает различные связанные с Unicode опции.

          В данной статье объясняется влияние флага u. Эта статья будет Вам полезна, если Вы знакомы с Unicode-проблемами в Javascript.


          N.B.! Поскольку при попытке публикации статьи обнаружились проблемы с отображением некоторых используемых в статье Unicode-символов, часть кода была заменена изображениями со ссылками на JSFiddle.

          Влияние на синтаксис


          Установка флага u в регулярном выражении позволяет использовать escape-последовательности кодовых точек ES6 Unicode (\u{...}) в шаблоне.



          При отсутствии флага u такие вещи, как \u{1234}, технически могут по-прежнему возникать в шаблонах, но они не будут интерпретироваться как escape-последовательности кодовых точек Unicode. /\u{1234}/ эквивалентно записи /u{1234}/, которая соответствует 1234 последовательным символам u вместо символа, соответствующего escape-последовательности кодовых точек U+1234.

          Движок Javascript делает так из соображений совместимости. Но с установленным флагом u и такие вещи как \a (где a не является escape-последовательностью) больше не будут эквивалентны a. Поэтому, даже если /\a/ обрабатывается как /a/, /\a/u выбрасывает ошибку, т.к. \a не является зарезервированной escape-последовательностью. Это позволяет расширить функционал флага u регулярных выражений в будущей версии ECMAScript. Например, /\p{Script=Greek}/u выбрасывает исключение для ES6, но может стать регулярным выражением, соответствующим всем символам греческого алфавита согласно базе данных Unicode, когда соответствующий синтаксис будет добавлен в спецификацию.

          Влияние на оператор ‘.


          При отсутствии флага u, . соответствует любому символу BMP (базовая многоязыковая плоскость — Basic Multilingual Plane) за исключением разделителей строки. Когда установлен флаг ES6 u, . соответствует также астральным символам.



          Влияние на квантификаторы


          В регулярных выражениях Javascript доступны следующие квантификаторы (а также их вариации): *, +, ?, и {2}, {2,}, {2,4}. При отсутствии флага u, если квантификатор следует за астральным символом, он применяется только к низкому суррогату (low surrogate) этого символа.



          С флагом ES6 u квантификаторы применяются к символам целиком, что справедливо даже для астральных символов.



          Влияние на символьные классы


          При отсутствии флага u любой заданный символьный класс может соответствовать только символам BMP. Такие вещи, как [bcd] работают как мы того ожидаем:

          const regex = /^[bcd]$/;
          console.log(
          	regex.test('a'), // false
          	regex.test('b'), // true
          	regex.test('c'), // true
          	regex.test('d'), // true
          	regex.test('e')  // false
          );
          

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



          Флаг ES6 u позволяет использовать цельные астральные символы в символьных классах.



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



          Флаг u также влияет на исключающие символьные классы. Например, /[^a]/ эквивалентно /[\0-\x60\x62-\uFFFF]/, что соответствует любому символу BMP, кроме a. Но с флагом u /[^a]/u соответствует гораздо большему набору всех символов Unicode, кроме a.



          Влияние на escape-последовательности


          Флаг u влияет на значение escape-последовательностей \D, \S, и \W. При отсутствии флага u, \D, \S, и \W соответствуют любым символам BMP, которые не соответствуют \d, \s и \w, соответственно.



          С флагом u, \D, \S, и \W также соответствуют астральным символам.



          Флаг u не обращается к их обратным аналогам \d, \s и \w. Было предложено сделать \d и \w\b) более Unicode-совместимыми, но это предложение было отклонено.

          Влияние на флаг i


          Когда установлены флаги i и u, все символы неявно приводятся к одному регистру с помощью простого преобразования, предоставляемого стандартом Unicode, непосредственно перед их сопоставлением.

          const es5regex = /[a-z]/i;
          const es6regex = /[a-z]/iu;
          console.log(
          	es5regex.test('s'),      es6regex.test('s'),      // true true
          	es5regex.test('S'),      es6regex.test('S'),      // true true
          	// Note: U+017F преобразуется в `S`.
          	es5regex.test('\u017F'), es6regex.test('\u017F'), // false true
          	// Note: U+212A преобразуется в `K`.
          	es5regex.test('\u212A'), es6regex.test('\u212A')  // false true
          );
          

          Приведение к одному регистру (case-folding) применяется к символам в шаблоне регулярного выражения, а также к символам в сопоставляемой строке.

          console.log(
          	/\u212A/iu.test('K'), // true
          	/\u212A/iu.test('k'), // true
          	/\u017F/iu.test('S'), // true
          	/\u017F/iu.test('s')  // true
          );
          

          Эта логика приведения к одному регистру применяется и к escape-последовательностям \w и \W, что также влияет на escape-последовательности \b и \B. /\w/iu соответствует [0-9A-Z_a-z], но также и U+017F, поскольку U+017F из сопоставляемой строки регулярного выражения преобразуется (canonicalizes) в S. То же самое касается U+212A и K. Таким образом, /\W/iu эквивалентно /[^0-9a-zA-Z_\u{017F}\u{212A}]/u.

          console.log(
          	/\w/iu.test('\u017F'), // true
          	/\w/iu.test('\u212A'), // true
          	/\W/iu.test('\u017F'), // false
          	/\W/iu.test('\u212A'), // false
          	/\W/iu.test('s'),      // false
          	/\W/iu.test('S'),      // false
          	/\W/iu.test('K'),      // false
          	/\W/iu.test('k'),      // false
          	/\b/iu.test('\u017F'), // true
          	/\b/iu.test('\u212A'), // true
          	/\b/iu.test('s'),      // true
          	/\b/iu.test('S'),      // true
          	/\B/iu.test('\u017F'), // false
          	/\B/iu.test('\u212A'), // false
          	/\B/iu.test('s'),      // false
          	/\B/iu.test('S'),      // false
          	/\B/iu.test('K'),      // false
          	/\B/iu.test('k')       // false
          );
          

          Влияние на HTML-документы


          Верьте или нет, но флаг u также влияет и на документы HTML.

          Атрибут pattern для элементов input и textarea позволяет вам указать регулярное выражение для проверки ввода пользователя. Затем браузер обеспечивает вас стилями и скриптами для создания поведения, основанного на достоверности ввода.



          Флаг u всегда включен для регулярных выражений, скомпилированных через HTML атрибут pattern. Вот демонстрационный пример.

          Поддержка


          На данный момент флаг ES6 u для регулярных выражений доступен в стабильных версиях всех основных браузеров, кроме Safari. Браузеры постепенно начинают использовать этот функционал для HTML атрибута pattern.
          Browser(s) JavaScript engine u flag u flag for pattern attribute
          Edge Chakra issue #1102227 + issue #517 + issue #1181 issue #7113940
          Firefox Spidermonkey bug #1135377 + bug #1281739 bug #1227906
          Chrome/Opera V8 V8 issue #2952 + issue #5080 issue #535441
          WebKit JavaScriptCore bug #154842 + bug #151597 + bug #158505 bug #151598

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


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


          Преобразование (transpiling) Unicode ES6 регулярных выражений в ES5


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



          Полномасштабные транспилеры ES6/ES7, такие как Traceur и Babel, зависят от regexpu при транспиляции u. Дайте мне знать, если вам удастся это сломать.
          Original source: habrahabr.ru (comments, light).

          https://habrahabr.ru/post/338366/


          Метки:  

          Не трогайте логи руками! Как сократить время на анализ с помощью автотестов

          Пятница, 22 Сентября 2017 г. 12:54 + в цитатник
          EFS_programm сегодня в 12:54 Разработка

          Не трогайте логи руками! Как сократить время на анализ с помощью автотестов

            Последнее время большое внимание в Программе «Единая Фронтальная Система» (ЕФС) уделяется автоматизации тестовых сценариев. Причины объективны и связаны с повышением уровня зрелости отдельных подсистем Программы и объемом регрессионного тестирования.

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




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

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

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

            Именно в этот момент мы в Программе ЕФС пришли к необходимости создания автоматизированной системы —  назовем ее Unified Logfile Analyzer или коротко ULA — которая бы помогла достичь двух целей:

            • минимизация времени анализа результатов автотестов;
            • минимизация количества дублирующих дефектов.

            С чего начинали?


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

            В качестве технологического стека были выбраны:

            • СУБД Oracle 11g для хранения справочных данных, истории;
            • Oracle TimesTen In-Memory Database 11g для хранения хэшей shingles;
            • Jersey для микросервисов;
            • React JS для FrontEnd.

            В ULA используются данные от самих автотестов: XML, CSV, снимки экрана, видео, а также от аппликативной части тестируемых подсистем: лог-файлы WebSphere, данные из БД журналирования.



            Система распределенная и состоит из нескольких модулей:

            • Агенты по разбору логов автотестов;

            На текущий момент разработаны агенты для Allure v.1.4,v. 1.5 и Serenity. Агенты обращаются в открытый API и работают под управлением Windows или Linux.

            • Plugin Jenkins;

            Управляет агентами по передаче логов-автотестов к Агенту;

            • Maven Plugin;

            Альтернативное решение Jenkins plugin: результаты выполнения можно загружать в обход Дженкинса, используя maven;

            • Набор микросервисов с функциями приема новых сообщений, принятия событий о завершении запусков, для функционирования Frontend части;

            • Frontend на базе React JS;
            • База данных под управлением Oracle 11g;
            • Службы нотификации для рассылки результатов тестов по определенным каналам.

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

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

            На данный момент реализованы и проходят тестирование ряд подсистем, рассмотрим их подробнее.

            Подсистема агрегации сообщений об ошибках


            Данная подсистема обладает следующими функциями:

            • прием сообщений от автотестов;
            • парсинг стандартных отчетов: Allure для API-тестов и Serenity для UI-тестов;
            • агрегация сообщений методом нечеткого поиска. В качестве основы был выбран алгоритм поиска нечетких дубликатов текстов (shingles);
            • автоматическое заведение шаблонов ошибок (обучение без учителя).


            Подсистема принятия решений


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

            Скриншоты


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

            Видеозаписи прохождения тестов


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

            БД


            Каждая из подсистем ЕФС логирует данные в специальную БД. В ходе анализа шага автотеста на основе маркера (сессия пользователя) записи из БД Журналирование копируются в БД ULA.

            Плоские текстовые файлы с системными логами


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

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

            Подсистема оповещений


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

            К примеру, недоступность сервисов на уровне TCP, ошибки HTTP (404, 500) и прочие проблемы, требующие быстрой реакции администратора. Сейчас прорабатывается задача автоматического заведения инцидентов на тестовой среде.

            Опишем в упрощенном виде реализованные в системе шаги алгоритма поиска дубликатов и агрегации.

            • Шаг 1. Канонизация текста.

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

            Пример канонизированного текста:
            «сумма на счете не изменилась на указанную сумму ожидаемая сумма на счете 173,40 euro баланс после пополнения 173,40 euro»

            • Шаг 2. Разделение на словосочетания (shingles).

            Разделение проводится с шагом в одно слово. Количество слов в словосочетании называется длиной shingle.

            Набор shingles с длиной 5:
            «сумма на счете не изменилась»; «на счете не изменилась на»; «счете не изменилась на указанную»; «не изменилась на указанную сумму»; «изменилась на указанную сумму ожидаемая»; «на указанную сумму ожидаемая сумма»; «указанную сумму ожидаемая сумма на»; «сумму ожидаемая сумма на счете».

            • Шаг 3. Вычисление хэшей shingles по алгоритму MD5.  

            Полученный набор хэшей текста хранится во временной таблице до окончания сравнения с хэшами shingles всех шаблонов ошибок, заведенных в системе.

            Для каждого набора shingles шаблонов ошибок вычисляется степень схожести по набору хэшей.
            SIMILARITY(i) = SIMCNT * 2 / (TSCNT + THCNT(i)),
            где SIMCNT – количество совпавших уникальных хэшей в двух наборах,  TSCNT – количество уникальных хэшей анализируемого текста, THCNT(i) – количество уникальных хэшей шаблона i.

            • Шаг 4. Выбирается подходящий шаблон ошибки.

            Ищется SIMILARITY = MAX(SIMILARITY(i)).
            Если SIMILARITY больше или равно заданному порогу схожести, то тексту проставляется существующий идентификатор шаблона.

            Если SIMILARITY меньше порога схожести, то канонизированный текст сам становится шаблоном, а набор хэшей shingles записывается в БД.

            • Заключительный этап.

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

            Поговорим об аналогах


            Справедливый вопрос – зачем вы писали систему сами, когда на рынке уже имеется аналогичный продукт, например, Report Portal от компании EPAM.

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

            Однако есть разница в подходах, для удобства сравнения мы представили это в таблице.

            ReportPortal
            Unified Logfile Analyzer
            Акцент на анализе логов самих автотестов, в которых также могут быть дефекты, включая дефекты логирования
            Указывает пользователю на подозрительные автотесты, когда тест помечен как успешно пройденный, но по нему имеются ошибки,  или,  наоборот, когда тест провален, но никаких ошибок не зарегистрировано
            В основе алгоритма определения схожести используется расстояние Левенштейна
            Нам не подходит данный алгоритм, так как на длинных словах расстояние получается существенным
            В своем решении используем алгоритм Shingles (http://ethen8181.github.io/machine-learning/clustering_old/text_similarity/text_similarity.html)
            В Report Portal мы пока не увидели этой возможности. Возможно, в будущем появится.
            Информация от автотестов (тексты ошибок, скриншоты, видеозапись теста) обогащается информацией из аппликативной части самих систем (файлы, БД), что улучшает качество анализа
            Большое внимание уделено отчетности: графики, разнообразная статистика
            Функционал отчетности планируем реализовать отдельно
            Не предусмотрена интеграция с HP ALM
            Есть интеграция с HP ALM, для нас это важно
            Используется нереляционная база данных MongoDB.  Спорить на эту тему можно долго
            По нашему мнению, решение на Oracle 11g будет вести себя более предсказуемо в части потребления ресурсов


            Unified Logfile Analyzer – система, которую мы собрали с нуля, разработали ее, учитывая свой опыт, опыт коллег, проанализировав существующие на рынке решения. Система самообучаемаемая, помогает нам быстрее находить и точно исправлять баги — куда без них.
            Сейчас мы запускаем ULA в продуктив, будем раскатывать на продуктах и сервисах Программы ЕФС. В следующем посте расскажем о первых результатах и поделимся кейсами.

            Будем рады обсудить решение и поспорить о подходах, поделитесь опытом и кейсами в комментариях!
            Original source: habrahabr.ru (comments, light).

            https://habrahabr.ru/post/338164/


            Метки:  

            Сheat-sheets «регулярные выражения»

            Пятница, 22 Сентября 2017 г. 11:34 + в цитатник
            FirstJohn сегодня в 11:34 Администрирование

            Сheat-sheets «регулярные выражения»

              Ловите 2 плаката с регулярными выражениями в форматах A2 и A3.

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


              Скачать плакат A2


              Скачать плакат A3


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



              Хотели сделать для вас ещё обоину на рабочий стол, “домики” на письменный и куб, который нужно склеить самому. Но все перессорились в спорах об удобстве.

              Расскажите, вы пользуетесь плакатами или предпочитаете cheat-sheet в другом формате? И какие регулярные выражения юзаете?
              Original source: habrahabr.ru (comments, light).

              https://habrahabr.ru/post/338386/


              Метки:  

              [Перевод] Как работает видеопроцессор

              Пятница, 22 Сентября 2017 г. 11:27 + в цитатник
              PatientZero сегодня в 11:27 Разработка

              Как работает видеопроцессор

              • Перевод
              image

              [Прим. пер.: оригинал статьи называется GPU Performance for Game Artists, но, как мне кажется, она будет полезной для всех, кто хочет иметь общее представление о работе видеопроцессора]

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

              Мы надеемся, что художники создадут ресурсы, которые не только хорошо выглядят, но и будут эффективны при рендеринге. Если художники немного больше узнают о том, что происходит внутри видеопроцессора, это может оказать большое влияние на частоту кадров игры. Если вы художник и хотите понять, почему для производительности важны такие аспекты, как вызовы отрисовки (draw calls), уровни детализации (LOD) и MIP-текстуры, то прочитайте эту статью. Чтобы учитывать то влияние, которое имеют ваши графические ресурсы на производительность игры, вы должны знать, как полигональные сетки попадают из 3D-редактора на игровой экран. Это значит, что вам нужно понять работу видеопроцессора, микросхемы, управляющей графической картой и несущей ответственность за трёхмерный рендеринг в реальном времени. Вооружённые этим знанием, мы рассмотрим наиболее частые проблемы с производительностью, разберём, почему они являются проблемой, и объясним, как с ними справиться.

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

              Часть 1: конвейер рендеринга с высоты птичьего полёта


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

              После экспорта сетки из 3D-редактора (Maya, Max и т.д.) геометрия обычно загружается в движок игры двумя частями: буфером вершин (Vertex Buffer, VB), содержащим список вершин сетки со связанными с ними свойствами (положение, UV-координаты, нормаль, цвет и т.д.), и буфером индексов (Index Buffer, IB), в котором перечислены вершины из VB, соединённые в треугольники.

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

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

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

              image

              • Входная сборка (Input Assembly). Видеопроцессор считывает буферы вершин и индексов из памяти, определяет как соединены образующие треугольники вершины и передаёт остальное в конвейер.
              • Затенение вершин (Vertex Shading). Вершинный шейдер выполняется для каждой из вершин сетки, обрабатывая по отдельной вершине за раз. Его основная задача — преобразовать вершину, получить её положение и использовать текущие настройки камеры и области просмотра для вычисления её расположения на экране.
              • Растеризация (Rasterization). После того, как вершинный шейдер выполнен для каждой вершины треугольника и видеопроцессор знает, где она появится на экране, треугольник растеризируется — преобразуется в набор отдельных пикселей. Значения каждой вершины — UV-координаты, цвет вершины, нормаль и т.д. — интерполируются по пикселям треугольника. Поэтому если одна вершина треугольника имеет чёрный цвет, а другая — белый, то пиксель, растеризированный посередине между ними получит интерполированный серый цвет вершин.
              • Затенение пикселей (Pixel Shading). Затем для каждого растеризированного пикселя выполняется пиксельный шейдер (хотя технически на этом этапе это ещё не пиксель, а «фрагмент», поэтому иногда пиксельный шейдер называют фрагментным). Этот шейдер запрограммированным образом придаёт пикселю цвет, сочетая свойства материала, текстуры, источники освещения и другие параметры, чтобы получить определённый внешний вид. Пикселей очень много (целевой рендер с разрешением 1080p содержит больше двух миллионов), и каждый из них нужно затенить хотя бы раз, поэтому обычно видеопроцессор тратит на пиксельный шейдер много времени.
              • Вывод целевого рендера (Render Target Output). Наконец пиксель записывается в целевой рендер, но перед этим проходит некоторые проверки, чтобы убедиться в его правильности. Глубинный тест отбрасывает пиксели, которые находятся глубже, чем пиксель, уже присутствующий в целевом рендере. Но если пиксель проходит все проверки (глубины, альфа-канала, трафарета и т.д.), он записывается в хранящийся в памяти целевой рендер.

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

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

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

              float3 MaterialColor; 
              Texture2D MaterialTexture; 
              SamplerState TexSampler; 
              
              float3 LightDirection; 
              float3 LightColor; 
              
              float4 MyPixelShader( float2 vUV : TEXCOORD0, float3 vNorm : NORMAL0 ) : SV_Target 
              { 
                 float3 vertexNormal = normalize(vNorm);  
                 float3 lighting = LightColor * dot( vertexNormal, LightDirection ); 
                 float3 material = MaterialColor * MaterialTexture.Sample( TexSampler, vUV ).rgb; 
              
                 float3 color = material * lighting; 
                 float alpha = 1; return float4(color, alpha); 
              }

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

              Вот сгенерированные инструкции шейдера:

              dp3 r0.x, v1.xyzx, v1.xyzx 
              rsq r0.x, r0.x 
              mul r0.xyz, r0.xxxx, v1.xyzx 
              dp3 r0.x, r0.xyzx, cb0[1].xyzx 
              mul r0.xyz, r0.xxxx, cb0[2].xyzx 
              sample_indexable(texture2d)(float,float,float,float) r1.xyz, v0.xyxx, t0.xyzw, s0 
              mul r1.xyz, r1.xyzx, cb0[0].xyzx 
              mul o0.xyz, r0.xyzx, r1.xyzx 
              mov o0.w, l(1.000000) 
              ret

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

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

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

              Центральный процессор и вызовы отрисовки


              Видеопроцессор не может работать в одиночку: он зависит от кода игры, запущенного в главном процессоре компьютера — ЦП, который сообщает ему, что и как рендерить. Центральный процессор и видеопроцессор — это (обычно) отдельные микросхемы, работающие независимо и параллельно. Чтобы получить необходимую частоту кадров — обычно это 30 кадров в секунду — и ЦП, и видеопроцессор должны выполнить всю работу по созданию одного кадра за допустимое время (при 30fps это всего 33 миллисекунд на кадр).

              image


              Чтобы добиться этого, кадры часто выстраиваются в конвейер: ЦП занимает для своей работы весь кадр (обрабатывает ИИ, физику, ввод пользователя, анимации и т.д.), а затем отправляет инструкции видеопроцессору в конце кадра, чтобы тот мог приняться за работу в следующем кадре. Это даёт каждому из процессоров полные 33 миллисекунды для выполнения работы, но ценой этому оказывается добавление латентности (задержки) длиной в кадр. Это может быть проблемой для очень чувствительных ко времени игр, допустим, для шутеров от первого лица — серия Call of Duty, например, работает с частотой 60fps для снижения задержки между вводом игрока и рендерингом — но обычно лишний кадр игрок не замечает.

              Каждые 33 мс конечный целевой рендер копируется и отображается на экране во VSync — интервал, в течение которого ищет новый кадр для отображения. Но если видеопроцессору требуется для рендеринга кадра больше, чем 33 мс, то он пропускает это окно возможностей и монитору не достаётся нового кадра для отображения. Это приводит к мерцанию или паузам на экране и снижению частоты кадров, которого нужно избегать. Тот же результат получается, если слишком много времени занимает работа ЦП — это приводит к эффекту пропуска, потому что видеопроцессор не получает команды достаточно быстро, чтобы выполнить свою работу в допустимое время. Если вкратце, то стабильная частота кадров зависит от хорошей производительности обоих процессоров: центрального процессора и видеопроцессора.

              image

              Здесь создание команд рендеринга у ЦП заняло слишком много времени для второго кадра, поэтому видеопроцессор начинает рендеринг позже и пропускает VSync.

              Для отображения сетки ЦП создаёт вызов отрисовки, который является простой последовательностью команд, сообщающей видеопроцессору, что и как отрисовывать. В процессе прохождения вызова отрисовки по конвейеру видеопроцессора он использует различные конфигурируемые настройки, указанные в вызове отрисовки (в основном задаваемые материалом и параметрами сетки) для определения того, как рендерится сетка. Эти настройки, называемые состоянием видеопроцессора (GPU state), влияют на все аспекты рендеринга и состоят из всего, что нужно знать видеопроцессору для рендеринга объекта. Наиболее важно для нас то, что видеопроцессор содержит текущие буферы вершин/индексов, текущие программы вершинных/пиксельных шейдеров и все входные данные шейдеров (например, MaterialTexture или LightColor из приведённого выше примера кода шейдера).

              Это означает, что для изменения элемента состояния видеопроцессора (например, для замены текстуры или переключения шейдеров), необходимо создать новый вызов отрисовки. Это важно, потому что эти вызовы отрисовки затратны для видеопроцессора. Необходимо время на задание нужных изменений состояния видеопроцессора, а затем на создание вызова отрисовки. Кроме той работы, которую движку игры нужно выполнять при каждом вызове отрисовки, существуют ещё затраты на дополнительную проверку ошибок и хранение промежуточных результатов. добавляемые графическим драйвером. Это промежуточный слой кода. написанный производителем видеопроцессора (NVIDIA, AMD etc.), преобразующий вызов отрисовки в низкоуровневые аппаратные инструкции. Слишком большое количество вызовов отрисовки ложится тяжёлой ношей на ЦП и приводит к серьёзным проблемам с производительностью.

              Из-за этой нагрузки обычно приходится устанавливать верхний предел допустимого количества вызовов отрисовки на кадр. Если во время тестирования геймплея этот предел превышается, то необходимо предпринять шаги по уменьшению количества объектов, снижению глубины отрисовки и т.д. В играх для консолей количество вызовов отрисовки обычно ограничивается интервалом 2000-3000 (например, для Far Cry Primal мы стремились, чтобы их было не больше 2500 на кадр). Это кажется большим числом, но в него также включены специальные техники рендеринга — каскадные тени, например, запросто могут удвоить количество вызовов отрисовки в кадре.

              Как упомянуто выше, состояние видеопроцессора можно изменить только созданием нового вызова отрисовки. Это значит, что даже если вы создали единую сетку в 3D-редакторе, но в одной половине сетки используется одна текстура для карты albedo, а в другой половине — другая текстура, то сетка будет рендериться как два отдельных вызова отрисовки. То же самое справедливо, когда сетка состоит из нескольких материалов: необходимо использовать разные шейдеры, то есть создавать несколько вызовов отрисовки.

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

              Чтобы избежать этого, часто применяют следующее решение — объединяют все текстурные карты, используемые сеткой, в одну большую текстуру, часто называемую атласом. Затем UV-координаты сетки настраиваются таким образом, чтобы они искали нужные части атласа, при этом всю сетку (или даже несколько сеток) можно отрендерить за один вызов отрисовки. При создании атласа нужно быть аккуратным, чтобы при низких MIP-уровнях соседние текстуры не накладывались друг на друга, но эти проблемы менее серьёзны, чем преимущества такого подхода для обеспечения скорости.

              image

              Текстурный атлас из демо Infiltrator движка Unreal Engine

              Многие движки поддерживают клонирование (instancing), также известное как батчинг (batching) или кластеризация (clustering). Это способность использовать один вызов отрисовки для рендеринга нескольких объектов, которые практически одинаковы с точки зрения шейдеров и состояния, и различия в которых ограничены (обычно это их положение и поворот в мире). Обычно движок понимает, когда можно отрендерить несколько одинаковых объектов с помощью клонирования, поэтому по возможности всегда стоит стремиться использовать в сцене один объект несколько раз, а не несколько разных объектов, которые придётся рендерить в отдельных вызовах отрисовки.

              Ещё одна популярная техника снижения числа вызовов отрисовки — ручное объединение (merging) нескольких разных объектов с одинаковым материалом в одну сетку. Оно может быть эффективно, но следует избегать чрезмерного объединения, которое может ухудшить производительность, увеличив количество работы для видеопроцессора. Ещё до создания вызовов отрисовки система видимости движка может определить, находится ли вообще объект на экране. Если нет, то гораздо менее затратно просто пропустить его на этом начальном этапе и не тратить на него вызовы отрисовки и время видеопроцессора (эта техника также известна как отсечение по видимости (visibility culling)). Этот способ обычно реализуется проверкой видимости ограничивающего объект объёма с точки обзора камеры и проверкой того, не блокируется ли он полностью (occluded) в области видимости другими объектами.

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

              image

              Кадр из XCOM 2, сделанный в RenderDoc. На каркасном виде (снизу) серым показана вся лишняя геометрия, передаваемая в видеопроцессор и находящаяся за пределами области видимости игровой камеры.

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

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

              Однако всё меняется, когда дело доходит до затрат на вызовы отрисовки. Как сказано выше, важная причина этих затрат — дополнительная нагрузка, создаваемая драйвером при преобразовании и проверке ошибок. Это было проблемой очень долго, но у большинства современных графических API (например, Direct3D 12 и Vulkan) структура изменена таким образом, чтобы избежать лишней работы. Хоть это и добавляет сложности движку рендеринга игры, однако приводит к менее затратным вызовам отрисовки, что позволяет нам рендерить гораздо больше объектов, чем было возможно раньше. Некоторые движки (наиболее заметный из них — последняя версия движка Assassin's Creed) даже пошли совершенно в другом направлении и используют возможности современных видеопроцессоров для управления рендерингом и эффективного избавления от вызовов отрисовки.

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

              Часть 2: обычные «бутылочные горлышки» видеопроцессора


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

              image

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

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

              image


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

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

              Профилирование


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

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

              image

              Базовый встроеннный профилировщик видеопроцессора движка Unity

              Для PC есть довольно неплохие (хотя и специфичные для оборудования) инструменты профилирования, которые можно получить от производителей видеопроцессоров, например Nsight компании NVIDIA, GPU PerfStudio компании AMD и GPA Intel. Кроме того, существует RenderDoc — лучший инструмент для отладки графики на PC, но в нём нет функций расширенного профилирования. Microsoft приступает к выпуску своего потрясающего инструмента для профилирования Xbox PIX и под Windows, хоть только для приложений D3D12. Если предположить, что компания хочет создать такие же инструменты анализа «бутылочных горлышек», что и в версии для Xbox (а это сложно, учитывая огромное разнообразие оборудования), то это будет отличный ресурс для разработчиков на PC.

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

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

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

              Инструкции шейдеров


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

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

              Неудивительно, что наилучший способ оптимизации «бутылочных горлышек» в инструкциях шейдеров — выполнение меньшего количества инструкций! Для пиксельных шейдеров это означает, что нужно выбрать более простой материал с меньшим количеством характеристик, чтобы снизить число инструкций, выполняемых на пиксель. Для вершинных шейдеров это означает, что нужно упросить сетку для уменьшения количества обрабатываемых вершин, а также использовать LOD (Level Of Detail, уровни детализации — упрощённые версии сетки, используемые, когда объект находится далеко и занимает на экране мало места).

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

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

              image
              Кадр игры PIX с режимом визуализации перерисовки

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

              Видеопроцессор предпринимает шаги по снижению перерисовки непрозрачных объектов. Начальный тест глубины (early depth test) (который выполняется перед пиксельным шейдером — см. схему конвейера в начале статьи) пропускает затенение пикселей, если определяет, что пиксель скрыт за другим объектом. Для этого он сравнивает затеняемый пиксель с буфером глубины (depth buffer) — целевым рендером, в котором видеопроцессор хранит глубину всего кадра, чтобы объекты могли правильно перекрывать друг друга. Но чтобы начальный тест глубины был эффективным, другой объект должен попасть в буфер глубины, то есть быть полностью отрендеренным. Это значит, что очень важен порядок рендеринга объектов.

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

              Чтобы помочь начальному тесту глубины, некоторые игры выполняют частичный предварительный проход глубины (depth prepass). Это подготовительный проход, при котором некоторые большие объекты, способные эффективно перекрывать другие объекты (большие здания, рельеф, главный герой и т.д.), рендерятся простым шейдером, который выполняет вывод только в буфер глубин, что относительно быстро, потому что при этом не выполняется работа пиксельного шейдера по освещению и текстурированию. Это «улучшает» буфер глубин и увеличивает объём работы пиксельных шейдеров, который можно пропустить на проходе полного рендеринга. Недостаток такого подхода в том, что двойной рендеринг перекрывающих объектов (сначала в проходе только для глубин, а потом в основном проходе) увеличивает количество вызовов отрисовки, плюс всегда существует вероятность того, что время на рендеринг прохода глубин окажется больше, чем время, сэкономленное на повышении эффективности начального теста глубин. Только подробное профилирование позволяет определить, стоит ли такой подход использования в конкретной сцене.


              Визуалиация перерисовки частиц взрыва в Prototype 2

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

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


              Инструмент «вырезания» частиц в Unreal Engine 4

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

              Очень близким по воздействию к перерисовке является излишнее затенение (overshading), причиной которого становятся мелкие или тонкие треугольники. Оно очень сильно может вредить производительности, напрасно тратя значительную часть времени видеопроцессора. Излишнее затенение — это последствие того, как видеопроцессор обрабатывает пиксели при затенении пикселей: не по одному за раз, а «квадами» (quads). Это блоки из четырёх пикселей, выстроенные квадратом 2x2. Так делается затем, чтобы оборудование могло справляться с такими задачами, как сравнение UV между пикселями для вычисления подходящих уровней MIP-текстурирования.

              Это значит, что если треугольник касается только одной точки квада (потому что треугольник мал или очень тонок), видеопроцессор всё равно обрабатывает весь квад и просто отбрасывает остальные три пикселя, впустую тратя 75% работы. Это растраченное время может накапливаться и особенно чувствительно для прямых (т.е. не отложенных) рендереров, выполняющих расчёт освещения и затенения за один проход в пиксельном шейдере. Такую нагрузку можно снизить использованием правильно настроенных LOD; кроме экономии на обработке вершинных шейдеров, они также значительно снижают количество излишнего затенения тем, что в среднем треугольники закрывают большую часть каждого из квадов.


              Пиксельный буфер 10x8 с квадами 5x4. Два треугольника плохо используют квады — левый слишком маленький, правый слишком тонкий. 10 красных квадов, которых касаются треугольники, должны быть полностью затенены, даже несмотря на то что на самом деле затенения требуют только 12 зелёных пикселей. В целом 70% работы видеопроцессора тратится впустую.

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

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

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

              Полоса пропускания памяти и текстуры


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

              Доступ к памяти аналогичен скачиванию файлов из Интернета. На скачивание файла требуется какое-то время, зависящее от полосы пропускания (bandwidth) Интернет-подключения — скорости, с которой могут передаваться данные. Эта полоса пропускания общая для всех загрузок — если вы можете скачать один файл со скоростью 6МБ/с, два файла будут скачиваться со скоростью 3МБ/с каждый.

              Это справедливо и для доступа к памяти: на доступ видеопроцессора к буферам индексов/вершин и текстурам требуется время, при этом скорость ограничена полосой пропускания памяти. Очевидно, что скорости намного быстрее, чем при Интернет-подключении — теоретически полоса пропускания видеопроцессора PS4 равна 176ГБ/с — но принцип остаётся тем же. Шейдер, обращающийся к нескольким текстурам, сильно зависит от полосы пропускания, ведь ему нужно передать все нужные данные вовремя.

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

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

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

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

              Теперь представьте, что произойдёт в кэше, если вы пытаетесь отобразить большую текстуру (например, 2048x2048) на объекте, который находится очень далеко и занимает на экране всего несколько пикселей. Каждый пиксель придётся получать из совершенно отдельной части текстуры, и кэш в этом случае будет полностью неэффективен, потому что он хранит только текселы, близкие к полученным при предыдущих доступах. Каждая операция доступа к текстуре пытается найти свой результат в кэше, но ей это не удаётся (так называемый «промах кэша» (cache miss)), поэтому данные необходимо получать из памяти, то есть тратиться дважды: занимать полосу пропускания и тратить время на передачу данных. Может возникнуть задержка, замедляющая весь шейдер. Это также может привести к тому, что другие (потенциально полезные) данные будут удалены из кэша, чтобы освободить место для соседних текселов, которые никогда не пригодятся, что снижает общую эффективность кэша. Это во всех отношениях плохие новости, не говоря уже о проблемах с качеством графики — небольшие движения камеры приводят к сэмплированию совершенно других текселов, в результате чего возникают искажения и мерцание.

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


              Текстура на двух квадах, один из который близко к камере, а другой гораздо дальше


              Та же текстура с соответствующей цепочкой MIP-текстур, каждая из которых в два раза меньше предыдущей

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

              Вся изложенная выше информация должна привесли к каким-то очевидным шагам по снижению или устранению «заторов» полосы пропускания при оптимизации текстур с точки зрения художников. Следует убедиться, что текстуры имеют MIP и что они сжаты. Не нужно использовать «тяжёлую» анизотропную фильтрацию 8x или 16x, если достаточно 2x, или даже трилинейной/билинейной фильтрации. Снижайте разрешение текстур, особенно если на экране часто отображаются MIP-текстуры максимальной детализации. Не используйте без необходимости функции материалов, требующие доступа к текстурам. И проверяйте, что все получаемые данные на самом деле используются — не сэмплируйте четыре текстуры RGBA, если в реальности вам необходимы данные только красного канала, объедините эти четыре канала в одну текстуру, избавившись от 75% нагрузки на полосу пропускания.

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

              При стандартном рендеринге все эти затраты обычно незаметны, потому что по сравнению с данными текстур этот объём данных относительно мал, но так бывает не всегда. В отличие от обычных вызовов отрисовки поведение проходов теней (shadow passes) довольно сильно отличается, и они с гораздо большей вероятностью могут ограничивать полосу пропускания.


              Кадр из GTA V с картами теней, иллюстрация взята из отличного анализа кадра Адриана Корреже (оригинал статьи)

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

              Последнее, о чём стоит упомянуть в отношении полосы пропускания памяти — это особый случай — Xbox. И у Xbox 360, и у Xbox One есть особая часть памяти, расположенная близко к видеопроцессору, называющаяся на 360 EDRAM, а на XB1 ESRAM. Это относительно небольшой объём памяти (10 МБ на 360 и 32 МБ на XB1), но он достаточно велик, чтобы хранить несколько целевых рендеров, и может быть некоторые часто используемые текстуры. При этом его полоса пропускания гораздо выше, чем у стандартной системной памяти (DRAM). Важна не только скорость, но и то, что эта полоса пропускания имеет свой канал, то есть не связана с передачами DRAM. Это добавляет сложности движку, но при эффективном использовании даёт дополнительный простор в ситуациях с ограничениями полосы пропускания. Художники обычно не имеют контроля над тем, что записывается в EDRAM/ESRAM, но стоит знать о них, когда дело доходит до профилирования. Подробнее об особенностях реализации в вашем движке вы можете узнать у 3D-программистов.

              И многое другое...


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

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

              Из статьи можно многое почерпнуть, но не забывайте, что 3D-программисты вашей команды всегда готовы встретиться с вами и обсудить всё то, что требует глубокого объяснения.

              Другие технические статьи



              Примечание: эта статья изначально опубликована на fragmentbuffer.com её автором Кейтом О'Конором (Keith O'Conor). Другие заметки Кейта можно прочитать в его твиттере (@keithoconor).
              Original source: habrahabr.ru (comments, light).

              https://habrahabr.ru/post/337484/


              Метки:  

              Нужны ли вам данные о финансах?

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

              Нужны ли вам данные о финансах?


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

                Открытость и качество финансовых данных


                Из всех открытых данных в России финансовые данные сейчас едва ли не самые открытые. Госорганы действительно пытаются повышать финансовую открытость — как свою собственную, так и подведомственных структур. Например, Минфин России организует для этого разные проекты и мероприятия. В частности, он с 2015 г. проводит ежегодный конкурс BudgetApps (http://www.budgetapps.ru/contest) для разработчиков и журналистов. Также можно вспомнить такие его начинания, как Московский финансовый форум и проекты "Финансовая грамотность" и "Бюджет для граждан".

                Но главное — это то, что в больших количествах раскрываются интересные данные. Огромная база данных по государственной контрактной системе публикуется Федеральным Казначейством посредством Единой информационной системы. Там можно найти данные о закупках, контрактах, заказчиках, поставщиках, планах закупок, банковских гарантиях и прочих сторонах закупочной деятельности, которая подлежит раскрытию в соответствии с 44-ФЗ и 223-ФЗ. Раскрыты реестры субсидий и участников и неучастников бюджетного процесса, они публикуются на Budget.gov.ru. Публикуется федеральный бюджет, пусть не всегда в идеальных, но все же пригодных для использования форматах.

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

                В чем смысл таких исследований


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

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

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

                Зачем участвовать в опросе


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

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

                Будем благодарны, если вы ответите на наши вопросы здесь: https://ru.surveymonkey.com/r/BVKG6B2
                Original source: habrahabr.ru (comments, light).

                https://habrahabr.ru/post/338468/


                Метки:  

                [Из песочницы] Области видимости и замыкания в JavaScript

                Пятница, 22 Сентября 2017 г. 11:00 + в цитатник
                NarekPK сегодня в 11:00 Разработка

                Области видимости и замыкания в JavaScript

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


                Начнем с областей видимости


                Область видимости


                Область видимости в JavaScript определяет, какие переменные доступны вам. Существуют два типа областей видимости: глобальная и локальная.


                Глобальная область видимости


                Если переменная объявлена вне всех функций или фигурных скобок ({}), то считается, что она определена в глобальной области видимости.


                Примечание: это верно только для JavaScript в веб браузерах. В Node.js глобальные переменные объявляются иначе, но мы не будем касаться Node.js в этой статье.


                const globalVariable = 'some value';
                

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


                const hello = 'Hello CSS-Tricks Reader!';
                
                function sayHello () {
                  console.log(hello);
                }
                
                console.log(hello); // 'Hello CSS-Tricks Reader!'
                sayHello(); // 'Hello CSS-Tricks Reader!'
                

                Хотя можно объявлять переменные в глобальной области видимости, но не рекомендуется это делать. Всё из-за того, что существует вероятность пересечения имен, когда двум или более переменным присваивают одинаковое имя. Если переменные объявляются через const или let, то каждый раз, когда будет происходить пересечение имён, будет показываться сообщение об ошибке. Такое поведение нежелательно.


                // Не делайте так!
                let thing = 'something';
                let thing = 'something else'; // Ошибка, thing уже была объявлена
                

                Если объявлять переменные через var, то вторая переменная после объявления перепишет первую. Такое поведение тоже нежелательно, т.к. код усложняется в отладке.


                // Не делайте так!
                var thing = 'something';
                var thing = 'something else'; // возможно где-то в коде у переменной совершенно другое значение
                console.log(thing); // 'something else'
                

                Итак, следует всегда объявлять локальные переменные, а не глобальные.


                Локальная область видимости


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


                В JavaScript выделяют два типа локальных областей видимости:


                • область видимости функции
                • и область видимости блока.

                Сначала рассмотрим область видимости функции


                Область видимости функции


                Переменная, объявленная внутри функции, доступна только внутри функции. Код снаружи функции не имеет к ней доступа.


                В примере ниже, переменная hello находится внутри области видимости функции sayHello:


                function sayHello () {
                  const hello = 'Hello CSS-Tricks Reader!';
                  console.log(hello);
                }
                
                sayHello(); // 'Hello CSS-Tricks Reader!'
                console.log(hello); // Ошибка, hello не определена
                

                Область видимости блока


                Переменная, объявленная внутри фигурных скобок {} через const или let, доступна только внутри фигурных скобок.


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


                {
                  const hello = 'Hello CSS-Tricks Reader!';
                  console.log(hello); // 'Hello CSS-Tricks Reader!'
                }
                
                console.log(hello); // Ошибка, hello не определена
                

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


                Подъем функции в области видимости


                Функции, объявленные как «function declaration» (прим. перев.: функция вида function имя(параметры) {...}), всегда поднимаются наверх в текущей области видимости. Так, два примера ниже эквивалентны:


                // Тоже самое, что пример ниже
                sayHello();
                function sayHello () {
                  console.log('Hello CSS-Tricks Reader!');
                }
                
                // Тоже самое, что пример выше
                function sayHello () {
                  console.log('Hello CSS-Tricks Reader!');
                }
                sayHello();
                

                Если же функция объявляется как «function expression» (функциональное выражение) (прим. перев.: функция вида var f = function (параметры) {...}), то такая функция не поднимается в текущей области видимости.


                sayHello(); // Ошибка, sayHello не определена
                const sayHello = function () {
                  console.log(aFunction);
                }
                

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


                У функций нет доступа к областям видимости других функций


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


                В примере ниже функция second не имеет доступа к переменной firstFunctionVariable.


                function first () {
                  const firstFunctionVariable = `I'm part of first`;
                }
                
                function second () {
                  first();
                  console.log(firstFunctionVariable); // Ошибка, firstFunctionVariable не определена.
                }
                

                Вложенные области видимости


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


                В тоже время внешняя функция не имеет доступа к переменным внутренней функции.


                function outerFunction () {
                  const outer = `I'm the outer function!`;
                
                  function innerFunction() {
                    const inner = `I'm the inner function!`;
                    console.log(outer); // I'm the outer function!
                  }
                
                  console.log(inner); // Ошибка, inner не определена
                }
                

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



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



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


                Замыкания


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


                function outerFunction () {
                  const outer = `I see the outer variable!`;
                
                  function innerFunction() {
                    console.log(outer);
                  }
                
                  return innerFunction;
                }
                
                outerFunction()(); // I see the outer variable!
                

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


                function outerFunction () {
                  const outer = `I see the outer variable!`;
                
                  return function innerFunction() {
                    console.log(outer);
                  }
                }
                
                outerFunction()(); // I see the outer variable!
                

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


                1. контроля побочных эффектов;
                2. создания приватных переменных.

                Контроль побочных эффектов с помощью замыканий


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


                function (x) {
                  console.log('A console.log is a side effect!');
                }
                

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


                Для пояснения рассмотрим пример


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


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


                function makeCake() {
                  setTimeout(_ => console.log(`Made a cake`), 1000);
                }
                

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


                Далее допустим, вашему другу нужно выбрать вкус торта. Для этого нужно дописать «добавить вкус» к функции makeCake.


                function makeCake(flavor) {
                  setTimeout(_ => console.log(`Made a ${flavor} cake!`, 1000));
                }
                

                После вызова функции торт будет испечён ровно через секунду.


                makeCake('banana'); // Made a banana cake!
                

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


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


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


                function prepareCake (flavor) {
                  return function () {
                    setTimeout(_ => console.log(`Made a ${flavor} cake!`, 1000));
                  }
                }
                
                const makeCakeLater = prepareCake('banana');
                
                // Позже в вашем коде...
                makeCakeLater(); // Made a banana cake!
                

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


                Приватные переменные с замыканиями


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


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


                function secret (secretCode) {
                  return {
                    saySecretCode () {
                      console.log(secretCode);
                    }
                  }
                }
                
                const theSecret = secret('CSS Tricks is amazing');
                theSecret.saySecretCode(); // 'CSS Tricks is amazing'
                

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


                Отладка областей видимости с помощью DevTools


                Инструменты разработчика (DevTools) Chrome и Firefox упрощают отлаживание переменных в текущей области видимости. Существует два способа применения этого функционала.


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


                Ниже пример с prepareCake:


                function prepareCake (flavor) {
                  // Добавляем debugger
                  debugger
                  return function () {
                    setTimeout(_ => console.log(`Made a ${flavor} cake!`, 1000));
                  }
                }
                
                const makeCakeLater = prepareCake('banana');
                

                Если открыть DevTools и перейти во вкладку Sources в Chrome (или вкладку Debugger в Firefox), то можно увидеть доступные переменные.



                Можно также переместить debugger внутрь замыкания. Обратите внимание, как переменные области видимости изменяться в этот раз:


                function prepareCake (flavor) {
                  return function () {
                    // Добавляем debugger
                    debugger
                    setTimeout(_ => console.log(`Made a ${flavor} cake!`, 1000));
                  }
                }
                
                const makeCakeLater = prepareCake('banana');
                


                Второй способ: добавлять брейкпоинт напрямую в код во вкладке Sources (или Debugger) путем клика на номер строки.



                Выводы:


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

                https://habrahabr.ru/post/338462/


                Метки:  

                Гейм-дев конференция 4C — день 1

                Пятница, 22 Сентября 2017 г. 10:49 + в цитатник

                Метки:  

                Cила PostgreSQL

                Пятница, 22 Сентября 2017 г. 10:36 + в цитатник
                rubyruby сегодня в 10:36 Разработка

                Cила PostgreSQL


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

                  Задача


                  Поиск отелей с доступными номерами на конкретные даты некоторой группой людей.

                  Проект


                  Когда проект попал к нам в руки, поиск уже был реализован. Он работал медленно, очень медленно. А все потому, что расчеты и выборки велись не на стороне базы данных, а на стороне web-приложения: выбиралась тонна записей из разных таблиц, и в циклах подбирались и рассчитывались номера, фильтровались, сортировались и выводились постранично. Очень неэффективно. А приложение, к слову, написано на Ruby on Rails.
                  Не надо так.

                  Исходные данные


                  Исходная схема данных (в примерах искусственно упрощена, чтобы влезть в ограничения sqlfiddle)


                  Places — направления, курорты. Из полей — только название.


                  Districts — районы. Каждое направление может иметь несколько районов. Поля: название и id направления.


                  Properties — отели, могут быть привязаны к направлению или к конкретному району. Поля:
                  • name — название
                  • dest_type — тип полиморфной связи с направлением или районом («Place» или «District»)
                  • dest_id — id связи с направлением или районом
                  • stars — звездность (от 0 до 5)
                  • currency — код валюты


                  Property_arrival_rules — правила въезда в каждый отель. Поля:
                  • arrival_date — дата заезда
                  • property_id — id отеля
                  • rule — тип правила (0 или 1), в зависимости от типа по разному рассчитывается дата выезда, подробнее в решении ниже
                  • min_stay — минимальное количество ночей для проживания

                  Отсутствие записи в таблице на конкретную дату означает, что въезд в этот день невозможен. Зачем хранится так? Все дело в типах правил въезда. Подробнее об этих типах в решении ниже.


                  Rooms — номера в отелях, точнее типы номеров, т.к. например, 2-х комнатных одинаковых номеров может быть несколько в одном отеле. Поля: название и id отеля.


                  Room_availabilities — доступность номера на каждую ночь. Поля:
                  • room_id — id номера
                  • date — дата
                  • initial_count — количество доступных номеров
                  • sales_count — количество уже забронированных номеров

                  Отсутствие записи на какую-либо ночь означает недоступность номера.


                  Room_price_policies — политики номеров. Один и тот же номер может иметь различные расценки в зависимости от количества гостей, типа питания и других условий. Поля:
                  • room_id — id номера
                  • max_guests — максимальное количество гостей
                  • meal_type — тип питания, число от 0 до 8, где 0 — без питания, 1 — завтрак, 2 — полупансион и т.д.
                  • has_special_requirements — наличие специальных условий, булево значение
                  • before_type — тип специального условия (0 или 1), 0 — политика действует, только если бронирование происходит до определенной даты, 1 — политика действует, если бронирование совершается за N дней до даты заезда
                  • before_date — дата для before_type 0
                  • days_before_arrival — количество дней для before_type 1


                  Room_prices — цены по политикам номеров за каждую ночь в валюте отеля. Поля:
                  • room_price_policy_id — id политики номера
                  • price_date — дата
                  • price — цена

                  Отсутствие записи за какую-либо ночь означает невозможность приобрести номер в эту ночь.


                  Currency_rates — курсы обмена валют. Поля:
                  • sale_currency — код продаваемой валюты
                  • buy_currency — код покупаемой валюты
                  • price — курс, число единиц продаваемой валюты, деленное на курс, даст число единиц покупаемой валюты


                  Входные параметры


                  Пользователь в форме поиска может выбрать:
                  • Направление или район — что-то из places или districts. Причем если это направление, то при поиске надо искать не только отели направления, но и отели всех районов направления
                  • Желаемая дата заезда
                  • Желаемая дата выезда
                  • Состав группы людей, например, 3 взрослых + 2 ребенка (7 и 9 лет)
                  • Опционально, фильтры по цене за ночь, звездности отеля, типу питания


                  Результаты поиска


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


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

                  Это возможно? Да, в 2 (два!) sql-запроса (после небольшой модификации схемы данных)

                  Решение


                  Допустим пользователь ищет по следующим параметрам:
                  • Направление “Валь Торанс”, в которое входят еще два района “Тинь Ле Лак” и “Тинь Валь Кларе”
                  • Желаемая дата заезда: 2 января 2018
                  • Желаемая дата выезда: 8 января 2018 (соответственно количество желаемых ночей — 6)
                  • Состав группы людей: 3 взрослых + 2 ребенка (7 и 9 лет)
                  • Сегодня: 17 августа 2017


                  Шаг 1. Ближайшая дата заезда к желаемой


                  По сути, надо найти по одному правилу въезда для каждого отеля направления или района с ближайшей датой к желаемой дате въезда. И здесь можно допустить, что ищем ближайшую дату не дальше N дней от желаемой, например, 7 дней. Вот так выглядит такой запрос.
                  Запрос ближайшей даты заезда
                  SELECT DISTINCT ON (property_id)
                    arrival_date,
                    property_id,
                    abs('2018-01-02'::date - arrival_date) AS days_diff
                  FROM property_arrival_rules
                  INNER JOIN properties ON properties.id = property_arrival_rules.property_id
                  WHERE '2017-01-02'::date - 7 <= arrival_date AND arrival_date <= '2018-01-02'::date + 7
                    AND (
                      (properties.dest_type = 'Place' AND properties.dest_id IN (9)) 
                      OR (properties.dest_type = 'District' AND properties.dest_id IN (16, 17))
                    )
                  ORDER BY property_id, days_diff
                  



                  Шаг 2. Подходящая дата выезда


                  Нам надо рассчитать дату выезда на каждый отель исходя из выбранного правила въезда (из шага 1) и количества ночей, вычисленных как разница между желаемыми датами выезда-заезда.
                  И тут открылась первая проблема, т.к. правила въезда оказались очень хитрыми. Есть два типа правил:
                  Тип 1. Можно заехать в определенный день на любое количество дней, но не меньше чем на N дней
                  Тип 2. Можно заехать в определенный день строго на N дней
                  И когда в искомый период попадают правила типа 2, то чтобы рассчитать весь период следует просматривать следующее правило, идущее в день окончания правила — дата заезда из правила + N дней.
                  Реальный пример правила типа 2. В отель можно въезжать только по субботам ровно на неделю. Если я хочу въехать на срок от 1 до 6 дней — мне все равно придется брать на всю неделю. Если же я хочу взять больше чем на 7 дней, например, на 9 дней, то мне придется взять или на 14 дней или ограничить себя сроком меньше — на 7 дней. И так далее…

                  И получается, что алгоритм расчета даты выезда выглядит следующим образом:

                  1. берем найденное правило въезда и предполагаемую дату выезда (дата заезда из правила + желаемое количество ночей)
                  2. проверяем находится ли дата выезда внутри минимального периода правила: от “даты заезда” до “даты заезда + N дней”
                  2.1. если внутри, т.е. период правила перекрывает желаемые даты — проверяем к какому концу периода ближе
                  2.1.1. если ближе к началу и это не первое просматриваемое правило, то дата выезда — это дата заезда из правила
                  2.1.2. иначе датой выезда оказывается “дата заезда + N дней”
                  2.2. если снаружи, т.е. периода правила может быть недостаточно — проверяем какого типа правило мы смотрим
                  2.2.1. если типа 1, то предполагаемая дата выезда и будет рассчитанной датой выезда
                  2.2.2. если типа 2, берем следующее правило на дату: “дата заезда + N дней”
                  2.2.2.1. если следующее правило существует, то рекурсивно повторяем п.2 уже для этого правила, с учетом того, что это не первое просматриваемое правило
                  2.2.2.2. если следующее правило не существует, то датой выезда будет “дата заезда + N дней”

                  И как такое положить на sql?

                  Можно на стороне приложения заранее рассчитать по правилам въезда все возможные периоды заезда-выезда на каждый день и положить в отдельную таблицу с полями:
                  arrival_date
                  (дата заезда)
                  wanted_departure_date
                  (желаемая дата выезда)
                  departure_date
                  (фактическая
                  рассчитанная
                  дата выезда)
                  property_id
                  (id отеля)

                  Или даже более плотно, дабы уменьшить количество записей, т.к. для правил типа 2 будут часто совпадать дата заезда и рассчитанная дата выезда для некоторых рядом стоящих дней
                  arrival_date
                  (дата заезда)
                  wanted_departure_range
                  (желаемый период выезда,
                  тип daterange)
                  departure_date
                  (фактическая
                  рассчитанная
                  дата выезда)
                  property_id
                  (id отеля)

                  И назовем ее property_arrival_periods — рассчитанные периоды въезда.

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

                  Таким образом при добавлении / изменении / удалении правила въезда, мы фоном в приложении:
                  • удаляем рассчитанные периоды за даты: от “даты правила минус 30 дней” до “даты правила”
                  • рассчитываем периоды на каждый день от “даты правила минус 30 дней” до “даты правила” на каждый период бронирования: на 1 день, на 2 дня, на 3 дня, …, на 30 дней

                  И тогда при поиске нам ничего не нужно считать, а только выбрать из этой новой таблицы.
                  Запрос дат заезда-выезда
                  WITH fit_arrival_rules AS (
                    SELECT DISTINCT ON (property_id)
                      arrival_date,
                      property_id,
                      abs('2018-01-02'::date - arrival_date) AS days_diff
                    FROM property_arrival_rules
                    INNER JOIN properties ON properties.id = property_arrival_rules.property_id
                    WHERE '2017-01-02'::date - 7 <= arrival_date AND arrival_date <= '2018-01-02'::date + 7
                      AND (
                        (properties.dest_type = 'Place' AND properties.dest_id IN (9)) 
                        OR (properties.dest_type = 'District' AND properties.dest_id IN (16, 17))
                      )
                    ORDER BY property_id, days_diff
                  )
                  
                  SELECT 
                    property_arrival_periods.arrival_date, 
                    property_arrival_periods.departure_date, 
                    property_arrival_periods.property_id
                  FROM property_arrival_periods
                  INNER JOIN fit_arrival_rules
                    ON property_arrival_periods.property_id = fit_arrival_rules.property_id 
                      AND property_arrival_periods.arrival_date = fit_arrival_rules.arrival_date
                  WHERE wanted_departure_range @> (property_arrival_periods.arrival_date + 6)
                  



                  Шаг 3. Доступные номера


                  Берем все доступные номера, т.е. те, что имеют записи на рассчитанный период въезда-выезда (из шага 2) и одновременно доступны каждую ночь периода.
                  Запрос доступных номеров
                  WITH fit_arrival_rules AS (
                    SELECT DISTINCT ON (property_id)
                      arrival_date,
                      property_id,
                      abs('2018-01-02'::date - arrival_date) AS days_diff
                    FROM property_arrival_rules
                    INNER JOIN properties ON properties.id = property_arrival_rules.property_id
                    WHERE '2017-01-02'::date - 7 <= arrival_date AND arrival_date <= '2018-01-02'::date + 7
                      AND (
                        (properties.dest_type = 'Place' AND properties.dest_id IN (9)) 
                        OR (properties.dest_type = 'District' AND properties.dest_id IN (16, 17))
                      )
                    ORDER BY property_id, days_diff
                  ),
                  fit_arrival_dates AS (
                    SELECT 
                      property_arrival_periods.arrival_date, 
                      property_arrival_periods.departure_date, 
                      property_arrival_periods.property_id
                    FROM property_arrival_periods
                    INNER JOIN fit_arrival_rules
                      ON property_arrival_periods.property_id = fit_arrival_rules.property_id 
                        AND property_arrival_periods.arrival_date = fit_arrival_rules.arrival_date
                    WHERE wanted_departure_range @> (property_arrival_periods.arrival_date + 6)
                  )
                  
                  SELECT room_availabilities.room_id
                  FROM room_availabilities
                  INNER JOIN rooms ON rooms.id = room_availabilities.room_id
                  INNER JOIN fit_arrival_dates ON fit_arrival_dates.property_id = rooms.property_id
                  WHERE fit_arrival_dates.arrival_date <= date AND date < fit_arrival_dates.departure_date
                    AND initial_count - sales_count > 0
                  GROUP BY fit_arrival_dates.arrival_date, fit_arrival_dates.departure_date, room_id
                  HAVING COUNT(room_availabilities.id) = (fit_arrival_dates.departure_date - fit_arrival_dates.arrival_date)
                  



                  Шаг 4. Стоимость номеров и “помещается ли группа?”


                  Берем политики номеров (из шага 3), для которых есть цены на каждый день рассчитанного периода, и вычисляем стоимость за период и среднюю цену за ночь, пересчитывая суммы при этом из валюты отеля в некую базовую валюту (в нашем случае — EUR). Кроме того, необходимо учитывать специальные условия политик “бронирование до даты” и “бронирование за N дней до въезда”.

                  Также нам понадобится признак “помещается ли вся группа в номер” на каждую полученную политику.
                  По задаче политика должна содержать максимально допустимые возрасты с количеством.
                  Например, в номер могут заехать 3 взрослых + 2 ребенка 5 лет.
                  В такой номер смогут поместиться группы:

                  • 3 взрослых
                  • 3 взрослых + ребенок 4 лет
                  • 2 взрослых + ребенок 10 лет (на место взрослого)

                  Но не поместятся:

                  • 4 взрослых
                  • 3 взрослых + ребенок 7 лет
                  • 2 взрослых + 2 ребенка 9 лет

                  И это проблема.
                  Мало того, что изначально максимальное количество гостей представлено полем типа hstore (к которому условия проблемно будет написать) в странном виде: Map, где ключи — максимальный возраст, а значения — количество, а для взрослых — ключ вообще “adults”.
                  Так еще и непонятно, как вообще представить такую информацию так, чтобы можно было проверить поместится группа людей или нет.

                  А давайте представим максимальное количество гостей в виде массива мест (отсортированного по возрастанию), где каждое место — максимальный возраст (18 для взрослого). И тогда вместимость номера “3 взрослых + 2 ребенка 5 лет” будет выглядеть как
                  [5, 5, 18, 18, 18]
                  А группу людей представим как массив их возрастов, и тогда “2 взрослых + 2 ребенка (5 и 9 лет)” будут выглядеть как
                  [5, 9, 18, 18]
                  В итоге, в таблицу политик (room_price_policies) был добавлен столбец вместимости (capacity) хранящий ее в таком виде.
                  Но еще остается вопрос. Как на sql написать условие (или запрос): поместится ли [5, 9, 18, 18] в [5, 5, 18, 18, 18]? Получается нам надо для каждого гостя из группы искать место в номере, и возраст места должен быть больше или равен возрасту гостя, и учитывать, что на одно место только один человек. Этакое рекурсивное исключение гостей и мест в номере.

                  И здесь нам помогут хранимые процедуры. Для нашей задачи процедура выглядит следующим образом.
                  Процедура 'помещается ли группа в номер?'
                  CREATE OR REPLACE FUNCTION is_room_fit_guests(guests INTEGER[], capacity INTEGER[])
                  RETURNS BOOLEAN
                  AS
                  $$ 
                  DECLARE
                    guest int;
                    seat int;
                    seat_index int;
                    max_array_index CONSTANT int := 2147483647;
                  BEGIN
                    guest = guests[1];
                  
                    IF guest IS NULL
                    THEN 
                      RETURN TRUE;
                    END IF;
                  
                    seat_index := 1;
                    FOREACH seat IN ARRAY capacity
                    LOOP
                      IF guest <= seat
                      THEN
                        RETURN is_room_fit_guests(guests[2:max_array_index], capacity[1:seat_index-1] || capacity[seat_index+1:max_array_index]);
                      END IF;
                      seat_index := seat_index + 1;
                    END LOOP;
                  
                    RETURN FALSE;
                  END;
                  $$ 
                  LANGUAGE plpgsql;
                  


                  И пример использования.

                  И теперь наш запрос выглядит так.
                  Запрос с расчетом стоимости и вместимости
                  WITH fit_arrival_rules AS (
                    SELECT DISTINCT ON (property_id)
                      arrival_date,
                      property_id,
                      abs('2018-01-02'::date - arrival_date) AS days_diff
                    FROM property_arrival_rules
                    INNER JOIN properties ON properties.id = property_arrival_rules.property_id
                    WHERE '2017-01-02'::date - 7 <= arrival_date AND arrival_date <= '2018-01-02'::date + 7
                      AND (
                        (properties.dest_type = 'Place' AND properties.dest_id IN (9)) 
                        OR (properties.dest_type = 'District' AND properties.dest_id IN (16, 17))
                      )
                    ORDER BY property_id, days_diff
                  ),
                  fit_arrival_dates AS (
                    SELECT 
                      property_arrival_periods.arrival_date, 
                      property_arrival_periods.departure_date, 
                      property_arrival_periods.property_id
                    FROM property_arrival_periods
                    INNER JOIN fit_arrival_rules
                      ON property_arrival_periods.property_id = fit_arrival_rules.property_id 
                        AND property_arrival_periods.arrival_date = fit_arrival_rules.arrival_date
                    WHERE wanted_departure_range @> (property_arrival_periods.arrival_date + 6)
                  )
                  
                  SELECT
                    rooms.property_id,
                    fit_arrival_dates.arrival_date,
                    fit_arrival_dates.departure_date,
                    room_price_policy_id,
                    room_price_policies.meal_type,
                    (
                      CASE
                        WHEN room_properties.currency = 'EUR' THEN SUM(room_prices.price)
                        ELSE (SUM(room_prices.price) / COALESCE(currency_rates.price, 1))::DECIMAL(10,2)
                      END
                    ) AS total,
                    (
                      CASE
                        WHEN room_properties.currency = 'EUR' THEN AVG(room_prices.price)::DECIMAL(10,2)
                        ELSE (AVG(room_prices.price) / COALESCE(currency_rates.price, 1))::DECIMAL(10,2)
                      END
                    ) AS average_night_price,
                    rooms.id AS room_id,
                    is_room_fit_guests(ARRAY[7,9,18,18,18], room_price_policies.capacity) AS fit_people
                  FROM room_prices
                  INNER JOIN room_price_policies ON room_prices.room_price_policy_id = room_price_policies.id
                  INNER JOIN rooms ON room_price_policies.room_id = rooms.id
                  INNER JOIN properties room_properties ON room_properties.id = rooms.property_id
                  LEFT JOIN currency_rates 
                    ON currency_rates.sale_currency = room_properties.currency 
                    AND currency_rates.buy_currency = 'EUR'
                  INNER JOIN (
                    SELECT room_availabilities.room_id
                    FROM room_availabilities
                    INNER JOIN rooms ON rooms.id = room_availabilities.room_id
                    INNER JOIN fit_arrival_dates ON fit_arrival_dates.property_id = rooms.property_id
                    WHERE fit_arrival_dates.arrival_date <= date AND date < fit_arrival_dates.departure_date
                      AND initial_count - sales_count > 0
                    GROUP BY fit_arrival_dates.arrival_date, fit_arrival_dates.departure_date, room_id
                    HAVING COUNT(room_availabilities.id) = (fit_arrival_dates.departure_date - fit_arrival_dates.arrival_date)
                  ) ra ON ra.room_id = rooms.id
                  INNER JOIN fit_arrival_dates ON fit_arrival_dates.property_id = rooms.property_id
                  WHERE fit_arrival_dates.arrival_date <= price_date AND price_date < fit_arrival_dates.departure_date
                    AND (room_price_policies.has_special_requirements = FALSE
                      OR (room_price_policies.has_special_requirements = TRUE AND room_price_policies.before_type = 0
                        AND room_price_policies.before_date IS NOT NULL AND '2017-08-17'::date < room_price_policies.before_date)
                      OR (room_price_policies.has_special_requirements = TRUE AND room_price_policies.before_type = 1
                        AND room_price_policies.days_before_arrival IS NOT NULL 
                        AND '2017-08-17'::date < fit_arrival_dates.arrival_date - room_price_policies.days_before_arrival)
                    )
                    AND room_price_policies.capacity IS NOT NULL
                  GROUP BY
                    rooms.property_id,
                    fit_arrival_dates.arrival_date,
                    fit_arrival_dates.departure_date,
                    room_price_policy_id,
                    room_price_policies.meal_type,
                    rooms.id,
                    room_properties.currency,
                    currency_rates.price,
                    room_price_policies.capacity
                  HAVING COUNT(room_prices.id) = (fit_arrival_dates.departure_date - fit_arrival_dates.arrival_date)
                  



                  Шаг 5. Подходящие отели


                  Выбираем отели с данными (из шага 4) по одной самой дешевой политике номера с положительным значением “помещается ли вся группа в номер”.
                  Запрос подходящих отелей
                  WITH fit_arrival_rules AS (
                    SELECT DISTINCT ON (property_id)
                      arrival_date,
                      property_id,
                      abs('2018-01-02'::date - arrival_date) AS days_diff
                    FROM property_arrival_rules
                    INNER JOIN properties ON properties.id = property_arrival_rules.property_id
                    WHERE '2017-01-02'::date - 7 <= arrival_date AND arrival_date <= '2018-01-02'::date + 7
                      AND (
                        (properties.dest_type = 'Place' AND properties.dest_id IN (9)) 
                        OR (properties.dest_type = 'District' AND properties.dest_id IN (16, 17))
                      )
                    ORDER BY property_id, days_diff
                  ),
                  fit_arrival_dates AS (
                    SELECT 
                      property_arrival_periods.arrival_date, 
                      property_arrival_periods.departure_date, 
                      property_arrival_periods.property_id
                    FROM property_arrival_periods
                    INNER JOIN fit_arrival_rules
                      ON property_arrival_periods.property_id = fit_arrival_rules.property_id 
                        AND property_arrival_periods.arrival_date = fit_arrival_rules.arrival_date
                    WHERE wanted_departure_range @> (property_arrival_periods.arrival_date + 6)
                  ),
                  properties_with_rooms AS (
                    SELECT
                      rooms.property_id,
                      fit_arrival_dates.arrival_date,
                      fit_arrival_dates.departure_date,
                      room_price_policy_id,
                      room_price_policies.meal_type,
                      (
                        CASE
                          WHEN room_properties.currency = 'EUR' THEN SUM(room_prices.price)
                          ELSE (SUM(room_prices.price) / COALESCE(currency_rates.price, 1))::DECIMAL(10,2)
                        END
                      ) AS total,
                      (
                        CASE
                          WHEN room_properties.currency = 'EUR' THEN AVG(room_prices.price)::DECIMAL(10,2)
                          ELSE (AVG(room_prices.price) / COALESCE(currency_rates.price, 1))::DECIMAL(10,2)
                        END
                      ) AS average_night_price,
                      rooms.id AS room_id,
                      is_room_fit_guests(ARRAY[7,9,18,18,18], room_price_policies.capacity) AS fit_people
                    FROM room_prices
                    INNER JOIN room_price_policies ON room_prices.room_price_policy_id = room_price_policies.id
                    INNER JOIN rooms ON room_price_policies.room_id = rooms.id
                    INNER JOIN properties room_properties ON room_properties.id = rooms.property_id
                    LEFT JOIN currency_rates 
                      ON currency_rates.sale_currency = room_properties.currency 
                      AND currency_rates.buy_currency = 'EUR'
                    INNER JOIN (
                      SELECT room_availabilities.room_id
                      FROM room_availabilities
                      INNER JOIN rooms ON rooms.id = room_availabilities.room_id
                      INNER JOIN fit_arrival_dates ON fit_arrival_dates.property_id = rooms.property_id
                      WHERE fit_arrival_dates.arrival_date <= date AND date < fit_arrival_dates.departure_date
                        AND initial_count - sales_count > 0
                      GROUP BY fit_arrival_dates.arrival_date, fit_arrival_dates.departure_date, room_id
                      HAVING COUNT(room_availabilities.id) = (fit_arrival_dates.departure_date - fit_arrival_dates.arrival_date)
                    ) ra ON ra.room_id = rooms.id
                    INNER JOIN fit_arrival_dates ON fit_arrival_dates.property_id = rooms.property_id
                    WHERE fit_arrival_dates.arrival_date <= price_date AND price_date < fit_arrival_dates.departure_date
                      AND (room_price_policies.has_special_requirements = FALSE
                        OR (room_price_policies.has_special_requirements = TRUE AND room_price_policies.before_type = 0
                          AND room_price_policies.before_date IS NOT NULL AND '2017-08-17'::date < room_price_policies.before_date)
                        OR (room_price_policies.has_special_requirements = TRUE AND room_price_policies.before_type = 1
                          AND room_price_policies.days_before_arrival IS NOT NULL 
                          AND '2017-08-17'::date < fit_arrival_dates.arrival_date - room_price_policies.days_before_arrival)
                      )
                      AND room_price_policies.capacity IS NOT NULL
                    GROUP BY
                      rooms.property_id,
                      fit_arrival_dates.arrival_date,
                      fit_arrival_dates.departure_date,
                      room_price_policy_id,
                      room_price_policies.meal_type,
                      rooms.id,
                      room_properties.currency,
                      currency_rates.price,
                      room_price_policies.capacity
                    HAVING COUNT(room_prices.id) = (fit_arrival_dates.departure_date - fit_arrival_dates.arrival_date)
                  )
                  
                  SELECT DISTINCT ON(property_id) *, 
                    1 as all_guests_placed
                  FROM properties_with_rooms
                  WHERE fit_people = TRUE
                  ORDER BY property_id, total
                  



                  Шаг 6. Неподходящие отели с номерами в наличии


                  Такие отели, в которых нет номера под всю группу гостей, в качестве вариантов для бронирования нескольких номеров. Выбираем отели из шага 4 с отрицательным значением “помещается ли вся группа в номер”, но не попавшие в результат шага 5
                  Запрос неподходящих отелей
                  WITH fit_arrival_rules AS (
                    SELECT DISTINCT ON (property_id)
                      arrival_date,
                      property_id,
                      abs('2018-01-02'::date - arrival_date) AS days_diff
                    FROM property_arrival_rules
                    INNER JOIN properties ON properties.id = property_arrival_rules.property_id
                    WHERE '2017-01-02'::date - 7 <= arrival_date AND arrival_date <= '2018-01-02'::date + 7
                      AND (
                        (properties.dest_type = 'Place' AND properties.dest_id IN (9)) 
                        OR (properties.dest_type = 'District' AND properties.dest_id IN (16, 17))
                      )
                    ORDER BY property_id, days_diff
                  ),
                  fit_arrival_dates AS (
                    SELECT 
                      property_arrival_periods.arrival_date, 
                      property_arrival_periods.departure_date, 
                      property_arrival_periods.property_id
                    FROM property_arrival_periods
                    INNER JOIN fit_arrival_rules
                      ON property_arrival_periods.property_id = fit_arrival_rules.property_id 
                        AND property_arrival_periods.arrival_date = fit_arrival_rules.arrival_date
                    WHERE wanted_departure_range @> (property_arrival_periods.arrival_date + 6)
                  ),
                  properties_with_rooms AS (
                    SELECT
                      rooms.property_id,
                      fit_arrival_dates.arrival_date,
                      fit_arrival_dates.departure_date,
                      room_price_policy_id,
                      room_price_policies.meal_type,
                      (
                        CASE
                          WHEN room_properties.currency = 'EUR' THEN SUM(room_prices.price)
                          ELSE (SUM(room_prices.price) / COALESCE(currency_rates.price, 1))::DECIMAL(10,2)
                        END
                      ) AS total,
                      (
                        CASE
                          WHEN room_properties.currency = 'EUR' THEN AVG(room_prices.price)::DECIMAL(10,2)
                          ELSE (AVG(room_prices.price) / COALESCE(currency_rates.price, 1))::DECIMAL(10,2)
                        END
                      ) AS average_night_price,
                      rooms.id AS room_id,
                      is_room_fit_guests(ARRAY[7,9,18,18,18], room_price_policies.capacity) AS fit_people
                    FROM room_prices
                    INNER JOIN room_price_policies ON room_prices.room_price_policy_id = room_price_policies.id
                    INNER JOIN rooms ON room_price_policies.room_id = rooms.id
                    INNER JOIN properties room_properties ON room_properties.id = rooms.property_id
                    LEFT JOIN currency_rates 
                      ON currency_rates.sale_currency = room_properties.currency 
                      AND currency_rates.buy_currency = 'EUR'
                    INNER JOIN (
                      SELECT room_availabilities.room_id
                      FROM room_availabilities
                      INNER JOIN rooms ON rooms.id = room_availabilities.room_id
                      INNER JOIN fit_arrival_dates ON fit_arrival_dates.property_id = rooms.property_id
                      WHERE fit_arrival_dates.arrival_date <= date AND date < fit_arrival_dates.departure_date
                        AND initial_count - sales_count > 0
                      GROUP BY fit_arrival_dates.arrival_date, fit_arrival_dates.departure_date, room_id
                      HAVING COUNT(room_availabilities.id) = (fit_arrival_dates.departure_date - fit_arrival_dates.arrival_date)
                    ) ra ON ra.room_id = rooms.id
                    INNER JOIN fit_arrival_dates ON fit_arrival_dates.property_id = rooms.property_id
                    WHERE fit_arrival_dates.arrival_date <= price_date AND price_date < fit_arrival_dates.departure_date
                      AND (room_price_policies.has_special_requirements = FALSE
                        OR (room_price_policies.has_special_requirements = TRUE AND room_price_policies.before_type = 0
                          AND room_price_policies.before_date IS NOT NULL AND '2017-08-17'::date < room_price_policies.before_date)
                        OR (room_price_policies.has_special_requirements = TRUE AND room_price_policies.before_type = 1
                          AND room_price_policies.days_before_arrival IS NOT NULL 
                          AND '2017-08-17'::date < fit_arrival_dates.arrival_date - room_price_policies.days_before_arrival)
                      )
                      AND room_price_policies.capacity IS NOT NULL
                    GROUP BY
                      rooms.property_id,
                      fit_arrival_dates.arrival_date,
                      fit_arrival_dates.departure_date,
                      room_price_policy_id,
                      room_price_policies.meal_type,
                      rooms.id,
                      room_properties.currency,
                      currency_rates.price,
                      room_price_policies.capacity
                    HAVING COUNT(room_prices.id) = (fit_arrival_dates.departure_date - fit_arrival_dates.arrival_date)
                  ),
                  properties_with_recommended_room AS (
                    SELECT DISTINCT ON(property_id) *, 
                      1 as all_guests_placed
                    FROM properties_with_rooms
                    WHERE fit_people = TRUE
                    ORDER BY property_id, total
                  )
                  
                  SELECT DISTINCT ON(property_id) *, 
                    0 as all_guests_placed   
                  FROM properties_with_rooms
                  WHERE property_id NOT IN (SELECT property_id FROM properties_with_recommended_room)
                  ORDER BY property_id, total
                  



                  Шаг 7. Все отели направления


                  И наконец, объединяем результаты, сортируя сначала подходящие отели (из шага 5), затем неподходящие отели с доступными номерами (из шага 6), затем все остальные отели, дополнительно сортируя по стоимости за период или звездности отеля при необходимости, а также добавляя пагинацию (20 отелей на странице)
                  Конечный запрос поиска отелей
                  WITH fit_arrival_rules AS (
                    SELECT DISTINCT ON (property_id)
                      arrival_date,
                      property_id,
                      abs('2018-01-02'::date - arrival_date) AS days_diff
                    FROM property_arrival_rules
                    INNER JOIN properties ON properties.id = property_arrival_rules.property_id
                    WHERE '2017-01-02'::date - 7 <= arrival_date AND arrival_date <= '2018-01-02'::date + 7
                      AND (
                        (properties.dest_type = 'Place' AND properties.dest_id IN (9)) 
                        OR (properties.dest_type = 'District' AND properties.dest_id IN (16, 17))
                      )
                    ORDER BY property_id, days_diff
                  ),
                  fit_arrival_dates AS (
                    SELECT 
                      property_arrival_periods.arrival_date, 
                      property_arrival_periods.departure_date, 
                      property_arrival_periods.property_id
                    FROM property_arrival_periods
                    INNER JOIN fit_arrival_rules
                      ON property_arrival_periods.property_id = fit_arrival_rules.property_id 
                        AND property_arrival_periods.arrival_date = fit_arrival_rules.arrival_date
                    WHERE wanted_departure_range @> (property_arrival_periods.arrival_date + 6)
                  ),
                  properties_with_rooms AS (
                    SELECT
                      rooms.property_id,
                      fit_arrival_dates.arrival_date,
                      fit_arrival_dates.departure_date,
                      room_price_policy_id,
                      room_price_policies.meal_type,
                      (
                        CASE
                          WHEN room_properties.currency = 'EUR' THEN SUM(room_prices.price)
                          ELSE (SUM(room_prices.price) / COALESCE(currency_rates.price, 1))::DECIMAL(10,2)
                        END
                      ) AS total,
                      (
                        CASE
                          WHEN room_properties.currency = 'EUR' THEN AVG(room_prices.price)::DECIMAL(10,2)
                          ELSE (AVG(room_prices.price) / COALESCE(currency_rates.price, 1))::DECIMAL(10,2)
                        END
                      ) AS average_night_price,
                      rooms.id AS room_id,
                      is_room_fit_guests(ARRAY[7,9,18,18,18], room_price_policies.capacity) AS fit_people
                    FROM room_prices
                    INNER JOIN room_price_policies ON room_prices.room_price_policy_id = room_price_policies.id
                    INNER JOIN rooms ON room_price_policies.room_id = rooms.id
                    INNER JOIN properties room_properties ON room_properties.id = rooms.property_id
                    LEFT JOIN currency_rates 
                      ON currency_rates.sale_currency = room_properties.currency 
                      AND currency_rates.buy_currency = 'EUR'
                    INNER JOIN (
                      SELECT room_availabilities.room_id
                      FROM room_availabilities
                      INNER JOIN rooms ON rooms.id = room_availabilities.room_id
                      INNER JOIN fit_arrival_dates ON fit_arrival_dates.property_id = rooms.property_id
                      WHERE fit_arrival_dates.arrival_date <= date AND date < fit_arrival_dates.departure_date
                        AND initial_count - sales_count > 0
                      GROUP BY fit_arrival_dates.arrival_date, fit_arrival_dates.departure_date, room_id
                      HAVING COUNT(room_availabilities.id) = (fit_arrival_dates.departure_date - fit_arrival_dates.arrival_date)
                    ) ra ON ra.room_id = rooms.id
                    INNER JOIN fit_arrival_dates ON fit_arrival_dates.property_id = rooms.property_id
                    WHERE fit_arrival_dates.arrival_date <= price_date AND price_date < fit_arrival_dates.departure_date
                      AND (room_price_policies.has_special_requirements = FALSE
                        OR (room_price_policies.has_special_requirements = TRUE AND room_price_policies.before_type = 0
                          AND room_price_policies.before_date IS NOT NULL AND '2017-08-17'::date < room_price_policies.before_date)
                        OR (room_price_policies.has_special_requirements = TRUE AND room_price_policies.before_type = 1
                          AND room_price_policies.days_before_arrival IS NOT NULL 
                          AND '2017-08-17'::date < fit_arrival_dates.arrival_date - room_price_policies.days_before_arrival)
                      )
                      AND room_price_policies.capacity IS NOT NULL
                    GROUP BY
                      rooms.property_id,
                      fit_arrival_dates.arrival_date,
                      fit_arrival_dates.departure_date,
                      room_price_policy_id,
                      room_price_policies.meal_type,
                      rooms.id,
                      room_properties.currency,
                      currency_rates.price,
                      room_price_policies.capacity
                    HAVING COUNT(room_prices.id) = (fit_arrival_dates.departure_date - fit_arrival_dates.arrival_date)
                  ),
                  properties_with_recommended_room AS (
                    SELECT DISTINCT ON(property_id) *, 
                      1 as all_guests_placed
                    FROM properties_with_rooms
                    WHERE fit_people = TRUE
                    ORDER BY property_id, total
                  ),
                  properties_without_recommended_room AS (
                    SELECT DISTINCT ON(property_id) *, 
                      0 as all_guests_placed   
                    FROM properties_with_rooms
                    WHERE property_id NOT IN (SELECT property_id FROM properties_with_recommended_room)
                    ORDER BY property_id, total
                  ),
                  properties_with_cheapest_room AS (
                    SELECT * FROM properties_with_recommended_room
                    UNION ALL
                    SELECT * FROM properties_without_recommended_room
                  )
                  
                  SELECT properties.*,
                    (
                      CASE 
                        WHEN room_id IS NOT NULL THEN 1
                        ELSE 0
                      END
                    ) AS room_available,
                    properties_with_cheapest_room.arrival_date,
                    properties_with_cheapest_room.departure_date,
                    properties_with_cheapest_room.room_id,
                    properties_with_cheapest_room.room_price_policy_id,
                    properties_with_cheapest_room.total,
                    properties_with_cheapest_room.average_night_price,
                    properties_with_cheapest_room.all_guests_placed
                  FROM properties
                  LEFT JOIN properties_with_cheapest_room ON properties_with_cheapest_room.property_id = properties.id
                  WHERE
                      (
                        (properties.dest_type = 'Place' AND properties.dest_id IN (9)) 
                        OR (properties.dest_type = 'District' AND properties.dest_id IN (16, 17))
                      )
                  ORDER BY all_guests_placed DESC NULLS LAST, room_available DESC, total ASC
                  LIMIT 20 OFFSET 0
                  



                  Шаг 8. 3 самых дешевых номера


                  Перед тем как отдать результат пользователю, для неподходящих отелей с доступными номерами отдельным sql-запросом, выбираем 3 самых дешевых номера. Запрос очень похож на поиск самих отелей. Разве что выбираются уникальные номера и только на конкретные отели (из шага 6). Допустим, что на текущей странице два таких отеля, и их id — 1 и 4. Запрос будет таким.
                  3 дешевых номера
                  WITH fit_arrival_rules AS (
                    SELECT DISTINCT ON (property_id)
                      arrival_date,
                      property_id,
                      abs('2018-01-02'::date - arrival_date) AS days_diff
                    FROM property_arrival_rules
                    INNER JOIN properties ON properties.id = property_arrival_rules.property_id
                    WHERE '2017-01-02'::date - 7 <= arrival_date AND arrival_date <= '2018-01-02'::date + 7
                      AND property_id IN (1, 4)
                    ORDER BY property_id, days_diff
                  ),
                  fit_arrival_dates AS (
                    SELECT 
                      property_arrival_periods.arrival_date, 
                      property_arrival_periods.departure_date, 
                      property_arrival_periods.property_id
                    FROM property_arrival_periods
                    INNER JOIN fit_arrival_rules
                      ON property_arrival_periods.property_id = fit_arrival_rules.property_id 
                        AND property_arrival_periods.arrival_date = fit_arrival_rules.arrival_date
                    WHERE wanted_departure_range @> (property_arrival_periods.arrival_date + 6)
                  ),
                  properties_with_available_rooms AS (
                    SELECT DISTINCT ON (rooms.id)
                      rooms.property_id,
                      fit_arrival_dates.arrival_date,
                      fit_arrival_dates.departure_date,
                      room_price_policy_id,
                      room_price_policies.meal_type,
                      (
                        CASE
                          WHEN room_properties.currency = 'EUR' THEN SUM(room_prices.price)
                          ELSE (SUM(room_prices.price) / COALESCE(currency_rates.price, 1))::DECIMAL(10,2)
                        END
                      ) AS total,
                      (
                        CASE
                          WHEN room_properties.currency = 'EUR' THEN AVG(room_prices.price)::DECIMAL(10,2)
                          ELSE (AVG(room_prices.price) / COALESCE(currency_rates.price, 1))::DECIMAL(10,2)
                        END
                      ) AS average_night_price,
                      rooms.id AS room_id
                    FROM room_prices
                    INNER JOIN room_price_policies ON room_prices.room_price_policy_id = room_price_policies.id
                    INNER JOIN rooms ON room_price_policies.room_id = rooms.id
                    INNER JOIN properties room_properties ON room_properties.id = rooms.property_id
                    LEFT JOIN currency_rates 
                      ON currency_rates.sale_currency = room_properties.currency 
                      AND currency_rates.buy_currency = 'EUR'
                    INNER JOIN (
                      SELECT room_availabilities.room_id
                      FROM room_availabilities
                      INNER JOIN rooms ON rooms.id = room_availabilities.room_id
                      INNER JOIN fit_arrival_dates ON fit_arrival_dates.property_id = rooms.property_id
                      WHERE fit_arrival_dates.arrival_date <= date AND date < fit_arrival_dates.departure_date
                        AND initial_count - sales_count > 0
                      GROUP BY fit_arrival_dates.arrival_date, fit_arrival_dates.departure_date, room_id
                      HAVING COUNT(room_availabilities.id) = (fit_arrival_dates.departure_date - fit_arrival_dates.arrival_date)
                    ) ra ON ra.room_id = rooms.id
                    INNER JOIN fit_arrival_dates ON fit_arrival_dates.property_id = rooms.property_id
                    WHERE fit_arrival_dates.arrival_date <= price_date AND price_date < fit_arrival_dates.departure_date
                      AND (room_price_policies.has_special_requirements = FALSE
                        OR (room_price_policies.has_special_requirements = TRUE AND room_price_policies.before_type = 0
                          AND room_price_policies.before_date IS NOT NULL AND '2017-08-17'::date < room_price_policies.before_date)
                        OR (room_price_policies.has_special_requirements = TRUE AND room_price_policies.before_type = 1
                          AND room_price_policies.days_before_arrival IS NOT NULL 
                          AND '2017-08-17'::date < fit_arrival_dates.arrival_date - room_price_policies.days_before_arrival)
                      )
                    GROUP BY
                      rooms.property_id,
                      fit_arrival_dates.arrival_date,
                      fit_arrival_dates.departure_date,
                      room_price_policy_id,
                      room_price_policies.meal_type,
                      rooms.id,
                      room_properties.currency,
                      currency_rates.price
                    HAVING COUNT(room_prices.id) = (fit_arrival_dates.departure_date - fit_arrival_dates.arrival_date)
                  )
                  
                  SELECT 
                    distinct_available_rooms.property_id,
                    distinct_available_rooms.room_id,
                    distinct_available_rooms.room_price_policy_id,
                    distinct_available_rooms.total
                  FROM properties
                  JOIN LATERAL (
                    SELECT * FROM properties_with_available_rooms
                    WHERE properties.id = properties_with_available_rooms.property_id
                    ORDER BY total
                    LIMIT 3
                  ) distinct_available_rooms ON distinct_available_rooms.property_id = properties.id        
                  WHERE properties.id IN (1, 4)
                  ORDER BY distinct_available_rooms.total
                  



                  Результат


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

                  https://habrahabr.ru/post/338406/


                  Метки:  

                  Открываем дочку американской IT-компании в России

                  Пятница, 22 Сентября 2017 г. 10:20 + в цитатник
                  Eskimo сегодня в 10:20 Управление

                  Открываем дочку американской IT-компании в России

                  • Tutorial

                  image


                  Наша компания из США (SkuVault, прописанная в Louisville, Kentucky) решила открыть 100-процентную дочку в России. Наш случай несколько уникален: CEO не мог посетить РФ, так что заверять и пересылать идентификационные документы приходилось туда-сюда меж двух контитентов.


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


                  Список базовых документов для открытия ООО в России выглядит следующим образом:


                  • Паспорт владельца
                  • Заявление на открытие ООО
                  • Решение (о создании организации и назначении гендира)

                  Если учредитель – юр. лицо, то список документов дополняется документами о создании материнской организации, данные об учредителях, и, как подсказывает нам налоговая – “выписка из торговой палаты, или ее аналога”.


                  Ключевые документы по организации в США


                  • Certificate of Existence (когда и кем была зарегистрирована организация);
                  • Annual Report (годовой отчет о состоянии компании);
                  • Letter of Good Standing (документ о финансовом здоровье и отсутствии проблем с налогами);

                  Подавать документы можно только в оригинале, а действительны они будут только подписанные Секретарем Штата (Secretary of State).


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

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


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


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


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


                  Итак, вот у нас на руках сканы документов учредителя, которые он прислал по почте:


                  1. Копия паспорта гражданина США
                  2. Копия водительских

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


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


                  Форма Заявления


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


                  Юристы и налоговая обычно утверждают, что заполнять заявление должно быть:


                  • Заполнено на кириллице, и только владельцем материнской организации (неправда, документ можно заполнить заранее, остается его только подписать)
                  • Подписано владельцем, с заверенной подписью только на территории РФ (неправда)
                  • Подписано владельцем, с заверенной подписью только в консульстве РФ (частично правда)

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


                  Путь первый: Консульство родное, сердцу дорогое


                  Если у вас есть готовность потратить кучу времени, чтоб посетить какое-нибудь русское консульство (которых 4 штуки по углам США), и назначить собеседовение (срок ожидания: ~2-4 недели) – снимаю перед вами шляпу.


                  В то время как посольство в D.C. моментально отвечает на корреспонденцию, и быстро берет трубку – консульство в Сан-Франциско больше похоже на ленивцев из Zootopia, и отвечают по почте только через две недели. Кстати, документы можно заверять в консульстве, которое не прикреплено к вашему штату (например гражданин из Кентукки, может записаться на прием в Сан-Франциско).


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


                  1. Учредительные документы в консульство (cert. of existence, letter of good standing, annual report)
                  2. Документы для подтверждения личности CEO (оригиналы и копии паспорта гражданина США, водительские)

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


                  У нашей налоговой есть “желаемая” форма заверенного:


                  1. Подпись заверена штампом нотариуса в консульстве
                  2. Следующая страница содержит информацию о нотариусе, заверившем подпись
                  3. Нотариус должен указать количество страниц в документе (дабы не допустить последующей подмены)
                  4. Заявление и страница с данными нотариуса должны быть подшиты, и на корешке должна быть печать, объединяющая всё подшитое.

                  Примечание: если регистрируетесь через местную юридическую фирму, которая относит документы в налоговую, пенсионный, и другие инстанции, необходимо написать доверенность (Limited Power of Attorney) от имени владельца, на курьера юридической фирмы (или, соответственно, если вы занимаетесь регистрацией – на вас).


                  Путь второй: местный (американский) нотариус


                  Намного проще и быстрее. CEO подписывает заявление у нотариуса, а нотариус заверяет подпись. В идеальном раскладе, нотариус добавляет номер своей лицензии в поле ИНН.


                  image


                  Доверенность на курьера


                  Дабы подавать документы в налоговую от имени CEO материнской компании, надо написать доверенность (Limited Power of Attorney), сроком на один год, на вас или курьера. Сдать в налоговую надо будет оригинал, а также его нотариально заверенную копию. В доверенности нужно обязательно указать, на какие именно действия доверенность дается.


                  image


                  Решение (форма о решении создания ООО)


                  С этим документов никакой волокиты вообще нет. CEO подписывает две копии Решения, и ставит печать (печать в штатах обычно перфорированная, не пугайтесь, в России их принимают).


                  Подаем документы, ну и сверяемся


                  Отлично! Мы на финишной прямой. Давайте сверим пак документов:


                  1. Материнская организация: Certificate of Existence (с апостилем -> переведенная на русский -> перевод нотариально заверен)
                  2. Материнская организация: Annual Report (с апостилем -> переведенная на русский -> перевод нотариально заверен)
                  3. Материнская организация: Letter of Good Standing (с апостилем -> переведенная на русский -> перевод нотариально заверен)
                  4. CEO / Учредитель материнской организации: Водительские Права (отсканированные + распечатанные -> переведенные на русский -> перевод заверен у нотариуса)
                  5. CEO / Учредитель материнской организации: паспорт гражданина США (US Citizen Passport) (отсканированный + распечатанный -> переведенный на русский -> перевод заверен у нотариуса)
                  6. 2 копии Решения, заполненные на русском, подписанные CEO / учредителем материнской организации, с печатью материнской организации.
                  7. Заявление, заполненное на русском, подписанное CEO / учредителем материнской организации, подпись заверена нотариусом.

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


                  Сноски:



                  Статья-перепост из моего бложика

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

                  https://habrahabr.ru/post/338458/


                  [Перевод] Как запустить свой проект на Product Hunt: руководство от администрации

                  Пятница, 22 Сентября 2017 г. 09:57 + в цитатник
                  nanton сегодня в 09:57 Маркетинг

                  Как запустить свой проект на Product Hunt: руководство от администрации

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



                  В этой статье мы расскажем:

                  • Как разместить публикацию на Product Hunt
                  • На чём следует сосредоточиться
                  • Чего стоит избегать
                  • Как подготовиться заранее
                  • Как подавать свой продукт
                  • Бонус: Как сделать так, чтобы о вас рассказали официальные аккаунты Product Hunt в Twitter и Facebook, а также как попасть в рассылку, которую ежедневно получают сотни тысяч читателей (даже после запуска).


                  Отказ от ответственности: ни один из этих советов не гарантирует 1000 голосов, широкой огласки или множества новых пользователей и притока трафика; это – лучшие методы для успешного запуска. Кроме того, они могут изменяться по мере развития платформы Product Hunt. Основываясь на ваших отзывах, мы постоянно добавляем новые функции (такие как «Задай вопрос» и «Готовятся к запуску»), а также экспериментируем с новыми идеями, чтобы привлечь внимание к хорошим продуктам и помочь их создателям найти своих пользователей.



                  Запуск продукта: Чеклист


                  Люди часто присылают нам письма (на hello@producthunt.com) или пишут кому-то из членов команды, чтобы рассказать о своем продукте и попросить совета. О том, почему это не самый эффективный способ попасть на главную страницу, речь пойдет дальше, но вот советы мы всегда даем с удовольствием!

                  Вот, что нужно сделать, запускаясь на Product Hunt:

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

                  Ссылки: Разместите в качестве основной ссылку на сайт продукта или компании. Затем добавьте ссылки на App Store, Google Play и другие ресурсы, где вашим продуктом можно воспользоваться или скачать его.

                  Название: Просто название вашего продукта. Ничего сверхъестественного!

                  Краткое описание (Tagline): Опишите, зачем нужен ваш продукт, уложившись в 60 символов. Больше конкретики! Расскажите, чем продукт полезен людям, для которых его сделали. Если для этого придется опустить 95% функций — сделайте это. Избегайте лозунгов, ориентируйтесь на то, как пользователи будут искать продукт уже после запуска. Подсказка: если описание начинается со слов “Лучший в мире...” возможно, вам стоит придумать что-то ещё.

                  Аватарка (Миниатюра): Описывает ваш продукт визуально. Здесь лучше всего подойдет статичная картинка или, может быть, ненавязчивая GIFка, для привлечения внимания. В некоторых случаях гифка может выразить всю суть проекта (см. Коллекцию Джека Двека Pitch with a GIF).

                  Галерея: Именно здесь можно развернуться и продемонстрировать свой продукт во всей красе; галерея — это первое, что люди видят, открывая ваш пост. Загрузите сюда как минимум два элемента и обратите внимание на порядок отображения. Если вы загружаете видео (с помощью ссылки на YouTube), оно всегда будет показываться первым. Хотя бы одно изображение/GIF должно быть “твитабельным” — то есть прямоугольным (с пропорциями примерно 2: 1), чтобы хорошо смотрелось в соцсетях. Помогите людям (и нам) продвигать то, что вы создали. P.S. В идеале размер изображения/GIF должен быть ниже 3 МБ.

                  Темы: Ограничитесь выбором 3-4 тем для вашего продукта. Даже если у него отличный интерфейс, это не значит, что «Опыт взаимодействия» (User Experience) – ваша тема, не стоит её указывать.

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

                  Создатели: По возможности старайтесь ссылаться на всех, кто работал над продуктом (только на личные учетные записи). Это позволит им присоединиться к обсуждению и получить признание за упорный труд. Кроме того, есть и еще одно преимущество: подписчики каждого разработчика получают уведомление в день запуска продукта, что помогает донести новость до широкой аудитории. Поэтому обязательно упомяните всех своих товарищей по команде. Чтобы обозначить кого-то как разработчика, необходимо, чтобы он был зарегистрирован на Product Hunt.

                  P.S. Не лишним будет связать свою учетную запись с Twitter и Facebook, чтобы ваши друзья могли найти вас.



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

                  Комментарий создателя: Кратко представитесь: расскажите о себе, свой команде и задаче, которую вы решаете. В трёх-четырёх предложениях изложите, в чём ценность продукта, когда и кому его следует использовать и почему вы его разрабатываете. Если это не первый запуск на Product Hunt, — скажем, масштабное обновление или анонс грандиозной новой функции — объясните, что изменилось.

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



                  Ссылки на соцсети: Пришло время добавить ссылки на аккаунты компании на Facebook, Instagram, Medium и AngelList! Они все будут отображаться прямо на боковой панели. Если у вас есть открытые вакансии на Angel List, включите и их. Запуск — самый удачный момент, чтоб привлечь соискателей.

                  Дополнительно:

                  Промо-акции: Пользователи PH любят выгодные предложения. Не стесняйтесь размещать специальные предложения или промо-акции в своем первом комментарии. Такие подарки в честь запуска (например, скидка 20% в течение 6 месяцев) могут увеличить вовлеченность, привлекают внимание и побуждают людей попробовать то, над чем вы работаете .P.S Распространение в комментариях промокодов, предоставляющих членам сообщества ранний доступ, творит чудеса

                  Просите об обратной связи: Диалог имеет большое значение. Хотите завязать разговор и получить обратную связь – просите об этом напрямую. Сообщите, какой именно отклик вам хотелось бы увидеть (например, «Дайте мне знать, что вы думаете о функциях X, Y, Z). Продукты, которые активно обсуждаются, вызывают больше интереса и, в целом, обычно приходят к хорошим результатам.



                  Как не надо делать


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

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

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

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

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

                  Не ссылайтесь на домен “producthunt.com”. Ссылайтесь на страницу вашего продукта напрямую, иначе людям будет сложнее вас найти. Тот факт, что вы ссылаетесь на страницу Product Hunt, вообще никак не влияет на действия алгоритма. Считается «общеизвестным», что за прямые ссылки на страницу продукта алгоритм накладывает санкции, но это неправда. Сосредоточьтесь на том, чтобы рассказать о себе.

                  P.S. Ссылка на producthunt.com — возможно, один из наиболее очевидных признаков «чрезмерной оптимизации» запуска (то есть попытки «переиграть» систему).

                  Внесём ясность: Главное — поделиться продуктом и рассказать о нём людям. Один из основных показателей, на который мы, как компания, работаем, напрямую связан с тем, сколько пользователей узнает о вашем продукте. Наши интересы совпадают.

                  Мы пытаемся пресечь только то, что вредит сообществу: навязчивое массовое информирование (то есть спам) или попытки обмануть систему, чтобы получить больше голосов (все, что считается «популярным» в сообществе). P.S. Подобные действия очень легко обнаружить (что часто подтверждается скриншотами, которые нам присылают). Кроме того, имейте в виду, что голоса, которые система воспринимает как спамерские, могут привести к понижению рейтинга в целом.

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



                  За несколько месяцев до запуска


                  Ознакомьтесь с Product Hunt: Если вы никогда раньше не бывали на Product Hunt – зарегистрируйтесь (но аккаунт компании пока не создавайте). Проведите несколько дней, изучая платформу: следите за темами, комментируйте, заводите друзей в ходе дискуссий. Ваша учетная запись должна быть «привязана» к реальной личности, чтобы люди знали, с кем они разговаривают. Для начала можете попробовать задавать вопросы и предлагать рекомендации в разделе Ask Product Hunt.

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

                  Страницы Upcoming (“Готовятся к запуску”) на Product Hunt: Этот новый инструмент будет доступен для всех в ближайшее время (мы недавно рассказывали о нём более подробно в рассылке). Что они из себя представляют, можно посмотреть здесь.



                  Размещение


                  Все продукты на главной странице Product Hunt разделены по двум вкладкам: «Популярные» и «Новые». Каждый размещённый продукт сначала попадает в категорию «Новые», а уже затем самые популярные из них оказываются в списке «Популярных» (говорящее название, да?). Каждый хочет попасть туда. Если вы воспользуетесь вышеизложенными советами, ваши шансы закрепиться на главной странице возрастут.

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

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

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

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

                  Лучшее время, чтобы разместить пост: Нет однозначно идеального времени, но есть факторы, которые стоит учитывать. Главная страница работает по 24-часовому циклу. Новые продукты появляются здесь в полночь по стандартному тихоокеанскому времени (11 утра по московскому времени) и продолжают размещаться в течение дня. Мы стараемся не размещать новых продуктов после 11 утра по стандартному тихоокеанскому времени (10 вечера по московскому времени), чтобы они не остались без внимания – такие посты переносятся на следующий день. Хотя «волшебного» времени нет, лучше всего постить до 9 утра по стандартному тихоокеанскому времени (8 вечера по московскому времени), тогда у людей будет достаточно времени чтобы познакомиться с вашим продуктом и обсудить его.

                  Пример 1: Скучная Компания



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

                  Название: Название компании. Отлично.
                  Краткое описание: «Новая компания Элона Маска, создающая транспортные тоннели» — коротко и ясно, очевиднее некуда.
                  Темы: Все три темы (Автомобили, Перевозки, Технологии) чётко соотносятся с реальным продуктом (сеть подземных автомобильных тоннелей).
                  Иконка: Иконка (в данном случай – GIF-анимация) ясно демонстрирует функционал и ключевые особенности продукта.
                  Галерея: В галерее находятся промо-видео на YouTube, демонстрирующая, в чем заключается суть продукта, и нескольких картинок/GIF-анимаций, которые дают представление о функционале.
                  Создатели: Мы всё ещё ждём, что Элон к нам присоединится… Но можем подтвердить, что он получил двух Золотых Котят, которых мы ему выслали.

                  Пример 2: Polymail



                  Ссылки: На странице есть прямые ссылки на вебсайт Polymail и на скачивание приложения в App Store.
                  Название: Снова – просто название продукта (Polymail).
                  Слоган: Грамотно оформленное четкое изложение сути продукта.
                  Краткое описание: Суть проекта и основные функции, которые предлагает Polymail, в двух ясных, простых для понимания строках
                  Темы: Правильное число тем (2-4) – приложение-клиент для электронной почты на iOS/Mac хорошо вписывается в категории Производительность, Электронная почта, Технологии
                  Иконка: Даже не используя в качестве иконки броскую гифку, приложение Polymail получило в конце года 4700 голосов и Золотого Котёнка. Иногда простое лого – это всё, что нужно.
                  Галерея: Создатели Polymail разместили несколько скриншотов с основным функционалом приложения (и интересным текстом, если присмотреться). Картинки и GIF-анимации на тему Product Hunt — это всегда хорошо.
                  Создатели: Вся работавшая над приложением команда отмечена как «Создатели», а не только основатель или генеральный директор.
                  Соцсети: Команда Poymail добавила информацию о своих аккаунтах на AngelList, GitHub, Facebook, Twitter и Medium.
                  Комментарий: Комментарий Брендона – короткий, информативный и скромный; самый подходящий тон при запуске приложения. Он просто и понятно объяснил основные отличия Polymail от конкурентов.



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





                  Маркетинг и продвижение продукта


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

                  Твиты / Репосты на Facebook


                  Общие советы: Расскажите о продукте, добавив изображение/гифку, чтобы привлечь внимание, без хештегов, но с тегом @ProductHunt. Мы ищем новые твиты и посты компаний или создателей о запуске продукта, которым можем помочь ретвитом или репостом через наш аккаунт.

                  Сделайте пост более личным: Посты с подводкой в духе «Я только что запустил свой первый продукт!» или «После месяцев упорной работы мы готовы поделиться...» находят больший отклик и привлекают более широкую аудиторию. Искренность — лучшая политика.

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

                  Давайте ссылку прямо на страницу вашего продукта, не на домен producthunt.com.

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

                  Картинки vs Гифки vs Видео. Не полагайтесь на превью, прикрепляйте файлы. Так ваш пост скорее заметят в ленте (превью может не прогрузиться в некоторых клиентах, так что пользователям будет видна только ссылка). Видео может сработать, но только если это необработанное демо, записанное со смартфона, или что-то настолько же простое (хотя тут всё зависит от конкретного случая, слишком обобщать не стоит). Но в общем и целом, добавление видео неизбежно приведет к снижению числа кликов. Мы пришли к выводу, что гифки дают оптимальный баланс, чтобы привлечь внимание, передать нужную информацию о продукте, но при этом не раскрыть карты настолько, чтобы у пользователя пропало желание перейти на сайт. Говоря об опыте Product Hunt, мы активно используем гифки и даже создали отдельный твиттер, где размещаем исключительно их.

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

                  Как действительно продвинуть свой продукт


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

                  Название «Pitch Decks From Top Startups» («Питч-деки от создателей успешных стартапов»; питч-дек — презентация бизнес плана для инвесторов — прим. пер.) передает все самое важное. Краткое описание «Коллекция питч-деков, собравших 700 миллионов долларов», логотипы популярных стартапов и комментарий создателя — все это вместе подчёркивает ценность продукта.

                  Однако бывает и так, что само название не слишком информативно. Возьмем, например, приложение с названием Muzzle.



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

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

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

                  Вот несколько популярных твитов о Muzzle:

                  Дэйв: «Лучший лендинг в истории»
                  Бэн: «Лучший леееендинг»
                  Андреас: «Самый. Лучший. Лендинг. На свете.»
                  Райан: «Лучший демо-лендинг, который я видел в этом году»

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

                  Подумайте, как представить свой продукт так, чтобы не знакомый с ним человек тут же понял, в чем смысл. GIF-анимация — самый быстрый способ донести задумку до пользователей, к тому же, гифками удобно делиться (Giphy Capture и CloudApp — простые и популярные в сообществе приложения).

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

                  P.P.S. Для всех, а не только для тех, кто запускает продукт: Будет здорово, если вы расскажете о любом продукте, используя наши советы. Мы активно ищем хороший контент для ретвитов, независимо от того, когда он был опубликован (только не забудьте поставить тег @ProductHunt, чтобы мы увидели его и разместили на сайте ссылку).

                  Новостная рассылка Product Hunt


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

                  После публикации вы тоже можете попасть в подборку новостной рассылки.

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

                  Если вы хотите, чтобы вас включили в новостную рассылку, то вот как привлечь наше внимание:

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

                  Спросите на Product Hunt (Ask Product Hunt): Этот относительно новый раздел сайта набирает популярность. Он стал источником вдохновения для будущих новостных рассылок. Мы рекомендуем вам тоже поучаствовать, задавая вопросы и предлагая рекомендации по продуктам: producthunt.com/ask



                  Надеемся, что это статья была вам полезна. Удачного запуска! И помните: как вы все ни прошло, никогда нельзя терять веру в себя.

                  Фред использует опцию Time Travel, которую мы недавно добавили, чтобы смотреть топ продуктов за определённый день или месяц, включая топы 2015, 2016 и 2017. Полистайте эти топы для вдохновения.
                  Original source: habrahabr.ru (comments, light).

                  https://habrahabr.ru/post/338450/


                  Метки:  

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