Скоро некоторые из нас пойдут отдавать свои голоса за очередных кандидатов в депутаты. Депутаты являются нашими представителями и им мы предоставляем право принимать некоторые законодательные решения за нас. В данных условиях логично выбирать тех кто делает свой выбор также как и мы.
Голоса депутатов находятся на vote.duma.gov.ru. Нам остаётся проголосовать за важные для нас законопроекты в соответствии со своим видением и получить рейтинг на основе которого и делать выбор на выборах.
Я выбрал наименее затратный путь и написал JavaScript расширение для Chrome. Работа с ним организованна через консоль браузера (Ctrl + Shift + J). Бонусом я протестировал поддержку русского языка в JavaScript без препроцессоров.
Под катом вас ждёт код с комментариями и комментарии к статье.
В папке "vote" три файла "manifest.json", "insert.js", "script.js" в кодировке UTF-8 (без BOM).
Код:
Файл "manifest.json":
{
"manifest_version": 2,
"name": "Твой кандидат",
"description": "Расширение для подсчёта персонального рейтинга депутатов на vote.duma.gov.ru",
"version": "1.0",
"content_scripts": [
{
"matches": [ "http://vote.duma.gov.ru/*" ],
"js": [ "insert.js" ],
"run_at": "document_end"
}
],
"web_accessible_resources": [
"script.user.js"
]
}
Часть манифеста "web_accessible_resources" даёт доступ странице загружать и использовать перечисленные в ней файлы плагина. "script.js" это основной скрипт в котором вся логика.
Хром не даёт прямого доступа расширениям к переменным страницы. Мы внедряем свой скрипт("script.js") в саму страницу при помощи "insert.js".
Файл "insert.js":
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = chrome.extension.getURL("script.user.js");
script.async = 1;
document.head.appendChild(script);
Файл "script.user.js":
// ==UserScript==
// @name Твой кандидат
// @description Расширение для подсчёта персонального рейтинга депутатов на vote.duma.gov.ru
// @author ivan386
// @license MIT
// @version 1.0
// @run-at document-end
// @include http://vote.duma.gov.ru/*
// ==/UserScript==
(function() {
'use strict';
var вывести = function( текст ){
console.log( текст );
document.querySelector( "#текст" ).innerText = текст;
};
var голосовать = function( ваш_голос )
{
if ( typeof deputiesData !== 'undefined' )
{
// Для хранения рейтинга и остальных данных депутатов используется localStorage
var депутаты = JSON.parse( localStorage.getItem( "депутаты" ) || "{}" );
// В переменной deputiesData храняться голоса депутатов за текущий закнопроект и другие данные.
deputiesData.forEach( function( депутат )
{
var ls_депутат = депутаты[ депутат.url ];
// Восстанавливаем сохранённый рейтинг или устанавливаем 0
депутат.рейтинг = ( !ls_депутат ) ? 0 : ls_депутат.рейтинг;
// Восстанавливаем статистику
депутат.статистика = ( !ls_депутат ) ? {} : ( ls_депутат.статистика || {} );
// Копируем имя
депутат.имя = депутат.sortName;
// Сохраняем статистику голосов депутатов на будущее.
if ( typeof( депутат.статистика[ депутат.result ] ) === 'undefined' )
депутат.статистика[ депутат.result ] = 1;
else
депутат.статистика[ депутат.result ] ++;
депутат.количество = 0
for ( var голоса in депутат.статистика )
депутат.количество += депутат.статистика[голоса];
// У каждого депутата есть ссылка с его ID на результаты его голосования.
// Используем её как уникальный идентификатор.
депутаты[ депутат.url ] = депутат;
// Результат голосования каждого депутата лежит в переменной result
// Соответствия взяты из функции renderer скрипта на странице с результатами голосования на vote.duma.gov.ru.
// Значение -1 соответствует голосу "За"
// Значение 0 соответствует голосу "Воздержался"
// Значение 1 соответствует голосу "Против"
// Значение 2 соответствует голосу "Не голосовал"
// Меняем рейтинг депутата в соответствии с нашим выбором.
депутат.рейтинг += ( депутат.result == ваш_голос ) ? 1 : -1;
} );
//Сохраняем рейтинг и другие данные в localStorage
localStorage.setItem( "депутаты" , JSON.stringify( депутаты ) );
вывести( "голос " + ( ваш_голос == -1 ? "За" : "Против" ) + " принят" );
}
else
вывести( "на странице не найдено результатов голосования депутатов за законопроект" );
};
var за = function()
{
// Значение -1 соответствует голосу "За"
return голосовать( -1 );
};
var против = function()
{
// Значение 1 соответствует голосу "Против"
return голосовать( 1 );
};
var по_рейтингу = function( список )
{
var список_по_рейтингу = [];
var вывод = [];
for ( var ключ in список )
список_по_рейтингу.push( список[ ключ ] );
список_по_рейтингу.sort( function( первый, второй )
{
// Сортируем в порядке убывания рейтинга
return второй.рейтинг - первый.рейтинг;
} );
список_по_рейтингу.forEach( function( элемент )
{
// Выводим результаты в консоль
вывод.push( элемент.имя + ": " + элемент.рейтинг + ( элемент.количество ? " (" + элемент.количество + ")" : "" ) );
} );
return вывод.join( "\n" );
};
var рейтинг_депутатов = function()
{
var депутаты = JSON.parse( localStorage.getItem( "депутаты" ) || "{}" );
вывести( по_рейтингу( депутаты ) );
};
var рейтинг_партий = function()
{
var депутаты = JSON.parse( localStorage.getItem( "депутаты" ) || "{}" );
var партии = {};
for ( var идентификатор in депутаты )
{
var депутат = депутаты[ идентификатор ];
var партия = партии[ депутат.factionCode ];
if ( партия )
{
// Рейтинг партии складывается из рейтинга депутатов.
партия.рейтинг += депутат.рейтинг;
партия.количество ++;
}
else
партии[ депутат.factionCode ] = { имя: депутат.faction , рейтинг: депутат.рейтинг , количество: 1 };
}
вывести( по_рейтингу( партии ) );
};
var окно = (typeof unsafeWindow === 'undefined') ? window : unsafeWindow;
document.body.insertBefore( document.createElement( "div" ) , document.body.firstChild ).innerHTML = 'Твой кандидат:\n <button onclick="за()">За</button>\n <button onclick="против()">Против</button>\n <button onclick="рейтинг_партий()">Рейтинг партий</button>\n <button onclick="рейтинг_депутатов()">Рейтинг депутатов</button>\n <div id="текст"></div>';
окно.за = за;
окно.против = против;
окно.рейтинг_депутатов = рейтинг_депутатов;
окно.рейтинг_партий = рейтинг_партий;
if ( typeof( окно.deputiesData ) !== 'undefined' )
deputiesData = окно.deputiesData;
вывести( "за() - проголосовать за\nпротив() - проголосовать против\nрейтинг_депутатов() - выводит текущий рейтинг депутатов в соответствии с вашими голосами\nрейтинг_партий() - выводит текущий рейтинг партий в соответствии с вашими голосами\n" );
})();
Установка
- Копируем папку "vote" с содержимым на локальный диск.
- В браузере Chrome открываем "chrome://extensions/" или "Главное Меню > Дополнительные инструменты > Расширения"
- Ставим галочку "Режим разработчика"
- Нажимаем "Загрузить распакованное расширение..."
- Находим и выбираем папку "vote" со скриптами.
- Нажимаем ОК.
Использование
Голосуем
- Заходим на страницы с голосами депутатов на vote.duma.gov.ru.
Например: http://vote.duma.gov.ru/vote/95967 (ссылка взята здесь) Открываем консоль браузера (Ctrl + Shift + J).
3.1. Если против пишем:
против()
3.2. Если за пишем:
за()
- Нажимаем Enter
Получаем рейтинг
- Заходим на любую страницу vote.duma.gov.ru.
Открываем консоль браузера (Ctrl + Shift + J) ещё не открыта.
3.1. Для вывода рейтинга депутатов:
рейтинг_депутатов()
3.2. Для вывода рейтинга партий:
рейтинг_партий()
- Нажимаем Enter
Либо можно воспользоваться кнопками вверху страницы.
Результат
В результате каждый пользователь этого скрипта получает свой рейтинг депутатов и партий в соответствии со своим выбором и выбором депутатов.
Пример рейтинга депутатов:
Иванов Иван Иванович: 5
Петров Пётр Петрович: 0
Сидоров Владимир Владимирович: -5
Пример рейтинга партий (в скобках количество депутатов):
Пиастрская: 100 (10)
Фарианская: 0 (1)
АнтиПиастрская: -100 (1)
Все имена и события вымышлены. Совпадения случайны.
Заключение
Надеюсь этот скрипт поможет делать более осознанный выбор своего представителя в законодательной власти. А заодно и покажет пример написания расширения для анализа данных на государственных сайтах .
Об ошибках прошу писать в личные сообщения.
Источники
Комментарии (54)
Zenitchik
29.07.2016 18:19+3Идея хороша. Надо развить, чтобы могли пользоваться те, кто не дружит с консолью.
ivan386
29.07.2016 18:31+1Позже продумаю и накидаю кнопочки и вывод списка на страницу. Или это может кто-то сделать за меня.
Alexey2005
29.07.2016 19:57Если такие рейтинги станут популярными, то на правительственных ресурсах просто перестанут выкладывать расклад по голосованиям. Будут сообщать лишь итоговый результат, а кто конкретно из депутатов как голосует — засекретят.
arvitaly
29.07.2016 21:05+3Вот где пригодился бы блокчейн. Если хранить хэши исторических документов у каждого гражданина, то, к примеру, если придет новое правительство и/или кто-то решит уничтожать документы, то будет способ валидации бекапов этих документов. Большой брат не сможет ничего переписать, а Министерство Правды в бессильной ярости будет пытаться уничтожить блокчейн.
Aleksandr_Kukolev
29.07.2016 20:03-3Что то не так с подсчетом процентов. 53.1 + 0.4 = 53.5, а ни как не 53.6
Evgeny42
29.07.2016 21:12+4Ну очевидно же, что для показа округляется, и результат округляется после суммирования не округленных процентов.
prostofilya
29.07.2016 20:11Глянул статистику, везде против 1-2 человека, а то и ноль. Будто перед голосованием кнопку просто потестили на работоспособность.
prostofilya
29.07.2016 20:13+3или пока одного из депутатов нет, его сосед по парте жмёт за него, чтоб его с работы выперли
Alexey2005
29.07.2016 20:22В таких условиях нужно изменить способ расчёта рейтинга, чтоб учитывать отсутствующих депутатов. Ведь если депутат не явился на голосование, он тем самым саботировал принятие данного закона (если бы не явились все 100%, закон бы не приняли вовсе).
dmitry_ch
30.07.2016 13:13Так они там друг за друга голосуют.
Быстрый поиск такое вот дает: https://www.youtube.com/watch?v=GGj9ZsYhNzA уж не знаю, чего он там «прыгает», но выглядит не очень приглядно.
Так что на голосовании нужно что-то большее, чем просто кнопка или просто карточка (они «ответственному» будут их сдавать, и тот бегать с пачкой). Ну не сетчатку же глаза сканить в процессе голосования?sim31r
30.07.2016 20:15+1Так они там друг за друга голосуют.
Если они из одной партии/фракции то они по любому гололосуют одинаково. Получается, что присутствие на рабочем месте всех представителей партии такая же формальность, как и у удаленного разработчика/админа, могут работать удаленно.Old_Chroft
31.07.2016 11:03[...]присутствие на рабочем месте всех представителей партии такая же формальность[...]
А так хочется видеть вменяемых людей, которые принимают решения за всю страну…
Zenitchik
31.07.2016 11:59Не всегда. Даже по спискам можно увидеть, что за некоторые законопроекты некоторые партии голосуют в разнобой.
Zenitchik
29.07.2016 22:41+2Ну, где как. Впечатление складывается такое, что демократы, не считая ЛДПР, только в КПРФ остались. А остальные — за закручивание гаек.
AVX
29.07.2016 22:00+2Да, это лучше, чем ничего. Но далеко от массового использования. Даже вот мне, как достаточно технически грамотного, просто лень лазить в консоль хрома и что-то там делать. А если этим будут пользоваться 2,5 гика вместо тысяч или миллионов обычных пользователей — толку не прибудет.
Как я вижу реализацию: делается сайт «персональный рейтинг депутатов» (или как-то так, не сильно важно), там вместо этого JS скрипта всё выполняется на сервере (php, или что ещё), пользователю выводится список законов или других актов, принимаемых госдумой (поправки к законам, и т.п.), и предлагается проголосовать: за против воздержался. После того, как проголосовал по интересующим вопросам (или всем, что довольно тяжело будет сделать сразу, их очень много), пользователю будет выведено: список депутатов (и из какой они партии), с которыми Ваше мнение совпадает больше всего (в порядке убывания), список партий, аналогично (алгоритмы нужно ещё продумать, это только задумка). Базу данных каждого пользователя хранить локально на его же компе (для безопасности), и его же предупреждать достаточно заметной ссылкой на соответствующую страницу с разъяснениями, что хранить эти данные на сайте — не лучшая затея, т.к. и на сайт будут давить спецслужбы чтобы доступ к этим данным получить, да и пользователю спокойнее. Хотя, можно оставить и какую-то регистрацию и хранить данные на сайте, чтобы пользователь мог авторизоваться с разных мест, и данные не были привязаны к компу, но с соответствующими рисками безопасности.
Далее. Можно выводить статистику самую разную: рейтинг депутатов (публичный, персональный), рейтинг партий аналогично, потенциальные друзья по политическим интересам (если они зарегистрировались и указали свои, например, профили в соцсетях, и разрешили это в настройках), а потом (когда база пользователей наберётся достаточно большая) можно прикрутить и собственно голосование за самих депутатов. И кто знает, может быть, такая система сможет быть весомой и реальной альтернативой официальным выборам…
А если позже реализовать подобное на госуслугах — то ещё один шаг к электронному правительству, и больше шансов на реально честные выборы.
P.S. простите за излишнюю политизированность, если есть.vstr
30.07.2016 20:54+1Дичайше плюсую. Давно пора сделать подобный ресурс с объективной информацией о депутатах, а именно — о пофамильном голосовании за различные законы, будь то вредительские инициативы в ИТ-отрасли или, к примеру, поборы на капремонт. Искать информацию на официальном сайте госдуры очень неудобно, а ЕР-овцы в своей агитации естественно умалчивают о том, как они голосовали за идиотские законы.
komjah
31.07.2016 10:47+1А зачем что-то говорить у них большинство в думе 53%, так что это именно они (ЕР) или принимают закон или отклоняют.
Что же касается рейтинга еще хотелось бы видеть граждан вносящих откровенно идиотские инициативы.
Zenitchik
31.07.2016 12:03Есть проблема. Кто-то должен ещё расшифровывать законопроекты. Иначе в каждый законопроект вникать по часу придётся, чтобы осмысленно проголосовать. И к этому кому-то должно быть доверие (это вообще нереально).
sim31r
31.07.2016 22:18Интересное наблюдение. В данной области тоже присутствует разделение труда и нужен специалист в области законов, которому можно доверять. Но изначально таким специалистом и является депутат со своими помощниками, который принимает решения в интересах своих избирателей, не требуя от них никакого участия вообще. Получается что так, что так, нужно доверенное лицо, после сокращения всех переменных получаем исходные начальные условия.
Пример из жизни типичный, избираются депутаты от КПРФ, но после избрания переходят в более выгодную им партию власти (в оппозиции «пенсионерской» у них перспектив меньше), случаи не единичные.
Вангую, что примерно так же будет и с доверенным лицом/сайтом, после того, как наберет ХХХ тысяч подписчиков, на него обратят внимание заинтересованные лица и подстроят трактовку в свою пользу, или займется партия власти, или оранжевые силы третьего не дано, высоки ставки и сильны игроки (по моим наблюдениям даже geektimes.ru не избежал данной участи в силу популярности). В итоге, лучше с политикой не связываться вообще, расклад сил по существу не изменить.
Но данное решение можно перенести в другие области, более спокойные. Например в область медицины. От выбора врачей (они поважнее депутатов, от врачей напрямую зависят жизни), до подбора дешевых аналогов лекарств (многие врачи в сговоре с фармацевтами и впаривают специфические лекарства, дорогие, а часто и вредные в данном случае).
Что-то подобное работает в области онлайн продаж, в реальном времени поддерживается рейтинг продавцов, магазинов и товаров.
ivan386
03.08.2016 17:05
deNULL
29.07.2016 22:12+1Не совсем понимаю — почему было не опубликовать расширение в Chrome Web Store?
Конечно, в обучающих целях инструкции по написанию и загрузке распакованных расширений могут быть полезны, но легкий способ установки тоже стоило дать, наверное.ivan386
30.07.2016 13:00+3Там 5$ хотят, иконку рисовать надо и регистрироваться в платёжной системе.
Zenitchik
31.07.2016 12:05А почему бы тогда не запаковать приложение, чтобы оно ставилось единым для chromium-браузеров образом? У кого Гугл-Хром — пусть незапакованное подключают, а у меня-то Vivaldi никаких проблем не создаёт.
NickKolok
02.08.2016 21:56У меня при разработке расширения возникла аналогичная проблема. По итогам обсуждения на гитхабе сделали юзерскрипт. Пример инструкции по установке. Настоятельно рекомендую.
ivan386
03.08.2016 17:13Уже сделал. Файл "script.user.js" можно теперь в Greasemonkey и Tampermonkey добавить.
Survtur
30.07.2016 20:49Надо срочно выкачать данные и сделать отдельный сайт, чтобы не думать о аддонах, скриптах и прочем. Будет очень полезно. Даже я, вроде продвинутый хаброчитатель, но сложности в этом случае меня останавливают (я в отпуске и с телефончика почитываю).
Но я очень хочу воспользоваться возможностью найти депутата, соответствующего моим ожиданиям. И уверен, что сайт будет очень популярным. Всё-таки думающих людей у нас много.
Arepo
31.07.2016 13:35+1Скачал результату по всем голосованиям и выложил в гитхаб здесь: https://github.com/data-dumaGovRu/vote
Общий размер получился 1438Мб в форматированном JSON.ivan386
31.07.2016 14:43Вот это дело. Я как раз хотел посмотреть статистику по прогульщикам, тем кто за любой кипиш, тем кто не хочет ничего менять, кто голосует так как скажет патрия а кто позволяет иметь собственное мнение.
Arepo
31.07.2016 14:55Судя по всему не так всё просто: парни из ЕР, похоже, вообще не голосуют против, вместо этого они вообще не голосуют целой фракцией и вопрос не набирает кворума.
sim31r
31.07.2016 22:39Ну я как представитель из области IT ничего против «прогульщиков» не имею. Можно работать удаленно за троих, а можно просиживать в офисе создавая видимость работы. Странно что столь категоричное мнение повторяется на IT ресурсе. За политикой и депутатами не слежу вообще. Может там есть какие-то нюансы, но со стороны кажется, пусть работают удаленно, лишь бы была обществу польза. Удаленно можно более детально вникнуть в суть принимаемых законов или заняться местечковыми делами с избирателями, у некоторых это под 10 000 км от «кнопки».
И опять же, новости из области развития ИИ подразумевают, что скоро депутаты останутся не у дел, специализированный ИИ может сформировать более точные законы и намного быстрее. Пример налог, НДС, может быть в разных случаях от 0% до 20%, выбор на первый взгляд кажется хаотичным. Сомневаюсь что число было оптимизировано до третьего знака после запятой, а в масштабах страны это миллиарды рублей. Во всех почти сетевых магазинах цена каждого товара оптимизируется весьма сложным алгоритмом до копеек в реальном времени, а в масштабах государства указания в стиле «плюс минус километр»
KaaPex
02.08.2016 10:19А можно же включить подобный скрипт через расширение хрома Tampermonkey и чтобы кнопочка сразу отображалась в нужном месте для голосования на сайте и вообще все красиво было.
ivan386
02.08.2016 17:26Адаптировал для Greasemonkey и Tampermonkey а делать красиво прошу самостоятельно.
Miraage
Вот это импортозамещение в названии переменных/функций!
ivan386
Главное что работает без препроцессора благодаря Unicode.
0x9d8e
У меня не работает. Одни сплошные is not defined, сначала на «deputiesData», затем на «против», при том, что расширение как-бы работает.
ivan386
за() и против() работают только на страницах результатов голосования. Попробуйте здесь: http://vote.duma.gov.ru/vote/95967
0x9d8e
Теперь работает. Дело было в том, что увидев под заголовком «Файл insert.js» точно такой-же удивился почему два куска кода из одного файла под двумя одинаковыми заголовками, перепроверил, но сделал «по ману». Не знаю, или в статье сначала ошибка была, или я глючу.
ivan386
Была ошибка. Я исправил.
0x9d8e
Спасибо, очень пригодится. Тем более что не получается (да и не хочется) следить кто там как голосует, а выбор хочется сделать обоснованный.
Жаль нельзя делать запросы на выборку законопроектов. Скажем выбрать голосования по запретительныем и ужесточающим нововведениям, где категория != ПДД. Всё равно придётся всё это читать, но уже полегче.
stas404
Не подсказывайте депутатам идеи для новых законопроектов.
Old_Chroft
Адинэсники смотрят на вас с недоумением…
Кстати, лет 8 (восемь!) назад видел CSS, где классы и id на русском — и что удивительно, даже всеми проклинаемый в то время IE6 его вполне переваривал.
Mirnin
Адинэсники заметили неладное только когда начали читать комментарии. Сам адинэсник, читал будто так и должно быть…