В 2008 году Центр городского экономического развития опросил 4387 низкооплачиваемых рабочих в Чикаго, Лос-Анджелесе и Нью-Йорке. Они хотели определить степень нарушений трудового законодательства в основных секторах экономики США.

Они обнаружили, что:

  • 76% тех, кто работал более 40 часов, не получали сверхурочную оплату, предусмотренную законом.
  • 68% выборки испытали хотя бы одно нарушение, связанное с оплатой труда, на предыдущей рабочей неделе.
  • Средний рабочий потерял 51 доллар из своего недельного заработка в 339 долларов из-за кражи заработной платы. Эти убытки составляют более 2652 долларов убытков за один год (из общей средней заработной платы в 17 616 долларов).

Кроме того, по оценкам этого исследования, рабочие по всей стране теряют коллективно 50 миллиардов долларов в год из-за кражи заработной платы.

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

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

В течение последнего семестра моя команда и я в Hack4Impact имели возможность работать с Community Legal Services of Philadelphia (CLS), бесплатной юридической клиникой, которая обслужила более миллиона малообеспеченных филадельфийцев с момента своего основания в 1966 г.

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

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

  • Пользователь должен иметь возможность добавить свой файл LocationHistory.json (загруженный из Google Takeout).

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

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

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

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

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

Если вы не знали, Google следит почти за всем, что вы делаете. В частности, они отслеживают вашу историю местоположений, если у вас есть телефон на базе Android, и вы еще не отключили его. Если вы хотите, вы можете загрузить свою историю на сегодняшний день, перейдя на takeout.google.com/settings/takeout и загрузив файл в формате JSON (будьте осторожны ... он может быть огромным).

Один только мой LocationHistory.json имел размер около 59,9 МБ (у меня был телефон Android около двух лет), но некоторые из клиентов, которые будут использовать нашу систему, могли иметь истории местоположений размером несколько сотен мегабайт . Попытка просто загрузить весь файл JSON в память приводит к зависанию браузера примерно на 30 секунд перед тем, как вызвать классическую ошибку «Aw Snap» на Chrome (обычно указывающую на ошибку нехватки памяти).

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

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

Первым решением, которое я придумал, было попытаться разделить файл на более управляемые части по 512 килобайт за раз. Однако у этого есть некоторые врожденные недостатки, в основном то, что файл, который я пытаюсь загрузить, содержит большую «строку», которая имеет формат объекта JSON (но еще не является объектом). Таким образом, когда я решаю разделить и обработать файл на последовательные части по 512 КБ, я легко могу столкнуться с ситуацией, когда я разрежу «объект» пополам.

Итак, теперь мне нужен был способ отслеживать наполовину завершенные объекты / объекты, которые были отрезаны, и соответственно добавлять / добавлять их к следующим фрагментам, чтобы убедиться, что все будет правильно проанализировано. Хотя файл Google LocationHistory.json относительно однороден, способ разделения фрагментов - нет. К счастью, существует уже существующая библиотека, которая поможет позаботиться обо всех возможных крайних случаях. Войдите в Oboe.js.

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

Сам код для гобоя относительно легко настроить. Рассматриваемый нами файл JSON имеет следующий общий вид.

{ 
 "locations": [ {
 "timeStampMs": ...,
 "latitudeE7": ...,
 "longitudeE7": ...,
 "accuracy": ...
 }, {
 "timeStampMs": ...,
 "latitudeE7": ...,
 "longitudeE7": ...,
 "accuracy": ...
 },
 ...
 ]
}

Согласно документации Oboe, узел locations должен быть нацелен, и любой его подобъект будет передан в функцию обратного вызова, как показано в примере кода ниже.

Затем нам нужно выяснить способ передачи этой функции фрагментами. Сама функция фрагментации немного сложнее, но основная функция заключается в обработке файла частями по 512 КБ за раз. Функция принимает сам файл (из входа) и экземпляр oboe.js (в нашем случае переменную os).

Обратите внимание на строку 11 следующее:

oboeInstance.emit('data', chunk);

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

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

Инициализировать карту в div с id='mapid' довольно просто:

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

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

Возвращаясь к нашему коду экземпляра oboe.js, мы можем немного отредактировать его, чтобы учесть добавление библиотеки PruneCluster:

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

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

Вот и все. Парсинг Истории местоположений Google на интерфейсе. Сервер не нужен. Фактически, я сейчас размещаю сайт на странице github по адресу hack4impact.github.io/cls.

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

Вы можете найти исходный код проекта в нашем репозитории.

Моя полная команда: Менеджер по продукту: Кришна Бхаратала, члены команды: Кэти Цзян, Даниэль Чжан, Санти Буэнахора и Рэйчел Х.

Первоначально опубликовано на сайте medium.freecodecamp.com участником Hack4Impact Abhinav Suri 20 марта 2017 г.