Skip to content

Commit c9b8003

Browse files
authored
Merge pull request #2195 from codalab/submission-logs-tabs
Group the submission logs into dynamic tabs
2 parents 97e0076 + 911af84 commit c9b8003

1 file changed

Lines changed: 107 additions & 102 deletions

File tree

src/static/riot/competitions/detail/submission_modal.tag

Lines changed: 107 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
<div class="submission-modal item" data-tab="admin" if="{submission.admin}">ADMIN</div>
77
<div class="submission-modal item" data-tab="{admin_: submission.admin}fact_sheet">FACT SHEET ANSWERS</div>
88
</div>
9+
<!-- Downloads -->
910
<div class="ui tab active modal-tab" data-tab="{admin_: submission.admin}downloads">
1011
<div class="ui relaxed centered grid">
1112
<div class="ui fifteen wide column">
@@ -36,118 +37,95 @@
3637
</div>
3738
</div>
3839
</div>
40+
<!-- Logs -->
3941
<div class="ui tab modal-tab" data-tab="{admin_: submission.admin}logs" hide="{opts.hide_output}">
40-
<div class="ui grid">
41-
<div class="three wide column">
42-
<div class="ui fluid vertical secondary menu">
43-
<div class="active submission-modal item" data-tab="{admin_: submission.admin}prediction">
44-
Prediction Logs
45-
</div>
46-
<div class="submission-modal item" data-tab="{admin_: submission.admin}scoring">
47-
Scoring Logs
48-
</div>
49-
</div>
50-
</div>
51-
<div class="thirteen wide column">
52-
<div class="ui active tab" data-tab="{admin_: submission.admin}prediction">
53-
<div class="ui top attached inverted pointing menu">
54-
<div class="active submission-modal item" data-tab="{admin_: submission.admin}p_stdout">
55-
stdout
56-
</div>
57-
<div class="submission-modal item" data-tab="{admin_: submission.admin}p_stderr">
58-
stderr
59-
</div>
60-
<div class="submission-modal item" data-tab="{admin_: submission.admin}p_ingest_stdout">
61-
Ingestion stdout
62-
</div>
63-
<div class="submission-modal item" data-tab="{admin_: submission.admin}p_ingest_stderr">
64-
Ingestion stderr
65-
</div>
66-
</div>
67-
68-
<div class="ui active bottom attached inverted segment tab log"
69-
data-tab="{admin_: submission.admin}p_stdout">
70-
<!--
71-
todo: something like:
72-
<pre>{ logs.prediction_stdout ? logs.prediction_stdout : "Empty Logs"}</pre>
73-
so log files don't look empty
74-
-->
75-
<pre>{ logs.prediction_stdout }</pre>
76-
</div>
77-
78-
<div class="ui bottom attached inverted segment tab log"
79-
data-tab="{admin_: submission.admin}p_stderr">
80-
<pre>{ logs.prediction_stderr }</pre>
81-
</div>
82-
83-
<div class="ui bottom attached inverted segment tab log"
84-
data-tab="{admin_: submission.admin}p_ingest_stdout">
85-
<pre>{ logs.prediction_ingestion_stdout }</pre>
86-
</div>
87-
88-
<div class="ui bottom attached inverted segment tab log"
89-
data-tab="{admin_: submission.admin}p_ingest_stderr">
90-
<pre>{ logs.prediction_ingestion_stderr }</pre>
91-
</div>
92-
</div>
93-
<div class="ui tab" data-tab="{admin_: submission.admin}scoring">
94-
<div class="ui top attached inverted pointing menu">
95-
<div class="active submission-modal item" data-tab="{admin_: submission.admin}s_stdout">
96-
stdout
97-
</div>
98-
<div class="submission-modal item" data-tab="{admin_: submission.admin}s_stderr">
99-
stderr
100-
</div>
101-
<div class="submission-modal item" data-tab="{admin_: submission.admin}s_ingest_stdout">
102-
Ingestion stdout
103-
</div>
104-
<div class="submission-modal item" data-tab="{admin_: submission.admin}s_ingest_stderr">
105-
Ingestion stderr
106-
</div>
107-
</div>
108-
109-
<div class="ui active bottom attached inverted segment tab log"
110-
data-tab="{admin_: submission.admin}s_stdout">
111-
<pre>{ logs.scoring_stdout }</pre>
112-
</div>
113-
114-
<div class="ui bottom attached inverted segment tab log"
115-
data-tab="{admin_: submission.admin}s_stderr">
116-
<pre>{ logs.scoring_stderr }</pre>
117-
</div>
118-
119-
<div class="ui bottom attached inverted segment tab log"
120-
data-tab="{admin_: submission.admin}s_ingest_stdout">
121-
<pre>{ logs.scoring_ingestion_stdout }</pre>
122-
</div>
123-
124-
<div class="ui bottom attached inverted segment tab log"
125-
data-tab="{admin_: submission.admin}s_ingest_stderr">
126-
<pre>{ logs.scoring_ingestion_stderr }</pre>
127-
</div>
128-
</div>
42+
<div class="ui top attached inverted pointing menu" if="{logTabs.length > 0}" ref="log_tabs_menu">
43+
<div each="{tab, i in logTabs}"
44+
class="submission-modal item {active: tab.fullTabId === activeLogTabId || (!activeLogTabId && i === 0)}"
45+
data-tab="{tab.fullTabId}"
46+
onclick="{() => activeLogTabId = tab.fullTabId}">
47+
{tab.label}
12948
</div>
13049
</div>
50+
<!-- If no logs -->
51+
<div class="ui bottom attached inverted segment tab log active" if="{logTabs.length === 0}">
52+
<pre class="empty">No logs available for this submission.</pre>
53+
</div>
54+
<!-- Dynamic tabs -->
55+
<div each="{tab, i in logTabs}"
56+
class="ui bottom attached inverted segment tab log {active: tab.fullTabId === activeLogTabId || (!activeLogTabId && i === 0)}"
57+
data-tab="{tab.fullTabId}">
58+
<pre class="{empty: is_empty(tab.content)}">{ show_log(tab.content) }</pre>
59+
</div>
13160
</div>
61+
<!-- Fact sheet -->
13262
<div class="ui tab modal-tab" data-tab="{admin_: submission.admin}fact_sheet">
13363
<div class="ui inverted segment log">
13464
<textarea name="fact-sheet" id="fact_sheet" ref="fact_sheet_text_area">{ JSON.stringify(fact_sheet_answers, null, 2) }</textarea>
13565
</div>
13666
<div class="ui button green" onclick="{update_fact_sheet.bind(this)}">Save</div>
13767
</div>
68+
<!-- Visualization -->
13869
<div class="ui tab modal-tab" data-tab="{admin_: submission.admin}graph" show="{opts.show_visualization && (!opts.hide_output || submission.admin)}">
13970
<iframe src="{detailed_result}" class="graph-frame" show="{detailed_result}"></iframe>
14071
</div>
72+
<!-- Admin -->
14173
<div class="ui tab leaderboard-tab" data-tab="admin" if="{submission.admin}">
14274
<submission-scores leaderboards="{leaderboards}"></submission-scores>
14375
</div>
76+
14477
<script>
14578
var self = this
14679
self.submission = {}
14780
self.logs = {}
14881
self.leaderboards = []
14982
self.columns = []
15083

