shahidki31 commented on a change in pull request #26253: [SPARK-29599][WEBUI] Support pagination for session table in JDBC/ODBC Tab URL: https://github.com/apache/spark/pull/26253#discussion_r339060339
########## File path: sql/hive-thriftserver/src/main/scala/org/apache/spark/sql/hive/thriftserver/ui/ThriftServerPage.scala ########## @@ -377,6 +404,164 @@ private[ui] class SqlStatsPagedTable( "%s/jobs/job/?id=%s".format(UIUtils.prependBaseUri(request, parent.basePath), jobId) } +private[ui] class SessionStatsPagedTable( + request: HttpServletRequest, + parent: ThriftServerTab, + data: Seq[SessionInfo], + subPath: String, + basePath: String, + parameterOtherTable: Iterable[String], + sessionStatsTableTag: String, + pageSize: Int, + sortColumn: String, + desc: Boolean) extends PagedTable[SessionInfo] { + + override val dataSource = new SessionStatsTableDataSource(data, pageSize, sortColumn, desc) + + private val parameterPath = s"$basePath/$subPath/?${parameterOtherTable.mkString("&")}" + + override def tableId: String = sessionStatsTableTag + + override def tableCssClass: String = + "table table-bordered table-condensed table-striped " + + "table-head-clickable table-cell-width-limited" + + override def pageLink(page: Int): String = { + val encodedSortColumn = URLEncoder.encode(sortColumn, UTF_8.name()) + parameterPath + + s"&$pageNumberFormField=$page" + + s"&$sessionStatsTableTag.sort=$encodedSortColumn" + + s"&$sessionStatsTableTag.desc=$desc" + + s"&$pageSizeFormField=$pageSize" + } + + override def pageSizeFormField: String = s"$sessionStatsTableTag.pageSize" + + override def pageNumberFormField: String = s"$sessionStatsTableTag.page" + + override def goButtonFormPath: String = { + val encodedSortColumn = URLEncoder.encode(sortColumn, UTF_8.name()) + s"$parameterPath&$sessionStatsTableTag.sort=$encodedSortColumn&$sessionStatsTableTag.desc=$desc" + } + + override def headers: Seq[Node] = { + val sessionTableHeaders = + Seq("User", "IP", "Session ID", "Start Time", "Finish Time", "Duration", "Total Execute") + + val tooltips = Seq[Option[String]](None, None, None, None, None, None, None) + + assert(sessionTableHeaders.length == tooltips.length) + + val headerRow: Seq[Node] = { + sessionTableHeaders.zip(tooltips).map { case (header, tooltip) => + if (header == sortColumn) { + val headerLink = Unparsed( + parameterPath + + s"&$sessionStatsTableTag.sort=${URLEncoder.encode(header, UTF_8.name())}" + + s"&$sessionStatsTableTag.desc=${!desc}" + + s"&$sessionStatsTableTag.pageSize=$pageSize" + + s"#$sessionStatsTableTag") + val arrow = if (desc) "▾" else "▴" // UP or DOWN + + if (tooltip.nonEmpty) { + <th> + <a href={headerLink}> + <span data-toggle="tooltip" title={tooltip.get}> + {header} {Unparsed(arrow)} + </span> + </a> + </th> + } else { + <th> + <a href={headerLink}> + {header} {Unparsed(arrow)} + </a> + </th> + } + } else { + val headerLink = Unparsed( + parameterPath + + s"&$sessionStatsTableTag.sort=${URLEncoder.encode(header, UTF_8.name())}" + + s"&$sessionStatsTableTag.pageSize=$pageSize" + + s"#$sessionStatsTableTag") + + if(tooltip.nonEmpty) { + <th> + <a href={headerLink}> + <span data-toggle="tooltip" title={tooltip.get}> + {header} + </span> + </a> + </th> + } else { + <th> + <a href={headerLink}> + {header} + </a> + </th> + } + } + } + } + <thead> + {headerRow} + </thead> + } + + override def row(session: SessionInfo): Seq[Node] = { + val sessionLink = "%s/%s/session/?id=%s".format( + UIUtils.prependBaseUri(request, parent.basePath), parent.prefix, session.sessionId) + <tr> + <td> {session.userName} </td> + <td> {session.ip} </td> + <td> <a href={sessionLink}> {session.sessionId} </a> </td> + <td> {formatDate(session.startTimestamp)} </td> + <td> {if (session.finishTimestamp > 0) formatDate(session.finishTimestamp)} </td> + <td sorttable_customkey={session.totalTime.toString}> + {formatDurationOption(Some(session.totalTime))} </td> + <td> {session.totalExecution.toString} </td> + </tr> + } + + /** + * Returns a human-readable string representing a duration such as "5 second 35 ms" + */ + private def formatDurationOption(msOption: Option[Long]): String = { + msOption.map(formatDurationVerbose).getOrElse("-") + } + + private def errorMessageCell(errorMessage: String): Seq[Node] = { + val isMultiline = errorMessage.indexOf('\n') >= 0 + val errorSummary = StringEscapeUtils.escapeHtml4( + if (isMultiline) { + errorMessage.substring(0, errorMessage.indexOf('\n')) + } else { + errorMessage + }) + val details = if (isMultiline) { + // scalastyle:off + <span onclick="this.parentNode.querySelector('.stacktrace-details').classList.toggle('collapsed')" + class="expand-details"> + + details + </span> ++ + <div class="stacktrace-details collapsed"> + <pre> + {errorMessage} + </pre> + </div> + // scalastyle:on + } else { + "" + } + <td> + {errorSummary}{details} + </td> + } + + private def jobURL(request: HttpServletRequest, jobId: String): String = Review comment: This method also seems not used? ---------------------------------------------------------------- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. For queries about this service, please contact Infrastructure at: us...@infra.apache.org With regards, Apache Git Services --------------------------------------------------------------------- To unsubscribe, e-mail: reviews-unsubscr...@spark.apache.org For additional commands, e-mail: reviews-h...@spark.apache.org