| | | 1 | | @{ |
| | 0 | 2 | | ViewData["Title"] = "Статистика фидов"; |
| | 0 | 3 | | var fidNames = ViewBag.FidNames as List<string> ?? new List<string>(); |
| | 0 | 4 | | var pushUrl = $"{Context.Request.Scheme}://{Context.Request.Host}/Studio2/FidStat/Push"; |
| | 0 | 5 | | var pushUrlShort = $"{Context.Request.Scheme}://{Context.Request.Host}/FidStat/Push"; |
| | | 6 | | } |
| | | 7 | | |
| | | 8 | | <link rel="stylesheet" href="~/css/filters.css" asp-append-version="true" /> |
| | | 9 | | <link rel="stylesheet" href="~/css/Studio2/FidStat/fid-stat-index.css" asp-append-version="true" /> |
| | | 10 | | |
| | | 11 | | <div class="container-fluid py-3 fs-page"> |
| | | 12 | | <div class="fs-hero app-section-hero app-section-hero--import"> |
| | | 13 | | <div> |
| | | 14 | | <h1> |
| | | 15 | | <i class="fas fa-broadcast-tower text-primary"></i> |
| | | 16 | | Статистика фидов |
| | | 17 | | </h1> |
| | | 18 | | <p class="subtitle mb-0">Мониторинг распределения товаров по ценовым диапазонам в XML/YML-фидах</p> |
| | | 19 | | <div class="subtitle-help"> |
| | | 20 | | Внешний скрипт или сервис анализа периодически отправляет снимок (<strong>snapshot</strong>) через |
| | | 21 | | <code>POST /Studio2/FidStat/Push</code>: имя фида, время и таблица диапазонов (от–до, количество, %). |
| | | 22 | | Здесь можно просмотреть историю, сравнить два снимка и найти <strong>аномалии</strong> — диапазоны, |
| | | 23 | | где распределение сильно отклоняется от среднего (кроме нормальной зоны 0.9–1.1). |
| | | 24 | | </div> |
| | | 25 | | <div class="fs-quick-links app-section-hero__quick-links"> |
| | | 26 | | <a asp-area="Studio2" asp-controller="ProductAnalysis" asp-action="Index"> |
| | | 27 | | <i class="fas fa-chart-pie"></i>Анализ товаров |
| | | 28 | | </a> |
| | | 29 | | <a href="?tab=api" id="fsOpenApiTab"> |
| | | 30 | | <i class="fas fa-paper-plane"></i>Инструкция API |
| | | 31 | | </a> |
| | | 32 | | </div> |
| | | 33 | | </div> |
| | | 34 | | <div class="fs-hero-actions"> |
| | | 35 | | <button type="button" class="btn btn-sm btn-outline-secondary" id="btnRefresh" data-app-tooltip="Перезагрузи |
| | | 36 | | <i class="fas fa-sync-alt me-1"></i> Обновить |
| | | 37 | | </button> |
| | | 38 | | </div> |
| | | 39 | | </div> |
| | | 40 | | |
| | | 41 | | <ul class="nav nav-tabs fs-page-tabs" id="fidStatTabs" role="tablist"> |
| | | 42 | | <li class="nav-item" role="presentation"> |
| | | 43 | | <button class="nav-link active" id="tab-snapshots-btn" data-bs-toggle="tab" data-bs-target="#tab-snapshots" |
| | | 44 | | type="button" role="tab" aria-controls="tab-snapshots" aria-selected="true"> |
| | | 45 | | <i class="fas fa-list me-1"></i>Снимки |
| | | 46 | | </button> |
| | | 47 | | </li> |
| | | 48 | | <li class="nav-item" role="presentation"> |
| | | 49 | | <button class="nav-link" id="tab-api-btn" data-bs-toggle="tab" data-bs-target="#tab-api" |
| | | 50 | | type="button" role="tab" aria-controls="tab-api" aria-selected="false"> |
| | | 51 | | <i class="fas fa-paper-plane me-1"></i>Отправка данных (API) |
| | | 52 | | </button> |
| | | 53 | | </li> |
| | | 54 | | </ul> |
| | | 55 | | |
| | | 56 | | <div class="fs-tab-hint" id="fidStatTabHint"> |
| | | 57 | | Просмотр истории снимков, фильтрация по фиду и сравнение распределений по ценовым диапазонам. |
| | | 58 | | </div> |
| | | 59 | | |
| | | 60 | | <div class="tab-content fs-tab-content"> |
| | | 61 | | <div class="tab-pane fade show active" id="tab-snapshots" role="tabpanel" aria-labelledby="tab-snapshots-btn"> |
| | | 62 | | |
| | | 63 | | <div class="fs-section-card"> |
| | | 64 | | <div class="fs-section-body"> |
| | | 65 | | <div class="fs-intro"> |
| | | 66 | | <i class="fas fa-circle-info"></i> |
| | | 67 | | <div class="fs-intro-text"> |
| | | 68 | | <strong>Как пользоваться:</strong> отфильтруйте фид и период → кликните снимок слева → справа |
| | | 69 | | увидите график и таблицу диапазонов. Отметьте два чекбокса и нажмите «Сравнить» для diff. |
| | | 70 | | Красные полосы — статистические выбросы (Z-score > 2 или высокая вариация). |
| | | 71 | | </div> |
| | | 72 | | </div> |
| | | 73 | | |
| | | 74 | | <div class="filter-bar"> |
| | | 75 | | <div class="row g-2 align-items-end"> |
| | | 76 | | <div class="col-lg-3 col-md-6"> |
| | | 77 | | <label class="form-label" for="filterFidName">Фид</label> |
| | | 78 | | <select id="filterFidName" class="form-select form-select-sm custom-select-init" onchange="loadS |
| | | 79 | | <option value="">— Все фиды —</option> |
| | 0 | 80 | | @foreach (var name in fidNames) |
| | | 81 | | { |
| | 0 | 82 | | <option value="@name">@name</option> |
| | | 83 | | } |
| | | 84 | | </select> |
| | | 85 | | <div class="fs-field-hint">Имя источника из поля <code>fidName</code> при Push</div> |
| | | 86 | | </div> |
| | | 87 | | <div class="col-lg-2 col-md-6"> |
| | | 88 | | <label class="form-label" for="filterDateFrom">Дата с</label> |
| | | 89 | | <input type="date" id="filterDateFrom" class="form-control form-control-sm" onchange="loadSnapsh |
| | | 90 | | <div class="fs-field-hint">По времени поступления снимка</div> |
| | | 91 | | </div> |
| | | 92 | | <div class="col-lg-2 col-md-6"> |
| | | 93 | | <label class="form-label" for="filterDateTo">Дата по</label> |
| | | 94 | | <input type="date" id="filterDateTo" class="form-control form-control-sm" onchange="loadSnapshot |
| | | 95 | | </div> |
| | | 96 | | <div class="col-lg-2 col-md-6"> |
| | | 97 | | <label class="form-label" for="filterPageSize">На странице</label> |
| | | 98 | | <select id="filterPageSize" class="form-select form-select-sm custom-select-init" onchange="load |
| | | 99 | | <option value="10">10</option> |
| | | 100 | | <option value="20" selected>20</option> |
| | | 101 | | <option value="50">50</option> |
| | | 102 | | <option value="100">100</option> |
| | | 103 | | </select> |
| | | 104 | | </div> |
| | | 105 | | <div class="col-lg-auto col-md-12"> |
| | | 106 | | <button type="button" class="btn btn-sm btn-outline-secondary w-100" onclick="clearFilters()" da |
| | | 107 | | <i class="fas fa-times me-1"></i> Сбросить |
| | | 108 | | </button> |
| | | 109 | | </div> |
| | | 110 | | </div> |
| | | 111 | | </div> |
| | | 112 | | |
| | | 113 | | <div class="fs-workflow"> |
| | | 114 | | <div class="fs-workflow-step"><span class="num">1</span>Выберите фид и период</div> |
| | | 115 | | <div class="fs-workflow-step"><span class="num">2</span>Клик по снимку → детали</div> |
| | | 116 | | <div class="fs-workflow-step"><span class="num">3</span>2+ чекбокса → Сравнить</div> |
| | | 117 | | <div class="fs-workflow-step"><span class="num">↑↓</span>Клавиши — навигация по списку</div> |
| | | 118 | | </div> |
| | | 119 | | </div> |
| | | 120 | | </div> |
| | | 121 | | |
| | | 122 | | <div class="fs-stats-row"> |
| | | 123 | | <div class="fs-stat"> |
| | | 124 | | <div class="fs-stat-icon text-primary"><i class="fas fa-camera"></i></div> |
| | | 125 | | <div class="fs-stat-value" id="statTotal">—</div> |
| | | 126 | | <div class="fs-stat-label">Снимков в выборке</div> |
| | | 127 | | <div class="fs-stat-subtext" id="statTotalSub">По текущему фильтру</div> |
| | | 128 | | </div> |
| | | 129 | | <div class="fs-stat"> |
| | | 130 | | <div class="fs-stat-icon text-success"><i class="fas fa-layer-group"></i></div> |
| | | 131 | | <div class="fs-stat-value" id="statFidCount">—</div> |
| | | 132 | | <div class="fs-stat-label">Уникальных фидов</div> |
| | | 133 | | <div class="fs-stat-subtext">Всего в базе</div> |
| | | 134 | | </div> |
| | | 135 | | <div class="fs-stat"> |
| | | 136 | | <div class="fs-stat-icon text-info"><i class="fas fa-clock"></i></div> |
| | | 137 | | <div class="fs-stat-value" id="statLatestDate">—</div> |
| | | 138 | | <div class="fs-stat-label">Последний снимок</div> |
| | | 139 | | <div class="fs-stat-subtext" id="statLatestSub">В текущей странице</div> |
| | | 140 | | </div> |
| | | 141 | | <div class="fs-stat"> |
| | | 142 | | <div class="fs-stat-icon text-warning"><i class="fas fa-filter"></i></div> |
| | | 143 | | <div class="fs-stat-value" id="statFilteredCount">—</div> |
| | | 144 | | <div class="fs-stat-label">Всего по фильтру</div> |
| | | 145 | | <div class="fs-stat-subtext">Для пагинации</div> |
| | | 146 | | </div> |
| | | 147 | | </div> |
| | | 148 | | |
| | | 149 | | <div class="row g-3"> |
| | | 150 | | <div class="col-lg-5"> |
| | | 151 | | <div class="fs-panel"> |
| | | 152 | | <div class="fs-panel-header"> |
| | | 153 | | <span> |
| | | 154 | | <i class="fas fa-list me-2 text-primary"></i>Снимки фидов |
| | | 155 | | <span class="fs-panel-hint">— клик открывает анализ</span> |
| | | 156 | | </span> |
| | | 157 | | <span id="snapshotCountBadge" class="badge bg-primary">0</span> |
| | | 158 | | </div> |
| | | 159 | | <div id="selectionInfo" class="selection-info d-none"> |
| | | 160 | | <i class="fas fa-check-circle text-primary"></i> |
| | | 161 | | <span>Выбрано: <span class="selection-count" id="selectionCount">0</span></span> |
| | | 162 | | <button type="button" class="btn btn-sm btn-primary ms-auto" onclick="showComparison()" id="compareB |
| | | 163 | | <i class="fas fa-code-branch me-1"></i> Сравнить |
| | | 164 | | </button> |
| | | 165 | | </div> |
| | | 166 | | <div class="fs-panel-body p-0"> |
| | | 167 | | <table class="table table-hover snapshot-table snapshot-table-compact mb-0"> |
| | | 168 | | <thead class="sticky-top"> |
| | | 169 | | <tr> |
| | | 170 | | <th class="text-center" style="width: 30px;"> |
| | | 171 | | <input type="checkbox" id="selectAllCheck" class="snapshot-check" onchange="toggleSe |
| | | 172 | | </th> |
| | | 173 | | <th onclick="sortBy('fidName')" class="sort-col" data-col="fidName"> |
| | | 174 | | Фид <span class="sort-icon"></span> |
| | | 175 | | <span class="th-hint">Источник</span> |
| | | 176 | | </th> |
| | | 177 | | <th onclick="sortBy('postTime')" class="sort-col" data-col="postTime"> |
| | | 178 | | Время <span class="sort-icon"></span> |
| | | 179 | | <span class="th-hint">postTime</span> |
| | | 180 | | </th> |
| | | 181 | | <th style="width: 70px;"> |
| | | 182 | | Мини-граф |
| | | 183 | | <span class="th-hint">Распред.</span> |
| | | 184 | | </th> |
| | | 185 | | </tr> |
| | | 186 | | </thead> |
| | | 187 | | <tbody id="snapshotTableBody"> |
| | | 188 | | <tr> |
| | | 189 | | <td colspan="4" class="text-center text-muted py-5"> |
| | | 190 | | <span class="spinner-border spinner-border-sm me-2"></span>Загрузка… |
| | | 191 | | </td> |
| | | 192 | | </tr> |
| | | 193 | | </tbody> |
| | | 194 | | </table> |
| | | 195 | | </div> |
| | | 196 | | <div class="fs-panel-footer"> |
| | | 197 | | <nav> |
| | | 198 | | <ul class="pagination pagination-sm mb-0 justify-content-center fs-pagination" id="paginationUl" |
| | | 199 | | </nav> |
| | | 200 | | </div> |
| | | 201 | | </div> |
| | | 202 | | </div> |
| | | 203 | | |
| | | 204 | | <div class="col-lg-7"> |
| | | 205 | | <div class="fs-panel"> |
| | | 206 | | <div class="fs-panel-header"> |
| | | 207 | | <span> |
| | | 208 | | <i class="fas fa-chart-bar me-2 text-primary"></i> |
| | | 209 | | Анализ диапазонов |
| | | 210 | | <span id="detailFidBadge" class="badge badge-fid ms-2 d-none"></span> |
| | | 211 | | </span> |
| | | 212 | | <small id="detailMeta" class="text-muted"></small> |
| | | 213 | | </div> |
| | | 214 | | <div class="fs-panel-body"> |
| | | 215 | | <div id="detailPlaceholder" class="text-center text-muted"> |
| | | 216 | | <i class="fas fa-hand-pointer fa-2x mb-3 text-secondary opacity-50"></i> |
| | | 217 | | <p class="mb-1"><strong>Выберите снимок в таблице слева</strong></p> |
| | | 218 | | <p class="small mb-0">Откроется гистограмма и таблица ценовых диапазонов с долями товаров</p> |
| | | 219 | | <ol class="fs-placeholder-steps list-unstyled"> |
| | | 220 | | <li><i class="fas fa-mouse-pointer me-1 text-primary"></i> Один клик — детальный просмотр</l |
| | | 221 | | <li><i class="fas fa-check-square me-1 text-primary"></i> Два чекбокса — сравнение снимков</ |
| | | 222 | | <li><i class="fas fa-file-csv me-1 text-primary"></i> Экспорт CSV и печать — внизу панели</l |
| | | 223 | | </ol> |
| | | 224 | | </div> |
| | | 225 | | |
| | | 226 | | <div id="detailLegend" class="fs-legend d-none"> |
| | | 227 | | <span class="fs-legend-item"> |
| | | 228 | | <span class="fs-legend-swatch fs-legend-swatch--normal"></span> Обычный диапазон |
| | | 229 | | </span> |
| | | 230 | | <span class="fs-legend-item"> |
| | | 231 | | <span class="fs-legend-swatch fs-legend-swatch--anomaly"></span> Аномалия (выброс) |
| | | 232 | | </span> |
| | | 233 | | <span class="fs-legend-item text-muted"> |
| | | 234 | | <i class="fas fa-info-circle"></i> 0.9–1.1 не помечается как аномалия |
| | | 235 | | </span> |
| | | 236 | | </div> |
| | | 237 | | |
| | | 238 | | <div id="detailStatsWrap" class="detail-stats-grid d-none"> |
| | | 239 | | <div class="detail-stat-item"> |
| | | 240 | | <div class="detail-stat-value" id="detailItemCount">0</div> |
| | | 241 | | <div class="detail-stat-label">Диапазонов</div> |
| | | 242 | | </div> |
| | | 243 | | <div class="detail-stat-item"> |
| | | 244 | | <div class="detail-stat-value" id="detailTotal">0</div> |
| | | 245 | | <div class="detail-stat-label">Всего позиций</div> |
| | | 246 | | </div> |
| | | 247 | | <div class="detail-stat-item"> |
| | | 248 | | <div class="detail-stat-value" id="detailMaxValue">0</div> |
| | | 249 | | <div class="detail-stat-label">Макс. в диапазоне</div> |
| | | 250 | | </div> |
| | | 251 | | <div class="detail-stat-item"> |
| | | 252 | | <div class="detail-stat-value" id="detailAvgValue">0</div> |
| | | 253 | | <div class="detail-stat-label">Среднее значение</div> |
| | | 254 | | </div> |
| | | 255 | | </div> |
| | | 256 | | |
| | | 257 | | <div id="detailChartWrap" class="mb-3 d-none"> |
| | | 258 | | <canvas id="chartCanvas"></canvas> |
| | | 259 | | </div> |
| | | 260 | | |
| | | 261 | | <div id="detailTableWrap" class="d-none"> |
| | | 262 | | <div class="table-responsive"> |
| | | 263 | | <table class="table table-sm table-bordered snapshot-table mb-0"> |
| | | 264 | | <thead class="sticky-top bg-light"> |
| | | 265 | | <tr> |
| | | 266 | | <th class="text-center" style="width:40px;">#</th> |
| | | 267 | | <th>Диапазон (от – до)</th> |
| | | 268 | | <th class="text-end" style="width: 100px;">Кол-во</th> |
| | | 269 | | <th class="text-end" style="width: 80px;">%</th> |
| | | 270 | | <th style="width: 120px;">Доля</th> |
| | | 271 | | </tr> |
| | | 272 | | </thead> |
| | | 273 | | <tbody id="detailTableBody"></tbody> |
| | | 274 | | </table> |
| | | 275 | | </div> |
| | | 276 | | </div> |
| | | 277 | | </div> |
| | | 278 | | <div id="detailFooter" class="fs-panel-footer d-none d-flex justify-content-between gap-2"> |
| | | 279 | | <button type="button" class="btn btn-sm btn-outline-secondary" onclick="printDetail()" data-app-tool |
| | | 280 | | <i class="fas fa-print me-1"></i>Печать |
| | | 281 | | </button> |
| | | 282 | | <button type="button" class="btn btn-sm btn-outline-primary" onclick="exportDetailCsv()" data-app-to |
| | | 283 | | <i class="fas fa-file-csv me-1"></i>Экспорт CSV |
| | | 284 | | </button> |
| | | 285 | | </div> |
| | | 286 | | </div> |
| | | 287 | | </div> |
| | | 288 | | </div> |
| | | 289 | | |
| | | 290 | | </div><!-- /tab-snapshots --> |
| | | 291 | | |
| | | 292 | | <div class="tab-pane fade" id="tab-api" role="tabpanel" aria-labelledby="tab-api-btn"> |
| | | 293 | | <div class="fs-section-card"> |
| | | 294 | | <div class="fs-section-body"> |
| | | 295 | | <div class="fs-intro"> |
| | | 296 | | <i class="fas fa-circle-info"></i> |
| | | 297 | | <div class="fs-intro-text"> |
| | | 298 | | <strong>Публичный endpoint</strong> принимает снимок распределения товаров по ценовым диапаз |
| | | 299 | | Авторизация не требуется. После успешной отправки снимок появится на вкладке «Снимки». |
| | | 300 | | Имена полей в JSON можно указывать в <code>camelCase</code> или <code>PascalCase</code>. |
| | | 301 | | </div> |
| | | 302 | | </div> |
| | | 303 | | |
| | | 304 | | <div class="fs-api-grid"> |
| | | 305 | | <div class="fs-api-card"> |
| | | 306 | | <div class="fs-api-card-title"><i class="fas fa-link me-1"></i>Адреса</div> |
| | | 307 | | <table class="table table-sm fs-api-meta-table mb-0"> |
| | | 308 | | <tbody> |
| | | 309 | | <tr> |
| | | 310 | | <th>Метод</th> |
| | | 311 | | <td><span class="badge bg-primary">POST</span></td> |
| | | 312 | | </tr> |
| | | 313 | | <tr> |
| | | 314 | | <th>Основной URL</th> |
| | 0 | 315 | | <td><code class="fs-api-url">@pushUrl</code></td> |
| | | 316 | | </tr> |
| | | 317 | | <tr> |
| | | 318 | | <th>Короткий alias</th> |
| | 0 | 319 | | <td><code class="fs-api-url">@pushUrlShort</code></td> |
| | | 320 | | </tr> |
| | | 321 | | <tr> |
| | | 322 | | <th>Авторизация</th> |
| | | 323 | | <td>Не требуется (<code>AllowAnonymous</code>)</td> |
| | | 324 | | </tr> |
| | | 325 | | <tr> |
| | | 326 | | <th>Content-Type</th> |
| | | 327 | | <td><code>application/json</code> — рекомендуется; без заголовка тоже принимаетс |
| | | 328 | | </tr> |
| | | 329 | | <tr> |
| | | 330 | | <th>Кодировка</th> |
| | | 331 | | <td>UTF-8</td> |
| | | 332 | | </tr> |
| | | 333 | | </tbody> |
| | | 334 | | </table> |
| | | 335 | | </div> |
| | | 336 | | |
| | | 337 | | <div class="fs-api-card"> |
| | | 338 | | <div class="fs-api-card-title"><i class="fas fa-reply me-1"></i>Ответы сервера</div> |
| | | 339 | | <div class="fs-api-response"> |
| | | 340 | | <div class="fs-api-response-item"> |
| | | 341 | | <span class="badge bg-success">200</span> |
| | | 342 | | <code>{"status":"ok","snapshotId":123,"itemsCount":15,"fidName":"FID_BY"}</code> |
| | | 343 | | </div> |
| | | 344 | | <div class="fs-api-response-item"> |
| | | 345 | | <span class="badge bg-warning text-dark">400</span> |
| | | 346 | | <code>{"status":"error","errors":["The FidName field is required."]}</code> |
| | | 347 | | </div> |
| | | 348 | | <div class="fs-api-response-item"> |
| | | 349 | | <span class="badge bg-danger">404</span> |
| | | 350 | | <span class="text-muted small">Неверный HTTP-метод (GET/PUT) или несуществующий путь |
| | | 351 | | </div> |
| | | 352 | | </div> |
| | | 353 | | </div> |
| | | 354 | | </div> |
| | | 355 | | |
| | | 356 | | <h2 class="fs-api-section-title">Поля корневого объекта</h2> |
| | | 357 | | <div class="table-responsive"> |
| | | 358 | | <table class="table table-sm snapshot-table fs-api-fields-table"> |
| | | 359 | | <thead> |
| | | 360 | | <tr> |
| | | 361 | | <th>Поле JSON</th> |
| | | 362 | | <th>Тип</th> |
| | | 363 | | <th class="text-center">Обяз.</th> |
| | | 364 | | <th>Описание</th> |
| | | 365 | | </tr> |
| | | 366 | | </thead> |
| | | 367 | | <tbody> |
| | | 368 | | <tr> |
| | | 369 | | <td><code>fidName</code></td> |
| | | 370 | | <td>string</td> |
| | | 371 | | <td class="text-center"><span class="badge bg-danger">да</span></td> |
| | | 372 | | <td> |
| | | 373 | | Уникальное имя фида-источника (до 100 символов). Примеры: <code>FID_BY</code>, < |
| | | 374 | | Используется в фильтре на вкладке «Снимки». |
| | | 375 | | </td> |
| | | 376 | | </tr> |
| | | 377 | | <tr> |
| | | 378 | | <td><code>postTime</code></td> |
| | | 379 | | <td>string (ISO 8601)</td> |
| | | 380 | | <td class="text-center"><span class="badge bg-secondary">нет</span></td> |
| | | 381 | | <td> |
| | | 382 | | Время формирования снимка на стороне отправителя, например <code>2026-07-09T09:3 |
| | | 383 | | или <code>2026-07-09T06:30:00Z</code>. Если не передано — будет <code>0001-01-01 |
| | | 384 | | </td> |
| | | 385 | | </tr> |
| | | 386 | | <tr> |
| | | 387 | | <td><code>items</code></td> |
| | | 388 | | <td>array</td> |
| | | 389 | | <td class="text-center"><span class="badge bg-danger">да</span></td> |
| | | 390 | | <td>Массив строк диапазонов. Минимум <strong>1</strong> элемент. Каждый элемент — од |
| | | 391 | | </tr> |
| | | 392 | | </tbody> |
| | | 393 | | </table> |
| | | 394 | | </div> |
| | | 395 | | |
| | | 396 | | <h2 class="fs-api-section-title">Поля элемента <code>items[]</code></h2> |
| | | 397 | | <div class="table-responsive"> |
| | | 398 | | <table class="table table-sm snapshot-table fs-api-fields-table"> |
| | | 399 | | <thead> |
| | | 400 | | <tr> |
| | | 401 | | <th>Поле JSON</th> |
| | | 402 | | <th>Тип</th> |
| | | 403 | | <th class="text-center">Обяз.</th> |
| | | 404 | | <th>Описание</th> |
| | | 405 | | </tr> |
| | | 406 | | </thead> |
| | | 407 | | <tbody> |
| | | 408 | | <tr> |
| | | 409 | | <td><code>id</code></td> |
| | | 410 | | <td>int</td> |
| | | 411 | | <td class="text-center"><span class="badge bg-secondary">нет</span></td> |
| | | 412 | | <td>Порядковый номер строки в исходной таблице. Сохраняется как <code>RowNumber</cod |
| | | 413 | | </tr> |
| | | 414 | | <tr> |
| | | 415 | | <td><code>rangeFrom</code></td> |
| | | 416 | | <td>decimal</td> |
| | | 417 | | <td class="text-center"><span class="badge bg-danger">да</span></td> |
| | | 418 | | <td>Начало диапазона (например коэффициент 0.000 или доля цены). До 4 знаков после з |
| | | 419 | | </tr> |
| | | 420 | | <tr> |
| | | 421 | | <td><code>rangeTo</code></td> |
| | | 422 | | <td>decimal</td> |
| | | 423 | | <td class="text-center"><span class="badge bg-danger">да</span></td> |
| | | 424 | | <td>Конец диапазона. Отображается в UI как «0.900 – 1.100».</td> |
| | | 425 | | </tr> |
| | | 426 | | <tr> |
| | | 427 | | <td><code>count</code></td> |
| | | 428 | | <td>int</td> |
| | | 429 | | <td class="text-center"><span class="badge bg-danger">да</span></td> |
| | | 430 | | <td>Количество товарных позиций, попавших в этот диапазон. Суммируется в карточке «В |
| | | 431 | | </tr> |
| | | 432 | | <tr> |
| | | 433 | | <td><code>percentage</code></td> |
| | | 434 | | <td>decimal</td> |
| | | 435 | | <td class="text-center"><span class="badge bg-danger">да</span></td> |
| | | 436 | | <td>Доля диапазона от общего числа позиций, в процентах (0–100). Используется для ги |
| | | 437 | | </tr> |
| | | 438 | | <tr> |
| | | 439 | | <td><code>indices</code></td> |
| | | 440 | | <td>string</td> |
| | | 441 | | <td class="text-center"><span class="badge bg-secondary">нет</span></td> |
| | | 442 | | <td>Дополнительная метка диапазона (до 200 символов), например <code>static_value</c |
| | | 443 | | </tr> |
| | | 444 | | </tbody> |
| | | 445 | | </table> |
| | | 446 | | </div> |
| | | 447 | | |
| | | 448 | | <h2 class="fs-api-section-title">Пример тела запроса (JSON)</h2> |
| | | 449 | | <div class="fs-code-block"> |
| | | 450 | | <div class="fs-code-block-header"> |
| | | 451 | | <span>application/json</span> |
| | | 452 | | <button type="button" class="btn btn-sm btn-outline-secondary fs-copy-btn" data-copy-target= |
| | | 453 | | <i class="fas fa-copy me-1"></i>Копировать |
| | | 454 | | </button> |
| | | 455 | | </div> |
| | | 456 | | <pre id="fs-example-json" class="fs-code-pre">{ |
| | | 457 | | "fidName": "FID_BY", |
| | | 458 | | "postTime": "2026-07-09T09:30:00+03:00", |
| | | 459 | | "items": [ |
| | | 460 | | { |
| | | 461 | | "id": 1, |
| | | 462 | | "rangeFrom": 0.000, |
| | | 463 | | "rangeTo": 0.500, |
| | | 464 | | "count": 120, |
| | | 465 | | "percentage": 12.5, |
| | | 466 | | "indices": "low" |
| | | 467 | | }, |
| | | 468 | | { |
| | | 469 | | "id": 2, |
| | | 470 | | "rangeFrom": 0.900, |
| | | 471 | | "rangeTo": 1.100, |
| | | 472 | | "count": 640, |
| | | 473 | | "percentage": 66.7, |
| | | 474 | | "indices": "normal" |
| | | 475 | | }, |
| | | 476 | | { |
| | | 477 | | "id": 3, |
| | | 478 | | "rangeFrom": 1.100, |
| | | 479 | | "rangeTo": 2.000, |
| | | 480 | | "count": 200, |
| | | 481 | | "percentage": 20.8 |
| | | 482 | | } |
| | | 483 | | ] |
| | | 484 | | }</pre> |
| | | 485 | | </div> |
| | | 486 | | |
| | | 487 | | <h2 class="fs-api-section-title">Примеры отправки</h2> |
| | | 488 | | |
| | | 489 | | <div class="fs-code-block"> |
| | | 490 | | <div class="fs-code-block-header"> |
| | | 491 | | <span><i class="fab fa-linux me-1"></i>curl</span> |
| | | 492 | | <button type="button" class="btn btn-sm btn-outline-secondary fs-copy-btn" data-copy-target= |
| | | 493 | | <i class="fas fa-copy me-1"></i>Копировать |
| | | 494 | | </button> |
| | | 495 | | </div> |
| | 0 | 496 | | <pre id="fs-example-curl" class="fs-code-pre">curl -sS -X POST '@pushUrl' \ |
| | | 497 | | -H 'Content-Type: application/json' \ |
| | | 498 | | -d '{ |
| | | 499 | | "fidName": "FID_BY", |
| | | 500 | | "postTime": "2026-07-09T09:30:00+03:00", |
| | | 501 | | "items": [ |
| | | 502 | | {"id": 1, "rangeFrom": 0.9, "rangeTo": 1.1, "count": 100, "percentage": 100} |
| | | 503 | | ] |
| | | 504 | | }'</pre> |
| | | 505 | | </div> |
| | | 506 | | |
| | | 507 | | <div class="fs-code-block"> |
| | | 508 | | <div class="fs-code-block-header"> |
| | | 509 | | <span><i class="fab fa-python me-1"></i>Python (requests)</span> |
| | | 510 | | <button type="button" class="btn btn-sm btn-outline-secondary fs-copy-btn" data-copy-target= |
| | | 511 | | <i class="fas fa-copy me-1"></i>Копировать |
| | | 512 | | </button> |
| | | 513 | | </div> |
| | | 514 | | <pre id="fs-example-python" class="fs-code-pre">import requests |
| | | 515 | | |
| | 0 | 516 | | url = "@pushUrl" |
| | | 517 | | payload = { |
| | | 518 | | "fidName": "FID_BY", |
| | | 519 | | "postTime": "2026-07-09T09:30:00+03:00", |
| | | 520 | | "items": [ |
| | | 521 | | { |
| | | 522 | | "id": 1, |
| | | 523 | | "rangeFrom": 0.9, |
| | | 524 | | "rangeTo": 1.1, |
| | | 525 | | "count": 100, |
| | | 526 | | "percentage": 100.0, |
| | | 527 | | } |
| | | 528 | | ], |
| | | 529 | | } |
| | | 530 | | |
| | | 531 | | # Важно: параметр json= автоматически ставит Content-Type: application/json |
| | | 532 | | response = requests.post(url, json=payload, timeout=30) |
| | | 533 | | response.raise_for_status() |
| | | 534 | | print(response.json())</pre> |
| | | 535 | | </div> |
| | | 536 | | |
| | | 537 | | <div class="fs-api-warnings"> |
| | | 538 | | <div class="fs-api-warning"> |
| | | 539 | | <i class="fas fa-triangle-exclamation text-warning"></i> |
| | | 540 | | <div> |
| | | 541 | | <strong>404 Not Found</strong> — чаще всего неверный HTTP-метод (нужен только <code>POST |
| | | 542 | | или опечатка в URL. Оба адреса выше эквивалентны. |
| | | 543 | | </div> |
| | | 544 | | </div> |
| | | 545 | | <div class="fs-api-warning"> |
| | | 546 | | <i class="fas fa-triangle-exclamation text-warning"></i> |
| | | 547 | | <div> |
| | | 548 | | <strong>400 Bad Request</strong> — пустой <code>fidName</code>, отсутствует массив <code |
| | | 549 | | или некорректный JSON. Текст ошибок — в поле <code>errors[]</code>. |
| | | 550 | | </div> |
| | | 551 | | </div> |
| | | 552 | | <div class="fs-api-warning"> |
| | | 553 | | <i class="fas fa-lightbulb text-primary"></i> |
| | | 554 | | <div> |
| | | 555 | | В Python используйте <code>requests.post(url, json=payload)</code>, а не <code>data=json |
| | | 556 | | без заголовка — иначе на старых версиях API мог вернуться 404. |
| | | 557 | | </div> |
| | | 558 | | </div> |
| | | 559 | | </div> |
| | | 560 | | </div> |
| | | 561 | | </div> |
| | | 562 | | </div><!-- /tab-api --> |
| | | 563 | | |
| | | 564 | | </div><!-- /tab-content --> |
| | | 565 | | </div> |
| | | 566 | | |
| | | 567 | | <div id="comparisonModal" class="comparison-modal" onclick="if(event.target === this) closeComparisonModal()"> |
| | | 568 | | <div id="comparisonContent" class="comparison-content"></div> |
| | | 569 | | </div> |
| | | 570 | | |
| | | 571 | | @section Scripts { |
| | | 572 | | <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script> |
| | | 573 | | <script src="~/js/Studio2/FidStat/fid-stat-index.js" asp-append-version="true"></script> |
| | | 574 | | } |