Github user vanzin commented on a diff in the pull request: https://github.com/apache/spark/pull/20663#discussion_r170685539 --- Diff: core/src/main/scala/org/apache/spark/ui/jobs/AllStagesPage.scala --- @@ -67,152 +41,120 @@ private[ui] class AllStagesPage(parent: StagesTab) extends WebUIPage("") { }.toMap val poolTable = new PoolTable(pools, parent) - val shouldShowActiveStages = activeStages.nonEmpty - val shouldShowPendingStages = pendingStages.nonEmpty - val shouldShowCompletedStages = completedStages.nonEmpty - val shouldShowSkippedStages = skippedStages.nonEmpty - val shouldShowFailedStages = failedStages.nonEmpty + val allStatuses = Seq(StageStatus.ACTIVE, StageStatus.PENDING, StageStatus.COMPLETE, + StageStatus.SKIPPED, StageStatus.FAILED) + val allStages = parent.store.stageList(null) val appSummary = parent.store.appSummary() - val completedStageNumStr = if (appSummary.numCompletedStages == completedStages.size) { - s"${appSummary.numCompletedStages}" - } else { - s"${appSummary.numCompletedStages}, only showing ${completedStages.size}" - } + + val (summaries, tables) = allStatuses.map( + summaryAndTableForStatus(allStages, appSummary, _, request)).unzip val summary: NodeSeq = <div> <ul class="unstyled"> - { - if (shouldShowActiveStages) { - <li> - <a href="#active"><strong>Active Stages:</strong></a> - {activeStages.size} - </li> - } - } - { - if (shouldShowPendingStages) { - <li> - <a href="#pending"><strong>Pending Stages:</strong></a> - {pendingStages.size} - </li> - } - } - { - if (shouldShowCompletedStages) { - <li id="completed-summary"> - <a href="#completed"><strong>Completed Stages:</strong></a> - {completedStageNumStr} - </li> - } - } - { - if (shouldShowSkippedStages) { - <li id="completed-summary"> - <a href="#skipped"><strong>Skipped Stages:</strong></a> - {skippedStages.size} - </li> - } - } - { - if (shouldShowFailedStages) { - <li> - <a href="#failed"><strong>Failed Stages:</strong></a> - {numFailedStages} - </li> - } - } + {summaries.flatten} </ul> </div> - var content = summary ++ - { - if (sc.isDefined && isFairScheduler) { - <span class="collapse-aggregated-poolTable collapse-table" - onClick="collapseTable('collapse-aggregated-poolTable','aggregated-poolTable')"> - <h4> - <span class="collapse-table-arrow arrow-open"></span> - <a>Fair Scheduler Pools ({pools.size})</a> - </h4> - </span> ++ - <div class="aggregated-poolTable collapsible-table"> - {poolTable.toNodeSeq} - </div> - } else { - Seq.empty[Node] - } - } - if (shouldShowActiveStages) { - content ++= - <span id="active" class="collapse-aggregated-allActiveStages collapse-table" - onClick="collapseTable('collapse-aggregated-allActiveStages', - 'aggregated-allActiveStages')"> - <h4> - <span class="collapse-table-arrow arrow-open"></span> - <a>Active Stages ({activeStages.size})</a> - </h4> - </span> ++ - <div class="aggregated-allActiveStages collapsible-table"> - {activeStagesTable.toNodeSeq} - </div> - } - if (shouldShowPendingStages) { - content ++= - <span id="pending" class="collapse-aggregated-allPendingStages collapse-table" - onClick="collapseTable('collapse-aggregated-allPendingStages', - 'aggregated-allPendingStages')"> + val poolsDescription = if (sc.isDefined && isFairScheduler) { + <span class="collapse-aggregated-poolTable collapse-table" + onClick="collapseTable('collapse-aggregated-poolTable','aggregated-poolTable')"> <h4> <span class="collapse-table-arrow arrow-open"></span> - <a>Pending Stages ({pendingStages.size})</a> + <a>Fair Scheduler Pools ({pools.size})</a> </h4> </span> ++ - <div class="aggregated-allPendingStages collapsible-table"> - {pendingStagesTable.toNodeSeq} + <div class="aggregated-poolTable collapsible-table"> + {poolTable.toNodeSeq} </div> + } else { + Seq.empty[Node] + } + + val content = summary ++ poolsDescription ++ tables.flatten.flatten + + UIUtils.headerSparkPage("Stages for All Jobs", content, parent) + } + + def summaryAndTableForStatus( + allStages: Seq[StageData], + appSummary: AppSummary, + status: StageStatus, + request: HttpServletRequest): (Option[Elem], Option[NodeSeq]) = { + val stages = if (status == StageStatus.FAILED) { + allStages.filter(_.status == status).reverse + } else { + allStages.filter(_.status == status) } - if (shouldShowCompletedStages) { - content ++= - <span id="completed" class="collapse-aggregated-allCompletedStages collapse-table" - onClick="collapseTable('collapse-aggregated-allCompletedStages', - 'aggregated-allCompletedStages')"> - <h4> - <span class="collapse-table-arrow arrow-open"></span> - <a>Completed Stages ({completedStageNumStr})</a> - </h4> - </span> ++ - <div class="aggregated-allCompletedStages collapsible-table"> - {completedStagesTable.toNodeSeq} - </div> + + if (stages.isEmpty) { + (None, None) + } else { + val killEnabled = status == StageStatus.ACTIVE && parent.killEnabled + val isFailedStage = status == StageStatus.FAILED + + val stagesTable = + new StageTableBase(parent.store, request, stages, statusName(status), stageTag(status), + parent.basePath, subPath, parent.isFairScheduler, killEnabled, isFailedStage) + val stagesSize = stages.size + (Some(summary(appSummary, status, stagesSize)), + Some(table(appSummary, status, stagesTable, stagesSize))) } - if (shouldShowSkippedStages) { - content ++= - <span id="skipped" class="collapse-aggregated-allSkippedStages collapse-table" - onClick="collapseTable('collapse-aggregated-allSkippedStages', - 'aggregated-allSkippedStages')"> - <h4> - <span class="collapse-table-arrow arrow-open"></span> - <a>Skipped Stages ({skippedStages.size})</a> - </h4> - </span> ++ - <div class="aggregated-allSkippedStages collapsible-table"> - {skippedStagesTable.toNodeSeq} - </div> + } + + private def statusName(status: StageStatus): String = status match { + case StageStatus.ACTIVE => "active" + case StageStatus.COMPLETE => "completed" + case StageStatus.FAILED => "failed" + case StageStatus.PENDING => "pending" + case StageStatus.SKIPPED => "skipped" + } + + private def stageTag(status: StageStatus): String = s"${statusName(status)}Stage" + + private def headerDescription(status: StageStatus): String = statusName(status).capitalize + + private def summaryContent(appSummary: AppSummary, status: StageStatus, size: Int): String = { + if (status == StageStatus.COMPLETE && appSummary.numCompletedStages != size) { + s"${appSummary.numCompletedStages}, only showing $size" + } else { + s"$size" } - if (shouldShowFailedStages) { - content ++= - <span id ="failed" class="collapse-aggregated-allFailedStages collapse-table" - onClick="collapseTable('collapse-aggregated-allFailedStages', - 'aggregated-allFailedStages')"> - <h4> - <span class="collapse-table-arrow arrow-open"></span> - <a>Failed Stages ({numFailedStages})</a> - </h4> - </span> ++ - <div class="aggregated-allFailedStages collapsible-table"> - {failedStagesTable.toNodeSeq} - </div> + } + + private def summary(appSummary: AppSummary, status: StageStatus, size: Int): Elem = { + val summary = + <li> + <a href={s"#${statusName(status)}"}> + <strong>{headerDescription(status)} Stages:</strong> + </a> + {summaryContent(appSummary, status, size)} + </li> + + if (status == StageStatus.COMPLETE) { + summary % Attribute(None, "id", Text("completed-summary"), Null) + } else { + summary } - UIUtils.headerSparkPage("Stages for All Jobs", content, parent) + } + + private def table( + appSummary: AppSummary, + status: StageStatus, + stagesTable: StageTableBase, size: Int): NodeSeq = { --- End diff -- nit: `size` goes in next line
--- --------------------------------------------------------------------- To unsubscribe, e-mail: reviews-unsubscr...@spark.apache.org For additional commands, e-mail: reviews-h...@spark.apache.org