A new (editing) frontend for VIVO
Hector Correa / hector_correa@brown.edu
Steven McCauley / steven_mccauley@brown.edu
Brown University Library
September/2019 - Podgorica, Montenegro
VIVO at Brown
Background
VIVO at Brown: 2014
Manager
(Django App)
Data Service
(Flask App)
general public
editors (faculty)
developers
VIVO
Fuseki
Solr
Java App
VIVO at Brown: 2017
Manager
(Django App)
Data Service
(Flask App)
general public
editors (faculty)
developers
Front End
(Rails App)
Solr (replica)
VIVO
Fuseki
Solr
Java App
Demo of current setup
(two apps)
Current setup (two apps)
left: viewing frontend right: current editor frontend
left: viewing frontend right: current editor frontend
Current setup (two apps)
left: viewing frontend right: current editor frontend
Current setup (two apps)
Problems with current setup (frontend)
VIVO at Brown: 2019 (later this year)
Data Service
(Flask App)
general public
editors (faculty)
developers
Front End
(Rails App)
Solr (replica)
VIVO
Fuseki
Solr
Java App
Manager
(Django App)
Demo of new setup
(one app)
New setup (one app)
left: viewing frontend right: new editor frontend
New setup (one app)
left: viewing frontend right: new editor frontend
New setup (one app)
left: viewing frontend right: new editor frontend
Goals (frontend)
General flow when viewing data
https://vivo.brown.edu/search
https://vivo.brown.edu/display/user-id
Data Service
(Flask App)
user
Front End
(Rails App)
Solr (replica)
VIVO
Fuseki
Solr
Java App
General flow when editing data
https://vivo.brown.edu/edit/user-id
Data Service
(Flask App)
user
Front End
(Rails App)
Solr (replica)
VIVO
Fuseki
Solr
Java App
REST
SPARQL
Backend
Backend: current state
Problems
Goals
Tools for scripting RDF (and other graphs)
Prior Work: a brief sample
Desired function: transactional edits on individual resources
Object-Relational Mapper: Model + CRUD workflow
from app import models, db
@route('/edit/<userID>/overview')
def update_overview(userID):
faculty = models.Faculty.query \
.filter_by(id=userID).first()
faculty.overview = "My new overview"
db.session.add(faculty)
db.session.commit()
return {'overview': faculty.overview}
*Based on general pattern using Flask-SQLAlchemy: https://flask-sqlalchemy.palletsprojects.com/en/2.x/quickstart/
from app import db
class Faculty(Model):
__table__ = 'faculty'
id = db.Column(db.Integer, key=True)
overview = db.Column(db.String)
statement = db.Column(db.String, length=500)
research_areas = db.Column(db.Integer,
relationship='ResearchArea')
weblinks = db.Column(db.Integer,
relationship='WebLink')
id | overview | statement | research_area | weblinks |
1 | "My old overview" | "My statement" | 17 | 13 |
TABLE 'faculty'
Designing an ORM for RDF ("R" is for "Resource")
RDF and ORM workflow: transactions and commits
def update_overview(userID):
…
…
db.session.commit()
msg = '''
PREFIX
DELETE DATA {
GRAPH {
}
}
INSERT DATA {
GRAPH {
}
}
'''
http.post('http://localhost:8080/vivo/api/sparqlUpdate', data={'update': msg})
RDF and ORM workflow: transactions and commits
def update_overview(userID):
…
db.session.add(faculty)
db.session.commit()
msg = '''
PREFIX brown <http://vivo.brown.edu/profile/>
DELETE DATA {
GRAPH <http://vivo.brown.edu/data> {
<http://vivo.brown.edu/individual/steve> brown:overview "My old overview"^^xsd:string .
}
}
INSERT DATA {
GRAPH <http://vivo.brown.edu/data> {
<http://vivo.brown.edu/individual/steve> brown:overview "My new overview"^^xsd:string .
}
}
'''
http.POST('http://localhost:8080/vivo/api/sparqlUpdate', data={'update': msg})
RDF and ORM workflow: Resource definition
from app import db
class Faculty(Model):
__table__ = 'faculty'
id = db.Column(db.Integer, key=True)
overview = db.Column(db.String)
statement = db.Column(db.String, length=500)
research_areas = db.Column(db.Integer,
relationship='ResearchArea')
weblinks = db.Column(db.Integer,
relationship='WebLink')
from app import db
class Faculty(Resource):
graph = 'http://vivo.brown.edu/data'
id = db.URI
overview = db.Property(uri='brown:overview',
datatype=db.String)
statement = db.Property(uri='brown:statement',
datatype=db.String, length=500)
research_areas = db.Property(uri='brown:topics',
relationship='ResearchArea')
weblinks = db.Property(uri='brown:weblinks'...
<http://.../steve> | brown:overview | "My old overview" |
<http://.../steve> | brown:statement | "My statement" |
<http://.../steve> | ... | ... |
id | overview | statement | ... |
1 | "My old overview" | "My statement" | ... |
Resource loading: dynamic attributes
from app import db
class Faculty(Resource):
…
overview = db.Property(uri='brown:overview',
datatype=db.String)
statement = db.Property(uri='brown:statement',
datatype=db.String, length=500)
research_areas = db.Property(uri='brown:topics',
relationship='ResearchArea')
weblinks = db.Property(uri='brown:weblinks',
relationship='WebLink')
msg = '''
PREFIX brown <http://vivo.brown.edu/profile/>
CONSTRUCT {
?uri brown:overview ?overview .
?uri …
}
WHERE {
?uri rdf:type brown:Faculty .
OPTIONAL { ?uri brown:overview ?overview .}
OPTIONAL { …
}
OPTIONAL clauses
Resource loading: dynamic attributes
from app import db
class Faculty(Resource):
…
overview = db.Property(uri='brown:overview',
datatype=db.String)
statement = db.Property(uri='brown:statement',
datatype=db.String, length=500)
research_areas = db.Property(uri='brown:topics',
relationship='ResearchArea')
weblinks = db.Property(uri='brown:weblinks',
relationship='WebLink')
msg = '''
PREFIX brown <http://vivo.brown.edu/profile/>
CONSTRUCT {
?uri brown:overview ?overview .
?uri …
}
WHERE {
?uri rdf:type brown:Faculty .
OPTIONAL { ?uri brown:overview ?overview .}
OPTIONAL { …
}
OPTIONAL clauses
Resource loading: dynamic attributes
from app import db
class Faculty(Resource):
…
overview = db.Property(uri='brown:overview',
datatype=db.String)
statement = db.Property(uri='brown:statement',
datatype=db.String, length=500)
research_areas = db.Property(uri='brown:topics',
relationship='ResearchArea')
weblinks = db.Property(uri='brown:weblinks',
relationship='WebLink')
msg = '''
PREFIX brown <http://vivo.brown.edu/profile/>
CONSTRUCT {
?uri brown:overview ?overview .
?uri …
}
WHERE {
?uri rdf:type brown:Faculty .
} UNION {
?uri rdf:type brown:Faculty
?uri brown:overview ?overview .
} UNION { ...
}
UNION clauses
Resource loading: dynamic attributes
from app import db
class Faculty(Resource):
…
overview = db.Property(uri='brown:overview',
datatype=db.String)
statement = db.Property(uri='brown:statement',
datatype=db.String, length=500)
research_areas = db.Property(uri='brown:topics',
relationship='ResearchArea')
weblinks = db.Property(uri='brown:weblinks',
relationship='WebLink')
msg = '''
PREFIX brown <http://vivo.brown.edu/profile/>
CONSTRUCT {
?uri brown:overview ?overview .
?uri …
}
WHERE {
?uri rdf:type brown:Faculty .
} UNION {
?uri rdf:type brown:Faculty
?uri brown:overview ?overview .
} UNION { ...
}
UNION clauses
Resource loading: dynamic attributes
def update_overview(userID):
faculty = models.Faculty.query...
from app import db
class Faculty(Resource):
…
overview = db.Property(uri='brown:overview',
datatype=db.String)
statement = db.Property(uri='brown:statement',
datatype=db.String, length=500)
research_areas = db.Property(uri='brown:topics',
relationship='ResearchArea')
weblinks = db.Property(uri='brown:weblinks',
relationship='WebLink')
msg = "DESCRIBE <http://.../steve>"
data = http.post(
'http://localhost:8080/vivo/api/sparqlQuery',
data={'query': msg})
<http://.../steve> | brown:overview | ... |
<http://.../steve> | obo:71003401 | ... |
<http://.../steve> | brown:statement | ... |
<http://.../steve> | obo:04300002 | ... |
Resource loading: dynamic attributes
def update_overview(userID):
faculty = models.Faculty.query...
from app import db
class Faculty(Resource):
…
overview = db.Property(uri='brown:overview',
datatype=db.String)
statement = db.Property(uri='brown:statement',
datatype=db.String, length=500)
research_areas = db.Property(uri='brown:topics',
relationship='ResearchArea')
weblinks = db.Property(uri='brown:weblinks',
relationship='WebLink')
msg = "DESCRIBE <http://.../steve>"
data = http.post(
'http://localhost:8080/vivo/api/sparqlQuery',
data={'query': msg})
faculty = models.Faculty.load(data)
<http://.../steve> | brown:overview | ... |
<http://.../steve> | obo:71003401 | ... |
<http://.../steve> | brown:statement | ... |
<http://.../steve> | obo:04300002 | ... |
RDF and ORM workflow: the rest
faculty = models.Faculty.query.filter_by(id='steve').first()
faculty.overview = "My new overview"
faculty = models.Faculty.query \
.filter_by(id='steve').first()
faculty.overview = "My new overview"
db.session.add(faculty)
db.session.commit()
msg = '''
DESCRIBE ?uri
WHERE {
?uri rdf:type brown:Faculty.
?uri brown:id "steve"^^xsd:string .
}
'''
Query filtering
remove = [(<http://vivo.brown.edu/individual/steve>,<brown:overview>,"\\"My old overview\\"^^xsd:string")]
add = [(<http://vivo.brown.edu/individual/steve>,<brown:overview>,"\\"My new overview\\"^^xsd:string")]
Attribute update
Designing an ORM for RDF ("R" is for "Resource")
In closing...
Takeaways
Thanks!
[the end]
[backup slides]
Future work
Queries: current state
Issues
Queries: current state
Issues
Queries: current state
Issues
Data editing: current state
Issues
Data editing: current state
Issues
Data ecosystem: current state
Resource-oriented SPARQL
Resource-oriented SPARQL
Object-Relational Mapper: CRUD workflow
from app import models, db
def update_overview(facultyID):
data = request.json()
faculty = models.Faculty.query.filter_by(id=facultyID).first()
faculty.overview = data.get(‘overview’)
db.session.add(faculty)
db.session.commit()
return {‘overview’: faculty.overview}
Problems with current setup (backend)
Visualizations
Reports
RDF and SPARQL: enforcing consistency
Hand-coding leads to data anomalies and other development headaches
RDF development is hampered by the lack of standard tools
VIVO at Brown
Manager
(Django App)
Data Service
(Flask App)
general public
editors (faculty)
developers
Front End
(Rails App)
Solr (replica)
VIVO
Fuseki
Solr
Java App