Заметки

Фудиари для Обсидиана

Вслед за первым плагином для Obsidian пару недель назад выкатил второй. Считает КБЖУ (калории, белки, жиры и углеводы) в пище. Помогает не переедать на пустом месте — всё-таки на глаз трудно оценить, сколько слопал за день, и можно ли позволить себе вон тот пончик.

Короче, полезная штука, если вы:

  1. Толстяк (как я)
  2. Хотите перестать им быть (как я)
  3. Ведете заметки в Obsidian (как я) 🙂

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

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

В репозитории по ссылке выше есть примеры.

2024-04-14 22:36:58 готово TypeScript Obsidian

Внешний вид Фастаймера

Завернул отрисовку Фастаймера через выноски: это механика Obsidian, позволяющая превращать обычную цитату в оформленный блок текста, привлекающий внимание читателя. Вы наверняка видели блоки в духе «совет» и «обрати внимание» — вот это и есть выноски.

Подробнее можно прочитать в справке Obsidian.

В общем, теперь таймер принимает разный цвет в зависимости от состояния (активный интервал — голубой, успех — зеленый, провал — красный). Кроме того, я немного уплотнил текст и отточил формулировки:

Пример

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

2024-03-09 17:44:32 Obsidian TypeScript

Маленькие радости

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

Наконец, берет «Сникерс», задумчиво крутит его в руках. Пододвигает себе ещё два и широко, с видимым удовольствием улыбается в усы :)

2024-03-06 21:22:43 тем временем Грузия

Не только лишь все

Завидной глубины комментарий из документации к методу WriteJSON() XDTOSerializer'а:

Не только лиь все

Ну да, метод дампит данные в JSON, а не в XML. Так что поспорить сложно, не все типы данных можно упаковать в XML с его помощью (если быть точным — никакие). Если бы дальше не пошла очевидная копипаста из справки к WriteXML() — счёл бы пасхалкой от разработчиков :)

2024-02-25 22:49:48

Фастаймер для Обсидиана

Разработчики Obsidian на днях заапрувили один из моих пет-плагинов на TypeScript — Фастаймер, трекер интервального голодания. Он добавляет в хранилище новый блок кода: вводишь в него дату начала окна голодания и получаешь дату завершения, время до этого момента и раскладку по зонам, которые предстоит пройти.

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

Думаю немного доработать визуальную часть (сейчас всё выводится текстом без какого-либо оформления). А ещё — прикрутить функции расчета статистики, чтобы можно было на ходу рисовать красивые графики в духе Charts и показывать ачивки. У меня была эта механика в реализации этого же приложения на Python, но я вряд ли к ней вернусь — в хранилище Obsidian эту задачу решать проще, чем раскатывать на компьютер дополнительную утилиту.

Короче, зацените плагин! :) В библиотеке Обсидиана его можно найти по имени (Fastimer). Или, при желании, установить вручную из репозитория.

2024-02-13 01:08:58 готово TypeScript Obsidian

Do? Do Not?

На одном из проектов у нас есть две общающиеся друг с другом системы: ERP и CRM. Обмен данными сделан по-взрослому: поднят push'n'pull сервер, прописаны подписки на события, подняты REST API и много другой интересной технической обвязки, но я сейчас не про неё.

Среди прочей логики, там так: если в CRM появляется новый контрагент — его данные отправляются в ERP. На днях с этим возникла проблема: один из контрагентов не отправлялся из CRM ну вот вообще никак, сколько его не записывай. Полезли разбираться, подозревая худшее: CRM написана на PHP (ничего личного, просто это не наш технический стек) и там много разного легаси. Выстрелить себе в ногу проще, чем высморкаться.

Однако особенно долго копаться не понадобилось. Открыли страницу контрагента в CRM и увидели, что у него стоит галка «Do Not Export To ERP», которая, собственно, блокирует отправку. Короче, очевидная ошибка какого-то менеджера.

Убираем галку, закрываем тикет?

Well yes, but actually no

Это решит проблему с этим конкретным контрагентом, но не причину, по которой она возникла. А она в интерфейсе, конкретно — в названии опции: используется «do not», которого желательно избегать из-за того, что пользователям сложнее правильно считать формулировку. К простому «do» это, кстати, тоже относится.