84+
// Logs helpers
85+
self.non_empty = (v) => !self.is_empty(v)
86+
self.show_log = (v) => self.non_empty(v) ? self.normalizeLog(v) : "No logs for this tab."
87+
self.normalizeLog = (v) => {
88+
if (v == null) return v
89+
if (Array.isArray(v)) return v.join('\n')
90+
if (typeof v === "object") {
91+
try { return JSON.stringify(v, null, 2) } catch { return String(v) }
92+
}
93+
return String(v)
94+
}
95+
self.is_empty = (v) => {
96+
v = self.normalizeLog(v)
97+
return v == null || (typeof v === "string" && v.trim().length === 0)
98+
}
99+
100+
// Dynamic tabs state
101+
self.logTabs = []
102+
103+
self.rebuild_log_tabs = () => {
104+
const prefix = self.submission && self.submission.admin ? 'admin_' : ''
105+
const candidates = [
106+
{ key:'p_stdout', label:'Prediction output', content: self.logs.prediction_stdout },
107+
{ key:'p_stderr', label:'Prediction errors', content: self.logs.prediction_stderr },
108+
{ key:'p_ing_out', label:'Ingestion output', content: self.logs.prediction_ingestion_stdout },
109+
{ key:'p_ing_err', label:'Ingestion errors', content: self.logs.prediction_ingestion_stderr },
110+
{ key:'s_stdout', label:'Scoring output', content: self.logs.scoring_stdout },
111+
{ key:'s_stderr', label:'Scoring errors', content: self.logs.scoring_stderr },
112+
{ key:'s_ing_out', label:'Scoring ingestion output', content: self.logs.scoring_ingestion_stdout },
113+
{ key:'s_ing_err', label:'Scoring ingestion errors', content: self.logs.scoring_ingestion_stderr },
114+
]
115+
116+
// Keep only non empty tabs
117+
self.logTabs = candidates
118+
.filter(t => !self.is_empty(t.content))
119+
.map(t => ({
120+
...t,
121+
fullTabId: `${prefix}sub_${self.submission.id}_${t.key}`
122+
}))
123+
124+
if (!self.activeLogTabId || !self.logTabs.find(t => t.fullTabId === self.activeLogTabId)) {
125+
self.activeLogTabId = self.logTabs.length ? self.logTabs[0].fullTabId : null
126+
}
127+
}
128+
151129
self.get_score_details = function (column) {
152130
try {
153131
let score = _.filter(self.submission.scores, (score) => {
@@ -159,6 +137,9 @@
159137
}
160138
}
161139
self.update_submission_details = () => {
140+
self.logs = {}
141+
self.rebuild_log_tabs()
142+
self.update()
162143
CODALAB.api.get_submission_details(self.submission.id)
163144
.done(function (data) {
164145
self.leaderboards = data.leaderboards
@@ -168,13 +149,26 @@
168149
self.detailed_result = data.detailed_result
169150
self.fact_sheet_answers = data.fact_sheet_answers
170151

171-
_.forEach(data.logs, (item) => {
172-
$.get(item.data_file)
173-
.done(function (content) {
174-
self.logs[item.name] = content
175-
self.update()
176-
})
152+
const requests = data.logs.map(item =>
153+
$.get(item.data_file)
154+
.done(content => { self.logs[item.name] = content })
155+
.fail(() => { self.logs[item.name] = "" })
156+
)
157+
// When all log files are done loading:
158+
$.when.apply($, requests).always(() => {
159+
self.rebuild_log_tabs()
160+
self.update()
161+
setTimeout(() => {
162+
if (!self.refs.log_tabs_menu) return
163+
const $items = $(self.refs.log_tabs_menu).find('.item')
164+
$items.tab()
165+
if (self.logTabs.length) {
166+
$items.tab('change tab', self.activeLogTabId || self.logTabs[0].fullTabId)
167+
}
168+
}, 0)
177169
})
170+
self.rebuild_log_tabs()
171+
self.update()
178172
if (self.submission.admin) {
179173
_.forEach(data.leaderboards, (leaderboard) => {
180174
_.map(leaderboard.columns, (column) => {
@@ -213,10 +207,15 @@
213207

214208
CODALAB.events.on('submission_clicked', () => {
215209
self.submission = opts.submission
210+
// reset per-submission state
211+
self.logs = {}
212+
self.logTabs = []
213+
self.activeLogTabId = null
214+
// update
216215
self.update()
217216
self.update_submission_details()
218217
let path = self.submission.admin ? 'admin_downloads' : 'downloads'
219-
$('.menu .submission-modal.item').tab('change tab', path)
218+
$('.ui.large.green.pointing.menu .submission-modal.item').tab('change tab', path)
220219
})
221220
</script>
222221

@@ -235,7 +234,7 @@
235234

236235
.file-download
237236
margin-top 25px !important
238-
margin-botton 25px !important
237+
margin-bottom 25px !important
239238

240239
.graph-frame
241240
height 100%
@@ -246,10 +245,16 @@
246245
#downloads thead tr th, #downloads tbody tr td
247246
font-size 16px !important
248247

249-
.inverted, textarea
250-
color: white
251-
background: #1b1c1d
252-
width: 100%
253-
height: 98%
248+
pre.empty
249+
opacity 0.7
250+
251+
.log
252+
color white
253+
background #1b1c1d
254+
255+
.log textarea
256+
width 100%
257+
height 98%
258+
254259
</style>
255260
</submission-modal>

0 commit comments

Comments
 (0)