Skip to content

Commit 39db4f8

Browse files
Move covmanager into frontend
1 parent 15350e9 commit 39db4f8

32 files changed

+4035
-2606
lines changed

server/covmanager/templates/collections/browse.html

+6-940
Large diffs are not rendered by default.
Original file line numberDiff line numberDiff line change
@@ -1,356 +1,9 @@
1-
{% extends 'layouts/layout_old.html' %}
2-
3-
{% block css.custom %}
4-
<link rel='stylesheet' href='/static/css/covmanager.css'>
5-
{% endblock css.custom %}
1+
{% extends 'layouts/layout_base.html' %}
62

73
{% block title %}Collections{% endblock title %}
84

95
{% block body_content %}
10-
<div id="main" class="panel panel-default">
11-
<div class="panel-heading"><i class="bi bi-clipboard-data"></i> Coverage Collections</div>
12-
<div class="panel-body">
13-
<div class="panel panel-default" style="float: left;">
14-
<div class="panel-heading"><i class="bi bi-archive-fill"></i> Repository Filters</div>
15-
<div class="panel-body">
16-
<div>
17-
<label>Repository:</label><input type="text" class="form-control" v-model="search.repository.value"
18-
@focus="suggestions.repository.enabled = true" @blur="suggestions.repository.enabled = false"/>
19-
<div v-show="suggestions.repository.enabled">
20-
<div v-for="name in suggestions.repository.value" v-on:mousedown="take_suggestion('repository', name)">!{ name }!</div>
21-
</div>
22-
</div>
23-
<div><label>Revision:</label><input type="text" class="form-control" v-model="search.revision.value"/></div>
24-
<div><label>Branch:</label><input type="text" class="form-control" v-model="search.branch.value"/></div>
25-
</div>
26-
</div>
27-
28-
<div class="panel panel-default" style="float: left;">
29-
<div class="panel-heading"><i class="bi bi-funnel-fill"></i> Misc Filters</div>
30-
<div class="panel-body">
31-
<div>
32-
<label>Tool:</label> <input type="text" class="form-control" v-model="search.tools.value"
33-
@focus="suggestions.tools.enabled = true" @blur="suggestions.tools.enabled = false"/>
34-
<div v-show="suggestions.tools.enabled">
35-
<div v-for="name in suggestions.tools.value" v-on:mousedown="take_suggestion('tools', name)">!{ name }!</div>
36-
</div>
37-
</div>
38-
<div><label>Description:</label> <input type="text" class="form-control" v-model="search.description.value"/></div>
39-
<div><label>Result Limit:</label> <input type="text" class="form-control" v-model="search.limit.value"/></div>
40-
</div>
41-
</div>
42-
43-
<div class="panel panel-default" style="float: left;">
44-
<div class="panel-heading"><i class="bi bi-calendar-range"></i> Date Filters</div>
45-
<div class="panel-body">
46-
<!--
47-
<label>Newer than:</label><datepicker placeholder="Select Date"></datepicker>
48-
<label>Older than:</label><datepicker placeholder="Select Date"></datepicker>
49-
-->
50-
</div>
51-
</div>
52-
53-
<div class="panel panel-default" style="float: right;">
54-
<div class="panel-heading"><i class="bi bi-lightning-charge-fill"></i> Actions</div>
55-
<div class="panel-body">
56-
<button @click="navigate('diff')" class="btn btn-default">View Differences</button>
57-
<button @click="aggregate()" class="btn btn-default">Aggregate</button>
58-
<button @click="navigate('patch')" class="btn btn-default">Patch Analysis</button>
59-
<button @click="summary()" class="btn btn-default">Report Summary</button>
60-
</div>
61-
</div>
62-
63-
</div>
64-
<table class="table table-condensed table-hover table-bordered table-db">
65-
<thead>
66-
<tr>
67-
<th @click="sortBy('id')" :class="{ active: sortKey == 'id' }" style="width: 25px;">ID</th>
68-
<th @click="sortBy('created')" :class="{ active: sortKey == 'created' }" style="width: 80px;">Created</th>
69-
<th @click="sortBy('repository')" :class="{ active: sortKey == 'repository' }" style="width: 50px;">Repository</th>
70-
<th @click="sortBy('revision')" :class="{ active: sortKey == 'revision' }" style="width: 100px;">Revision</th>
71-
<th @click="sortBy('branch')" :class="{ active: sortKey == 'branch' }" style="width: 50px;">Branch</th>
72-
<th @click="sortBy('tools')" :class="{ active: sortKey == 'tools' }" style="width: 50px;">Tools</th>
73-
<th @click="sortBy('description')" :class="{ active: sortKey == 'description' }" style="width: 150px;">Description</th>
74-
</tr>
75-
</thead>
76-
<tbody>
77-
<tr v-for="collection in ordered_collections" :id="collection.id" @click="collection_click_handler(collection.id)">
78-
<td><a :href="`${collection.id}/browse`">!{ collection.id }!</a></td>
79-
<td>!{ formatDate(collection.created) }!</td>
80-
<td><a :href="`../repositories/`">!{ collection.repository }!</a></td>
81-
<td>!{ collection.revision }!</td>
82-
<td>!{ collection.branch }!</td>
83-
<td>!{ collection.tools }!</td>
84-
<td>!{ collection.description }!</td>
85-
</tr>
86-
</tbody>
87-
</table>
88-
</div>
89-
90-
<script>
91-
let URLS = {
92-
collections_api: '{% url 'covmanager:collections_api' %}',
93-
diff: '{% url 'covmanager:collections_diff' %}',
94-
patch: '{% url 'covmanager:collections_patch' %}',
95-
aggregate: '{% url 'covmanager:collections_aggregate_api' %}',
96-
repository_search : '{% url 'covmanager:repositories_search_api' %}',
97-
tools_search : '{% url 'covmanager:tools_search_api' %}',
98-
}
99-
100-
let pmanager = new HashParamManager()
101-
102-
const app = Vue.createApp({
103-
delimiters: ['!{', '}!'],
104-
data() {
105-
return {
106-
collections: null,
107-
sortKey: "",
108-
reverse: false,
109-
block_fetch: true,
110-
search_initialized: false,
111-
112-
search: {
113-
branch: { value : "", contains : true },
114-
description: { value : "", contains : true },
115-
repository: { value : "", contains: true, postfix: "__name" },
116-
revision: { value : "", contains : true },
117-
tools: { value : "", contains: true, postfix: "__name" },
118-
limit: { value : "10", contains : false },
119-
},
120-
121-
suggestions: {
122-
repository: { value: [], enabled : false},
123-
tools: { value: [], enabled : false},
124-
},
125-
126-
selected_collections: [],
127-
}
128-
},
129-
created() {
130-
const self = this
131-
pmanager.forEach(function(k,v) {
132-
// TODO: This is a shortcut that saves us iterating through this.search
133-
// for every key that we are trying to map to a field in our search
134-
// object, but this won't work once we have more postfixes.
135-
k = k.replace(/__contains$/, '')
136-
k = k.replace(/__name$/, '')
137-
138-
if (k != "" && k in self.search) {
139-
self.search[k].value = v
140-
}
141-
})
142-
143-
self.fetch()
144-
},
145-
watch: {
146-
// This handles all search updates
147-
search: { handler: 'fetch', deep: true },
148-
149-
// Watches for changes to values that need suggestions
150-
'search.repository.value': function() {
151-
this.update_suggestions('repository')
152-
},
153-
'search.tools.value': function() {
154-
this.update_suggestions('tools')
155-
},
156-
collections: function() {
157-
/* Whenever our search results change, deselect everything */
158-
this.selected_collections = []
159-
for (let i = 0; i < this.collections.length; ++i) {
160-
let target = $("#" + this.collections[i].id)
161-
if (target)
162-
target.toggleClass("collection-selected", false)
163-
}
164-
}
165-
},
166-
computed: {
167-
ordered_collections() {
168-
return _.orderBy(this.collections, [this.sortKey], [this.reverse ? 'desc' : 'asc'])
169-
},
170-
},
171-
methods: {
172-
apiurl: function() {
173-
let url = URLS.collections_api
174-
175-
for (k in this.search) {
176-
let obj = this.search[k]
177-
let v = obj.value
178-
179-
if ("postfix" in obj) {
180-
k += obj.postfix
181-
}
182-
183-
if ("contains" in obj && obj.contains) {
184-
k += "__contains"
185-
}
186-
187-
pmanager.update_value(k, v)
188-
}
189-
190-
let query = pmanager.get_query()
191-
192-
if (query) {
193-
url += "?" + query
194-
pmanager.update_hash()
195-
}
196-
197-
return url;
198-
},
199-
fetch: _.throttle (function () {
200-
this.loading = true
201-
fetch(this.apiurl(), {
202-
method: 'GET',
203-
credentials: 'same-origin'
204-
}).then(response => {
205-
if (response.ok) {
206-
return response.json()
207-
}
208-
swal('Oops', E_SERVER_ERROR, 'error')
209-
this.loading = false
210-
}).then(json => {
211-
this.collections = json["results"]
212-
this.loading = false
213-
this.block_fetch = false
214-
})
215-
}, 500),
216-
navigate: function (dst) {
217-
let ids = []
218-
219-
if (this.selected_collections.length > 0) {
220-
ids = this.selected_collections;
221-
} else {
222-
for (let i = 0; i < this.ordered_collections.length; ++i) {
223-
ids.push(this.ordered_collections[i].id)
224-
}
225-
}
226-
227-
if (ids) {
228-
let url = URLS[dst] + '#ids=' + ids.join(',')
229-
if (window.location.hash) {
230-
url += "&" + window.location.hash.substr(1)
231-
}
232-
233-
var win = window.open(url, '_blank');
234-
win.focus();
235-
}
236-
},
237-
sortBy: function (sortKey) {
238-
this.reverse = (this.sortKey === sortKey) ? !this.reverse : false
239-
this.sortKey = sortKey
240-
},
241-
update_suggestions: _.throttle (function (key) {
242-
fetch(URLS[key + '_search'] + "?name=" + this.search[key].value, {
243-
method: 'GET',
244-
credentials: 'same-origin'
245-
}).then(response => {
246-
if (response.ok) {
247-
return response.json()
248-
}
249-
swal('Oops', E_SERVER_ERROR, 'error')
250-
}).then(json => {
251-
this.suggestions[key].value = json["results"]
252-
})
253-
}, 500),
254-
take_suggestion: function(key, val) {
255-
this.suggestions[key].enabled = false
256-
this.suggestions[key].value = []
257-
this.search[key].value = val
258-
},
259-
aggregate: function() {
260-
let ids = []
261-
262-
if (this.selected_collections.length > 0) {
263-
ids = this.selected_collections;
264-
} else {
265-
for (let i = 0; i < this.ordered_collections.length; ++i) {
266-
ids.push(this.ordered_collections[i].id)
267-
}
268-
}
269-
270-
if (ids) {
271-
swal({
272-
title: "Create Collection Aggregation",
273-
text: 'Enter optional new description for result collection',
274-
content: 'input',
275-
buttons: true,
276-
}).then(description => {
277-
if (!description) {
278-
/* User pressed cancel button */
279-
return;
280-
}
281-
282-
// Call API, get new collection id back, then navigate to it
283-
this.loading = true
284-
285-
let data = {
286-
'ids': ids.join(",")
287-
}
288-
if (description) {
289-
data['description'] = description
290-
}
291-
292-
fetch(URLS.aggregate, {
293-
method: 'POST',
294-
credentials: 'same-origin',
295-
headers: {
296-
"Content-Type": "application/json; charset=utf-8",
297-
"X-Requested-With": "XMLHttpRequest"
298-
},
299-
body: JSON.stringify(data),
300-
})
301-
.then(response => {
302-
this.loading = false
303-
if (response.ok) {
304-
return response.json()
305-
}
306-
swal('Oops', E_SERVER_ERROR, 'error')
307-
})
308-
.then(json => {
309-
let newid = json["newid"]
310-
if (newid) {
311-
window.open(json["newid"] + "/browse", '_self');
312-
} else {
313-
swal('Oops', E_SERVER_ERROR, 'error')
314-
}
315-
})
316-
})
317-
}
318-
},
319-
summary: function() {
320-
let target_collection = null
321-
322-
if (this.selected_collections.length == 1) {
323-
target_collection = this.selected_collections[0]
324-
} else if (this.collections.length == 1) {
325-
target_collection = this.collections[0].pk
326-
} else {
327-
swal('Error', "Function requires exactly one collection to be selected.", 'error')
328-
return
329-
}
330-
331-
window.open(`${target_collection}/summary/`, '_blank').focus()
332-
},
333-
collection_click_handler: function(id) {
334-
var self = this
335-
let idx = self.selected_collections.indexOf(id)
336-
let target = $("#" + id)
3376

338-
if (idx < 0) {
339-
self.selected_collections.push(id)
340-
if (target)
341-
target.toggleClass("collection-selected", true)
342-
} else {
343-
self.selected_collections.splice(idx, 1)
344-
if (target)
345-
target.toggleClass("collection-selected", false)
346-
}
347-
},
348-
formatDate: function (datetime){
349-
return formatClientTimestamp(datetime)
350-
}
351-
}
352-
})
7+
<collectionslist />
3538

354-
app.mount('#main')
355-
</script>
3569
{% endblock body_content %}

0 commit comments

Comments
 (0)