Skip to content

Commit

Permalink
Merge pull request #2367 from CannonLock/SOFTWARE-5095
Browse files Browse the repository at this point in the history
Software 5095
  • Loading branch information
matyasselmeci authored Mar 23, 2022
2 parents d14c3dc + 9b13e57 commit 0a9de38
Show file tree
Hide file tree
Showing 9 changed files with 523 additions and 38 deletions.
13 changes: 13 additions & 0 deletions src/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,12 @@ def resources():
return render_template("resources.html.j2")


@app.route('/collaborations')
def collaboration_list():

return render_template("collaborations.html.j2")


@app.route("/collaborations/osg-scitokens-mapfile.conf")
def collaborations_scitoken_text():
"""Dumps output of /bin/get-scitokens-mapfile --regex at a text endpoint"""
Expand Down Expand Up @@ -197,6 +203,7 @@ def collaborations_scitoken_text():
return Response(mapfile, mimetype="text/plain")



@app.route('/contacts')
def contacts():
try:
Expand Down Expand Up @@ -240,6 +247,12 @@ def miscresource_json():
def vosummary_xml():
return _get_xml_or_fail(global_data.get_vos_data().get_tree, request.args)

@app.route('/vosummary/json')
def vosummary_json():
return Response(to_json_bytes(
simplify_attr_list(global_data.get_vos_data().get_expansion(), namekey='Name')
), mimetype="application/json")


@app.route('/rgsummary/xml')
def rgsummary_xml():
Expand Down
84 changes: 84 additions & 0 deletions src/static/js/pages/collaborations.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import {create_ul} from "../util.js";
import {TablePage} from "../table.js"

const data_function = async () => {
let data = await fetch("/vosummary/json").then(d => d.json())
return data
}

const columns = [
{
id: 'LongName',
name: 'Long Name'
}, {
id: 'PrimaryURL',
name: 'Primary URL'
}, {
id: 'FieldsOfScience',
name: "FieldsOfScience",
data: d => d,
formatter: data => {
if(!("FieldsOfScience" in data && data["FieldsOfScience"] != null)){
return ""
}
let list = []
for(const [key, value] of Object.entries(data["FieldsOfScience"])){
list = list.concat(value["Field"])
}
return list.join(", ")
},
html: data => {
let key = "FieldsOfScience"
if(key in data){
return create_ul(data[key])
}
let d = document.createElement("span")
d.innerText = "null"
return d
}
}, {
id: "DataFederations",
name: 'Data Federation',
hidden: true,
data: d => d,
html: data => {
let key = "DataFederations"
if(key in data){
return create_ul(data[key])
}
let d = document.createElement("span")
d.innerText = "null"
return d
}
}, {
id: 'OASIS',
name: 'Oasis',
hidden: true,
data: d => d,
html: data => {
let key = "OASIS"
if(key in data){
return create_ul(data[key])
}
let d = document.createElement("span")
d.innerText = "null"
return d
}
}, {
id: 'Credentials',
name: 'Credentials',
hidden: true,
data: d => d,
html: data => {
let key = "Credentials"
if(key in data && data[key]){
return create_ul(data[key]["TokenIssuers"]["TokenIssuer"])
}
let d = document.createElement("span")
d.innerText = "null"
return d
}
}
]

const page = new TablePage(data_function, columns)
1 change: 0 additions & 1 deletion src/static/js/pages/resources.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import {TablePage} from "../table.js"

const data_function = async () => {
let data = await fetch("/miscresource/json").then(d => d.json())
console.log(data)
return data
}

Expand Down
137 changes: 111 additions & 26 deletions src/static/js/table.js
Original file line number Diff line number Diff line change
@@ -1,73 +1,155 @@
import {remove_children} from "./util.js";
import {remove_children, flatten, tokenize_object} from "./util.js";

