Перейти к основному содержимому

Генераторы

Тег <gen> — это «фабрика» данных. Атрибут type выбирает, какой генератор использовать; каждый остальной атрибут — параметр этого генератора. Генератор производит значения последовательности:

<sequence name="Status">
<gen type="text" value="new,active,closed"/>
</sequence>
./run demo.tdc
active
new
closed
active
closed

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

Где может жить генератор

<gen> живёт там, где объявляются данные:

  • внутри <sequence> — простой, составной по значению, составной или условной — заполняет count значений (или столько, сколько в отфильтрованном подмножестве, если у последовательности задан parent). «Массив длиной count» — это модель, которой удобно рассуждать; движок по умолчанию выдаёт значения по одному, пока файл течёт, и массив не держит;
  • внутри <case> у <mix> — одна ветка процентного разбиения.

В одном теле последовательности может жить несколько <gen>, и что каждый из них означает, решает name. Оставьте генератор без имени — его значение склеивается в собственное значение последовательности вместе с литералами <data> рядом: составная по значению. Дайте name — он становится отдельным полем, которое читается как ${{Имя.Поле}}: составная последовательность. Оба вида свободно смешиваются в одном теле.

Прямо в блоке вывода генератор не разрешён. <gen> как непосредственный ребёнок <line> — это ошибка TDC131: блок только форматирует текст, он не генерирует. Чтобы подставить сгенерированное значение в вывод, объявите именованную последовательность и сошлитесь на неё через ${{Имя}} — см. Вывод и форматирование.

Общие атрибуты

Эти атрибуты работают на любом генераторе; остальные зависят от type.

АтрибутОбязательныйЧто делает
typeдаКакой генератор использовать (см. таблицу ниже)
nameнетДелает генератор полем своей последовательности, читается как ${{Имя.Поле}}. Без него значение попадает в собственное значение последовательности
ifнетУсловие ветки внутри условной последовательности — срабатывает первый истинный <gen>
commentнетСвободный комментарий, движок его игнорирует

Генераторы

У каждого типа своя страница со всеми параметрами и разобранными примерами.

typeЧто производит
textЗначение из набора — равномерно или по точным percent
numberЦелое число в диапазоне или строка фиксированной ширины из цифр
templateВстроенные правдоподобные данные и технические идентификаторы
fileЗначения из ваших собственных файлов и колонок CSV
dateДата или дата-время в диапазоне и заданном формате
symbolСтрока из символов набора или именованного алфавита
regexСтрока по конечному регулярному выражению
advanced_regexRegex плюс взвешенный выбор между альтернативами
increment / decrementВозрастающий и убывающий счётчики
timeseriesВременной ряд — тренд + сезонность + шум
patternРаспределение в форме нарисованной кривой
httpЗначения отвечает ваш собственный сервис, пачками
runningИтог, переносимый по колонке, — остаток, рекорд
statОдно число на весь прогон — среднее, сумма, максимум
formulaАрифметика по другим колонкам той же строки

О пресетах. Старого type="preset" больше нет. Алгоритмические идентификаторы — UUID, IBAN, номера банковских карт, git SHA, национальные идентификаторы — теперь являются путями template: глобальные под префиксом common. (например, common.id.uuid), страновые — под своей страной (например, russia.tax.inn_person для ИНН). Полный каталог — на страницах template и справочника генераторов.

Объявленные доли или розыгрыш из источника

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

Вы объявили доли — получите их точно. Там, где значения перечислены в самом конфиге, TDC раскладывает квоту по строкам и перемешивает её. percent="30,70" — это 30 и 70, а не «примерно». Если долей не задать, они равные, и равные тоже точные:

10 значений на 1000 строк
text:  100 100 100 100 100 100 100 100 100 100

Так работают text и <mix>, а numberкогда его percent делит группы length: length="2,3" percent="70,30" на тысяче строк даёт ровно 700 и 300.

missing= на том же генераторе меняет счётчики

Квота сначала раскладывается по всей колонке, а missing= затем гасит ячейки, не глядя, какое значение в них лежит. Поэтому percent="90,10" missing="0.5" на тысяче строк даёт примерно 450 / 50 / 500 пустых: СООТНОШЕНИЕ уцелевших значений всё ещё 90:10, а абсолютные счётчики — не те, что дал бы один percent.

Это не погрешность округления, и никакой порядок применения этого не исправит: два требования несовместимы. Ровно 100 строк fail И половина файла пустая означало бы, что fail — это 100 из 500 уцелевших значений, то есть 20 %, а не запрошенные 10 %. Если вам нужно точное число значений в готовом файле, не ставьте missing= на этот генератор.

Простой числовой диапазон — уже другой род. value="1..10" разыгрывает, и на тысяче строк десять значений выходят как 97 84 106 112 107 102 90 95 86 121 — разброс розыгрыша, а не квота. Правило в том, что записал конфиг: выписанные доли соблюдаются точно, в диапазон запускают руку.

Вы указали на источник — получите розыгрыш. Файл или пул — это набор, в который вы лезете по разу на строку, независимо. На тех же 1000 строках счёт ложится так, как выпадет:

те же 10 значений, но из источника
file:   81  88  93  97  98 102 103 105 111 122
pool:   90  92  95  97 100 102 104 105 106 109

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

Когда источнику нужны пропорции, он берёт их из самих данных. CSV, который знает, как часто продаётся каждый товар, говорит об этом колонкой, а weight="sales" заставляет розыгрыш ей следовать — точно, как percent. Это честное место для цифр: у справочника на 3000 позиций частоты лежат в файле, а не у вас в конфиге.