Программистам часто непросто понять, почему так: мы привыкли мгновенно рассчитывать в уме булевые выражения и вариации в духе «не (не истина)» для нас — обычное дело. А вот люди с другим бэкграундом могут путаться. Совсем чуточку, но иногда и этого достаточно, чтобы в горячке дня воспринять «do not export» как «do export», ткнуть опцию и побежать дальше.

Отсюда выводим правильное решение: переименовать галку. Подойдёт «Disable Export» или «Stop Export». В голову ещё приходит «Prohibit Export», но это скорее про межличностные отношения и вообще, запрет что-то делать не означает, что это что-то не будет сделано :)

2024-01-14 22:25:45 работа английский

Последний мет

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

В остальном хватает bruh moments: например, проект не открывается в современной Visual Studio (нужно указывать CMake вручную). Код довольно небрежный, документации нет, комментариев и оформления по большому счёту тоже. Разработчику без опыта в С++ может быть непросто вкатиться.

Позабавил нейминг:

long CAddInNative::FindMethod(const WCHAR_T* wsMethodName)
{ 
    long plMethodNum = -1;
    wchar_t* name = 0;

    ::convFromShortWchar(&name, wsMethodName);

    plMethodNum = findName(g_MethodNames, name, eMethLast);

    if (plMethodNum == -1)
        plMethodNum = findName(g_MethodNamesRu, name, eMethLast);

    delete[] name;

    return plMethodNum;
}

Со строк выше на нас смотрит необъяснимая любовь автора кода к сокращениям: вот что ему мешало назвать переменную "eMethodLast", а не "eMethLast"? В конце концов, у нас уже есть "wsMethodName" и "plMethodNum".

Возможно, это такая пасхалка с отсылкой на Breaking Bad. Тогда, конечно, уверенный лайк :)

2023-12-17 19:34:42

Пропущенный съезд

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

― Энди Вейер, «Марсианин»

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

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

2023-11-14 09:34:10 код с запашком

Бытовой героизм

Поднимал какое-то время назад Swagger для внутренней API-шки. Пока возился — стало понятно, что часть функционала в документацию включать не нужно. Поискал способ, как это сделать без костылей — наткнулся на забавный вопрос на GitHub'е.

Чем забавный? Ну, невольно вспомнил про Мисту. В среде 1С-разработчиков это синоним слова "токсичность": мол, спросишь там что-нибудь — получишь ушат помоев за шиворот вместо ответа. Здесь, конечно, не так всё запущено, но ребята, с завидным упорством ссылающиеся на 14-и страничный мануал, здорово рассмешили.

Одно радует: к концу треда всё же нашёлся отважный повстанец, который просто взял и запостил нужный параметр для декоратора.

Не все герои носят плащи, ей-богу.

2023-09-24 23:06:30 работа Swagger

Румынская фича

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

При этом фича везде зарегулирована по-своему: где-то просто работает, где-то нужна регистрация.

Читаю:

Скрин

🤔

  • Португалия: да
  • Пуэрто-Рико: нет
  • Катар: да (с регистрацией)
  • Реюньон: да
  • Румыния: да (с регистрацией) (но бойтесь Дракулы)

Не знаю, как ещё объяснить это кладбище.

UPD: Нашёл разгадку. Могильные кресты означают, что за регистрацию нужно заплатить 700 баксов.

Объяснение про Дракулу мне нравилось больше.

2023-08-30 01:02:24 тем временем работа

Новый Фастаймер

Выпустил новую версию своего консольного таймера для интервального голодания. Сам таймер я написал где-то год назад, когда меня в очередной раз расстроило приложение Zero для Android: в нём не работала какая-то совсем примитивная функция вроде просмотра конкретного интервала.

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

2023-08-23 20:58:32 готово Python

Таков путь

Немного экзистенциальной безысходности от СП (на случай, если вам её почему-то не хватало):

Исключение генерируется

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

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

Исключение генерируется

2023-07-17 00:46:01

Реостат для запросов

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

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

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

