Advent of Code
4 поста
4 поста
4 поста
1 пост
6 постов
21 пост
Забавную ситуацию вспомнил: как-то на ревью коллега стал меня убеждать в необходимости своих правок. Обосновывал тем, что "из теста вернули с комментарием — очень долго работает запрос, не можем дождаться ответа и обрубаем". Поэтому он решил изменить способ итерации, чтобы всё ускорилось 🚀
Краткая суть: был метод, возвращавший некие данные за период (12 месяцев с отмоткой назад, endDate = 12 месяцев назад, нижняя граница). Внутри себя - он итерировался по этим месяцам, дергал другой метод для получения помесячных наборов, и агрегировал их. Конструкция была такая:
LocalDate startDate = LocalDate.of(2022, 11, 1);
LocalDate endDate = startDate.minusMonths(12);
for (LocalDate date = endDate; date.isBefore(startDate); date = date.minusMonths(1))
Подобный метод-агрегатор был не один, и на ревью первого из них я коллеге посоветовал генерировать набор дат вызовом библиотечного АПИ datesUntil с шагом в месяц, и дальше сгенерированный набор использовать. Это дело благополучно забылось, даты продолжали создаваться внутри for и кочевали с копипастой, дожив до обсуждаемого момента 🤷🏻♂️
Я сильно засомневался, что в совершенно ненагруженном вызове, при итерации по 12 элементам — иной способ итерации даст какую-то заметную разницу. Сделал иллюстративные наброски в JShell (полноценные бенчмарки лень было, да и ни к чему) для сравнения разных способов с исходным. Типа таких:
Внимательный читатель уже видит суть проблемы. 🕵🏻♂️ Самый внимательный - уверен, увидел уже на третьем абзаце!
Результаты, ожидаемо, отличались крайне незначительно:
Dates list for - i => 692
Dates in-place generated for each => 704
Dates list for - each => 708
Dates array for - i => 708
Dates list forEach => 712
Dates list while + iterator => 1060
Разумного объяснения не было 🤯 Стали закрадываться мысли - а не хлопнуть ли нам взяться ли нам за JMeter, профайлер, снять граф... Углубиться, так сказать, до дна.
Не пришлось - взглянул повнимательней и заметил разницу: date = date.minusMonths(1) vs date.plusMonths(1). Вот оно! 🥳 Банальный бесконечный цикл с отмоткой назад от нижней границы периода.
Будь я бдительней - раньше бы заметил, что при копипасте цикла между методами - поменялось назначение границ периода, а итерация осталась старой. В одном методе работало ожидаемо, в другом - 💥.
Используй коллега библиотечный вызов - получал бы единообразно нужный период. И места для ошибки меньше, и проверять проще. И протестировано всё авторами. И оптимизацию бесплатную могут завезти.
Молоко, говорят - просто полезно для здоровья. Его даже за вредность дают. Но не всем 🐱
Продолжая развлекаться с нейросетями - поставил АПИшку к Midjourney (https://github.com/novicezk/midjourney-proxy).
Важно: это только АПИ, смысл её - в переадресации запросов к дискорд-боту Midjourney, адаптер, короче. Тестовый период и/или подписка - завязаны на дискорд-аккаунт. И похоже, что триала сейчас нет, или он крайне ограничен.
Ещё минусы:
Вся документация, примеры, встроенный "свагер" - на чистейшем... китайском? Иероглифами, короче.
Токен надо доставать через браузер, предварительно залогинившись в Дискорд.
Плюсы: она работает 🫡 И это удобней, чем листать ответы бота в дискорде, продираясь через тысячи сообщений.
Ставить как обычно - склонировать/скачать с гитхаба. Сделал docker-compose для удобства, собрать/обновить контейнер можно аналогично сборке freeGPT (docker-compose build).
Текстом можно скопипастить тут.
Токен (user-token): зайти на сервер Midjourney в Дискорде, в браузере в консоли разработчика (F12) - вкладка Сеть/Network. В заголовках запроса будет Authorization.
guild-id: Номер сервера (из адресной строки - первый набор цифр)
channel-id: Номер канала (из адресной строки - второй набор цифр)
Если регистрируется свежий аккаунт - предварительно через Дискорд нужно принять ToS Midjourney:
Посмотреть свагер и подёргать запросы можно тут: localhost:1339/mj/doc.html#/home
С иероглифами документация выглядит даже забавно где-то 🙃 К счастью, иконки и цвета кнопочек остались общепонятными.
Запрос генерации
Посмотреть очередь генерации
С новым аккаунтом не прокатило, предлагает подождать и повторить позже. Разве что где-то найти бюджетную тестовую подписку.
Есть у меня такой pre-push хук - автоматом прогоняет тесты локально, через maven. Подключается по необходимости через отдельные git конфиги для проектов.
Стащил его, судя по всему, отсюда: https://gist.github.com/arnobroekhof/9454645. Потом допиливал немного – чтобы он с многомодульными проектами работал корректно. Может, ещё что-то по мелочи причёсывал.
И он отлично работает (разве что можно через sed попробовать результаты по всем модулям агрегировать).
Но вот проблема – на текущем проекте везде gradle, а под него я что-то не могу найти похожего простого решения
Есть ли оно?
Очень легко и удобно оказалось в Докере (compose) связывать сети. Раньше сервисы обычно пихал в один compose-файл (или, точнее, лень было разделять специально – как шли “из коробки” – так и запускались).
Но стало неудобно. Совсем неудобно стало, что каждый норовит себе отдельную БД поднять соседним контейнером. И всё равно приходится лезть, и монтирование данных для БД на хостовой ФС прописывать. Ближе к делу:
Сеть с драйвером bridge по умолчанию создается, но пусть явно будет прописано.
И всё. Оба сервиса живут внутри сети postgres_net, друг-друга видят по названиям, по ним же и пингуются. huginn использует pglocal в своём конфиге.
Минус вижу – порядок ожидания нужных служб сделать посложней, чем с depends_on и службами в одном файле.
Или через docker-compose -f service1.yml -f service2.yml up все нужные службы пускать (тогда depends_on сработает). Мне не удобно так.
Или через command и внешние скрипты ожидания приходится. Хорошо, что wait-for-it.sh уже придуман до нас!
Такие вот удачные модельки-конструкторы у нас делают.
Вчера фланировал по центру и забрёл в винный подвальчик, где ими торгуют.
Всяких размеров есть, но мелкие прям хороши:
не надо резать, не надо клеить
симпатичные
собрать можно за час, а не за месяц/два/никогда
своё, родное ❤️
Годнота!
скопипячено из телеги
Оказавшись на задворках Москвы - с меня слетела шляпа довелось сходить в любопытный театр (Этнотеатр) на не менее любопытный спектакль – Комедия о Фроле Скобееве.
Фото с etnoteatr.ru
Современная постановка сделана по пьесе конца XIX века, а та, в свою очередь, написана по произведению века XVII (или начала XVIII, тут мнения расходятся).
И вот прямо так интересно оказалось всё это вместе:
И факт старинности основы спектакля (русский театр, всё же, в массе известен века с двадцатого, если правильно помню).
И подход, который нашёл постановщик к подаче действа – сцена в виде этакой балаганной карусели – здорово добавляет антуража.
И костюмы проработаны, и игра цепляет, и “осовремененено” совсем каплю и в меру – “виной” тому, видимо, этнографичность и историчность театра 🙂
Тем более, что у себя, в Питере, я подобного сочетания не обнаружил, да и в принципе постановок по этой пьесе не встретил. Рекомендую!
Традиционно приготовлено из канала :(
Частенько встречаются перечисления, дополнительно хранящие некие значения.
Чтобы в дальнейшем можно было, например, при сериализации в json это самое значение подставлять автоматом. Тогда возникает обычно и обратная задача – десериализовать (распарсить) значение обратно в Enum.
Вот мне и надоело копипастить туда-сюда все эти методы (сериализации/десериализации) между классами Enum. Их прям очень много таких оказалось на нынешнем проекте. Решил сделать один раз утилитный метод и в проекте им пользоваться. Благо, время позволило.
В принципе, подобный метод есть в недрах Apache Commons EnumUtils, но он работает только со строковым параметром String enumName, плюс выкидывает стандартное исключение. А обычно надо выкинуть некое кастомное, принятое на проекте. Так и родился свой костылёчек, как оно зачастую и бывает.
Импорты копировать не буду, полагаю, IDE предложит их подставить по выбору, если автоматически не сможет этого сделать. Сам утилитный метод примитивен:
Разве что – в реальных условиях он выбрасывает не IllegalArgumentException, а кастомное исключение.
ValuedEnum – это интерфейс, который реализуют все Enum со значением. Вот он:
Сделан sealed, чтобы при имплементации (методом копипасты с опорой на "так сойдёт") – был дополнительный “маячок”, заставляющий обратить внимание – это именно Enum со значением.
Примеры реализаций и тесты картинками вставлять смысла не вижу - там уже достаточно много и проще скопировать-запустить с сайта или найти в говнотелеге.
Результаты тестов привожу:
Всяческого разумного порицания жду нетерпеливо, неразумного - со смирением :)
Накопилось ещё некоторое количество “приёмчиков” работы с Open/Libre Office. Если предыдущая заметка на эту тему была посвящена LO Writer, то на сей раз – “подопытным” выступает пакет Calc (электронные таблицы).
Для затравки – простое, но часто нужное действие – перемещение строк и столбцов таблицы.
Выделить нужный диапазон при помощи выделения всего столбца/строки (для этого можно щелкнуть ЛКМ на заголовок столбца/строки).
Кликнуть и зажать ЛКМ внутри выделенного диапазона (на самих выделенных ячейках с данными, не на заголовке).
Тащить мышью диапазон, затем при отпускании:
Перезапишет тот диапазон ячеек, на который был перетащен
При зажатом Alt – поменяет диапазоны ячеек местами
Выделить нужный диапазон ячеек (или весь столбец/строку целиком по клику на заголовок).
Верхнее меню: Данные -> Текст по столбцам.
ЛКМ на столбец в нижней области открывшегося окошка (для выделения диапазона, в котором требуется преобразование).
Выбрать тип столбца – Ок.
Эту операцию удобно проиллюстрировать примером: есть столбец с датами, которые хранятся, как текст (т.е. тип данных внутри офиса у них – текстовый, такое часто встречается при работе с кривыми выгрузками для Экселя). Использование меню Формат – Формат чисел – Дата не поможет в данном случае, т.к. дата хранится в виде строки '2023-06-16.
Вот здесь и пригодится изменение типа данных ячеек, после которого их уже можно будет отформатировать, как даты (чтобы изменить отображение, если надо).
Ещё одна отличная функция Calc – можно получить итоговые значения для заданного набора данных (агрегировать). Буквально – в несколько кликов! Что отдельно приятно – функция работает с фильтрами.
Ctrl + * – выделить все данные на листе. Можно выбирать и не весь диапазон, естественно.
Данные -> Промежуточные итоги.
Выбрать столбец, по которому агрегировать.
Выбрать столбец для которого нужно получить агрегат.
Выбрать агрегатную функцию.
Кнопками слева от таблицы (1-2-3 и +/-) можно переключать вид (свернуть-развернуть).
Вывод, в общем-то, простой: если поискать, поинтересоваться и немного глубже узнать возможности электронных таблиц Calc – с их помощью можно достаточно широкий круг рутинных задач решить быстро и просто. Пользуйтесь с удовольствием!
Источник (с гифками-примерам).