function populate_node(data, node, columns){
columns.forEach(column => {
for(const column of columns){
let column_node = node.getElementsByClassName(column["id"])[0]

if("html" in column){
if(column_node === undefined){continue}

column_node.parentNode.hidden = false

if("html" in column && data[column["id"]] !== undefined && data[column["id"]] !== null){

remove_children(column_node)

let child = column['html'](data)
column_node.appendChild(child)
} else {
column_node.innerText = data[column["id"]].toString()
if(data[column["id"]] === null || data[column["id"]] === undefined){
column_node.parentNode.hidden = true
} else {
column_node.innerText = data[column["id"]].toString()
}
}
})
}
}

class Search {
constructor(data_function, listener) {

this.data_function = data_function
this.listener = listener

this.node = document.getElementById("search")
this.error_node = document.getElementById("search-error")
this.history_node = document.getElementById("search-history")

this.history = new Set()
this.fields = []
this.lunr_idx = undefined
this.data_function = data_function
this.listener = listener
this.timer = undefined

this.node.addEventListener("input", this.search)
this.node.addEventListener("keypress", this.save_history)
}
search = () => {
clearTimeout(this.timer)
this.timer = setTimeout(this.listener, 250)
}
save_history = (e) => {
if(e.keyCode == 13){
this.history.add(this.node.value)
window.localStorage.setItem("search-history" + window.location.pathname, [...this.history.values()].join(","))
this.update_history_node()
}
}
update_history_node = () => {
remove_children(this.history_node)
for(const previous_search of [...this.history.values(), ...this.fields]){
let option = document.createElement("option")
option.value = previous_search
this.history_node.appendChild(option)
}
}
load_history = () => {
let history = window.localStorage.getItem("search-history" + window.location.pathname)

if(history !== null){
this.history = new Set(history.split(","))
}

this.update_history_node()
}
initialize = async () => {

if(this.lunr_idx){return}

let data = Object.entries(await this.data_function()).map(
([k, v], i) => {
v['ref'] = k
return v
})
let search = this
this.load_history()
let data = this.organize_data(await this.data_function())

// Get the data fields and load them into the search presets
this.fields = new Set()
Object.values(data).forEach(value => search.fields = new Set([...Object.keys(value), ...search.fields]))
this.fields.delete("ref")
this.fields = Array.from(search.fields.values()).sort()
this.update_history_node()

// Create the search object
this.lunr_idx = lunr(function () {
this.tokenizer.separator = /[\s]+/

this.ref('ref')

Object.keys(data[0]).forEach(k => this.field(k))
search.fields.forEach(v => this.field(v.toLowerCase()))

data.forEach(function (doc) {

let string_doc = {}
for (const [key, value] of Object.entries(doc)) {
if(typeof value != "string"){
string_doc[key] = JSON.stringify(value)
} else {

if(key == "ref"){
string_doc[key] = value
} else if(typeof value != "string"){
string_doc[key.toLowerCase()] = JSON.stringify(value).toLowerCase()
} else {
string_doc[key.toLowerCase()] = value.toLowerCase()
}
}
this.add(string_doc)
}, this)
})
}
/*
Organizes a dictionary of data for the search index.
Used to facilitate searches at various depths of nested data.
*/
organize_data = (data) => {
// Convert dictionary to array of dictionaries with previous keys under 'ref'
let data_array = Object.entries(data).map(
([k, v], i) => {
v['ref'] = k
return v
})

data_array = data_array.reduce((array_in_progress, current_value) => {
let flattened_data = flatten(current_value)

let tokenized_data = Object.keys(current_value).reduce(
(attrs, inner_key) => ({
...attrs,
[inner_key] : tokenize_object(current_value[inner_key])
}),
{}
)

// Overwrite flat data with top level values if need be
array_in_progress.push({...flattened_data, ...tokenized_data})

return array_in_progress
}, [])

return data_array
}
filter_data = (data) => {
this.error_node.innerText = ""

if(this.node.value == ""){
return data
} else {
try {
let table_keys = this.lunr_idx.search(this.node.value).map(r => r.ref)
let table_keys = this.lunr_idx.search(this.node.value.toLowerCase()).map(r => r.ref)
return table_keys.map(key => data[key])
} catch(e) {
this.error_node.innerText = e.message
Expand All @@ -87,6 +169,11 @@ class Table {
keyboard: true
})
this.columns = columns
this.columns.push({
hidden: true,
id: "data",
data: d => d
})
}
initialize = async () => {
let table = this;
Expand All @@ -113,15 +200,13 @@ class Table {
data: data
}).forceRender();
}
toggle_row = (toggled_row, project) => {
populate_node(project, this.display_node, this.columns)
toggle_row = (toggled_row, data) => {
populate_node(data, this.display_node, this.columns)
this.display_modal.show()
}
row_click = async (PointerEvent, e) => {
let data = await this.data_function()
let row_name = e["cells"][0].data
let project = data[row_name]
this.toggle_row(PointerEvent.currentTarget, project)
let data = e["cells"][e['cells'].length - 1].data
this.toggle_row(PointerEvent.currentTarget, data)
}
}

Expand All @@ -142,13 +227,13 @@ class CardDisplay{
update = async (data) => {
this.wrapper.remove_children()

for(const project of data){
for(const value of data){
let clone = this.template_card.cloneNode(true)
clone.removeAttribute('id')

populate_node(project, clone, this.columns)
populate_node(value, clone, this.columns)

let card_key = project['Name'].replace(/\s|\.|\\|\/|\(|\)/g, '')
let card_key = value['ref'].replace(/\s|\.|\\|\/|\(|\)/g, '')

clone.setAttribute("href", "#" + card_key)
clone.setAttribute("aria-controls", card_key)
Expand Down
Loading

0 comments on commit 0a9de38

Please sign in to comment.