Форматирование на любом генераторе

Несколько атрибутов работают на любом type. Значение генерируется как обычно, а затем переоформляется на выходе — генератору всё равно, какой из этих атрибутов вы к нему прикрепили.

case= / mask= — регистр букв и маски отображения

Когда брать. Сырое значение верно, но должно выглядеть определённым образом: колонка, которая обязана быть целиком в верхнем регистре, или простое число, которое должно читаться как оформленный идентификатор.

case= меняет регистр букв; mask= разбивает и переставляет символы по фиксированному шаблону. Оба оформляют весь генератор. Пример ниже прогоняет одни и те же четыре города через три последовательности — сырьё, затем тот же список с case="lower" и case="upper". order="sequential" держит города в ногу, чтобы колонки совпадали.

<sequence name="Raw"><gen type="text" value="Moscow,Berlin,Paris,Rome" order="sequential"/></sequence>
<sequence name="Low"><gen type="text" value="Moscow,Berlin,Paris,Rome" order="sequential" case="lower"/></sequence>
<sequence name="Up"><gen type="text" value="Moscow,Berlin,Paris,Rome" order="sequential" case="upper"/></sequence>
...
<data>${{Raw}} -> case="lower": ${{Low}} | case="upper": ${{Up}}</data>
./run cities.tdc
Moscow  ->  case="lower": moscow  |  case="upper": MOSCOW
Berlin  ->  case="lower": berlin  |  case="upper": BERLIN
Paris   ->  case="lower": paris   |  case="upper": PARIS
Rome    ->  case="lower": rome    |  case="upper": ROME

mask= делает тот же трюк для «красивых» идентификаторов — голое 37898432363 с mask="xxx-xxx-xxx xx" выходит как 378-984-323 63. Все слоты маски (x, w, *), все режимы регистра и многошаговые цепочки фильтров полностью разобраны на Маски и регистр.

order= / cycle= — порядок значений

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

По умолчанию order="random". Поставьте order="sequential" — и строка i берёт i-е значение по порядку, возвращаясь к началу, когда список заканчивается. cycle="false" превращает это возвращение в явную ошибку — удобно, когда исчерпание значений должно быть сбоем, а не тихим повтором.

Оба атрибута читают три генератора, у которых есть что обходить: text (его список через запятую), file (его строки или колонка CSV) и date (его range=, обходимый шагами step=). У остальных типов обходить нечего — жеребьёвка не заканчивается, — поэтому движок их отвергает (TDC015), а не принимает просьбу, которую не может выполнить.

<sequence name="Rand"><gen type="text" value="янв,фев,мар"/></sequence>
<sequence name="Seq"><gen type="text" value="янв,фев,мар" order="sequential"/></sequence>
...
<data>random=${{Rand}} sequential=${{Seq}}</data>
./run order.tdc (7 строк)
random=фев   sequential=янв
random=фев   sequential=фев
random=мар   sequential=мар
random=янв   sequential=янв
random=фев   sequential=фев
random=мар   sequential=мар
random=янв   sequential=янв

То же касается файлов: <gen type="file" src="cities.txt" order="sequential"/> проходит файл строка за строкой. Подробности — Маски и регистр.

Несколько значений на строку — repeat="N"

Добавьте фиксированный repeat — и строка возьмёт с хода N значений вместо одного. Ход продолжается через строки, так что элемент k строки r — это значение источника номер r×N+k:

<sequence name="Step"><gen type="text" value="created,paid,shipped,delivered" repeat="4" order="sequential"/></sequence>
./run steps.tdc (3 строки)
created,paid,shipped,delivered
created,paid,shipped,delivered
created,paid,shipped,delivered

Четыре шага по списку из четырёх — это весь список на каждой строке, и именно так записывается весь жизненный цикл записи одной последовательностью. По более короткому списку строки различаются: value="a,b,c" с repeat="2" даёт a,b, затем c,a, затем b,c.

Продолжать, а не начинать заново, — это то, что делает repeat="1" в точности тем же самым, что и просто order="sequential": одна колонка, а не особый случай.

Две формы отклоняются, а не угадываются. Диапазонный repeat="2..5" (TDC254): ход идёт с постоянным шагом, а у строки, длину которой решает квота длин, такого числа нет. Идущая по порядку date (TDC254): она несёт рядом с текстом момент времени, и у строки с несколькими датами нет одного момента для of= и plus=. distinct="true" тоже отклоняется (TDC307) — идущая по порядку строка ничего не тянет, значит и тянуть без возврата нечего.

missing= / anomaly= — пропуски и выбросы

Когда брать. Нужны данные, которые выглядят как в реальном мире — где часть полей пуста, а несколько значений сидят далеко за пределами нормального диапазона. missing= вставляет пустые ячейки (пропуски полностью случайного характера); anomaly= вставляет выбросы, чтобы у последующего конвейера или модели было что-то ненормальное, с чем нужно справиться. Оба прикрепляются к генератору как case= и применяются после того, как значение произведено.

Эти ручки формируют реалистичность, а не базовое значение, и цепляются к любому генератору. Различаются они тем, с чем могут работать: missing= очищает ячейку, что бы в ней ни лежало, а anomaly= умножает — и потому срабатывает только на значениях, которые читаются как числа. Числовая строка из списка text будет умножена; имя рядом с ними пройдёт без изменений, а список, в котором чисел нет вовсе, отвергается. Полные правила — в Аномалиях и пропусках.