2023-05-27 13:20:05 Битрикс

Ду ю спик инглиш?

Диалог входа в хранилище конфигурации актуальной версии платформы (8.3.22.1923, если быть точным), запущенной с английским интерфейсом:

Диалог входа

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

Может, их и вовсе нет.

2023-05-14 10:15:16

Опочтовиться

Пополнял словарь в попытках описать англоговорящему приятелю свои последние приключения и вычитал про красочную идиому «to go postal». Означает что-то вроде «рехнуться от злости»; появилась где-то между 80-и и 90-и годами в Штатах после серии довольно безумных инцидентов, в которых работники почты ехали крышей и нападали на всех подряд, включая коллег и посетителей.

В общем, выражение на первый взгляд звучит забавно, но уж больно мрачная история за кулисами. Думаю, продолжу применять старое-доброе «to go ballistic». Буквально «разозлиться так, что стать похожим на ракету, потерявшей управление», «взорваться от гнева».

В русском языке, кстати, есть похожие «ракетные» коннотации, но они почему-то про более управляемую или полезную, что ли, историю. Типа, пожар в заднице был настолько силён, что бедняга покинул Землю и успешно приземлился на Марсе (а не просто взорвался) :-)

2023-05-08 09:12:34 английский

Пауза()

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

Метод ВызватьПаузу

Странное ограничение, если честно. С одной стороны, адекватный разработчик и так не будет в клиент-серверном вызове делать искусственную паузу, с другой — кому чешется, всё равно её там сделает (через проверку времени в цикле, например). Стоило ради security by obscurity огород городить?

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

2023-04-30 14:30:43

Сделай это педантично

Ежедневная пальма первенства в номинации «самый философский код» уходит автору этого элегантного способа проверить, что две булевые переменные не равны друг другу:

If DataStructure.Property("AmountVATIn")
    And ((DataStructure.AmountVATIn And NOT SearchPriceIncludesVAT)
    OR (NOT DataStructure.AmountVATIn And SearchPriceIncludesVAT)) Then    
    Price = RecalculateAmountOnVATFlagsChange(Price, DataStructure.AmountVATIn, TabSectionLine.VATRate);
EndIf;

Думаю дописать сюда что-нибудь вроде «And Not (DataStructure.AmountVATIn = SearchPriceIncludesVAT)», чтобы придать происходящему тонкую нотку безумия.

2023-03-21 20:26:43 работа код с запашком

Повествование через окружение

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

Например, недавно отмечал с коллегами десятилетие компании в местном гольф-клубе. Шары нужно было отправлять в полет со второго этажа; перил там по понятным причинам нет, но натянута сетка на случай, если кто-то потеряет равновесие.

Фотка с сайта клуба, чтобы было понятнее.

Рядом висят предупреждения: прыгнете в сетку своим хотением — заплатите десять тысяч дирхам. Запишете этот прыжок веры на камеру — готовьте ещё пять тысяч.

Чувствуется история за кулисами, да?

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

UPD: Ещё один отличный пример откуда-то из сети.

2023-03-19 15:51:48 тем временем работа

Помедленнее, я записываю

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

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

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

2023-03-05 15:54:51 Битрикс готово

REST-сервис для Service Manager

На этой неделе написал REST-сервис для настройки к нашему сервис-менеджеру (это такая конфигурация для управления инстансом 1cFresh). Развернуть контур разработки у нас — задача регулярная, и базу менеджера каждый раз приходилось тюнить руками: подкручивать витрину, менять адреса приложений, перезаписывать регламентные задания и так далее.

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

Эта — хороший пример. Настройка сервис-менеджера не была проблемой (запустить приложение и покрутить настройки), но о ней нужно было думать и её нужно было делать. А теперь — нет:

  1. Есть JSON-файл со всеми настройками;
  2. Есть REST-сервис для его обработки;
  3. Есть скрипт, который засунет первый во второй;
  4. Есть пайплайн, который сделает всё это сам.

Короче, ботинок тёр ногу, а теперь перестал. Йаху!

2023-02-25 19:02:49 работа

Ранее Ctrl + ↓