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

Как рынок жилья?

Как устроен местный рынок труда?

Насколько улицы удобны для пешеходов?

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

Введите Citrics

Цель Citrics — предоставить пользователям исчерпывающие сравнительные данные о городах в простом для понимания формате, что позволит принимать решения на основе данных.

Проект был задуман как часть Lambda Labs, заключительного подразделения Lambda School, где у студентов есть возможность провести четыре недели, работая с удаленной межфункциональной командой разработчиков. Нашим заинтересованным лицом был сотрудник Lambda. Мы будем повторять существующую кодовую базу, которая включает интерфейс React/Redux, развернутый на AWS, и серверную часть Java/Spring, подключенную к базе данных PostgreSQL.

Процесс планирования

Наша заинтересованная сторона поставила перед нами две основные цели для этой итерации приложения.

  1. Создайте более привлекательный способ отображения доступных данных.
  2. Создайте для пользователей возможность поиска по определенным критериям (например, города с арендной платой за одну спальню от 800 до 1500 долларов и рейтингом ходьбы более 85).

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

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

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

Кодирование, катя камень в гору

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

Исходный городской объект

Например, исходный объект города включал несколько полей, которые были основными данными для многих сравнительных графиков, в том числе:

  • Средний возраст
  • Семейный доход
  • Средняя стоимость дома
  • Индекс стоимости жизни
  • Средняя температура
  • История населения
  • Историческая стоимость дома

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

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

Пересмотренный городской объект

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

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

Приготовление лимонада из лимонов

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

Работа с Plotly

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

Чтобы удовлетворить требования каждого отдельного графика, я начал с двух универсальных объектов: один для хранения индивидуальных данных каждого города, а второй — для указания Plotly, как генерировать график.

Затем я написал функцию graphBuilder для генерации необходимых данных и массивов компоновки, необходимых компоненту ‹Plot /›. Эта функция была структурирована как оператор if/else для каждой категории, чтобы учесть каждую индивидуальную разницу в именах полей источника данных, заголовках осей и т. д. При необходимости эта функция вызывала другие вспомогательные функции для заполнения дополнительных фрагментов данных по мере необходимости. необходимо для построения каждого графика.

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

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

Задним числом всегда 20/20…

К тому времени, когда наша команда подошла к концу этого проекта, мы многого достигли, в том числе:

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

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

Если бы у нас был еще месяц

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

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

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

Извлеченные уроки

Есть две основные темы, которые я вынес из этого проекта.

  1. Важность подробной документации по конкретному проекту
  2. Пишите функции и код как можно более обобщенно

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

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

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

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

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

экспортировать константу FETCH_DATA_START = ‘FETCH_DATA_START’

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

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