The solution proposed to show the portfolio in the previous post, of course, impresses with its simplicity, but it can be made much more functional by using the
Exhibit technology from the SIMILE project.
The SIMILE project developed by MIT includes a set of applications designed to process and display information in the Semantic Web style: several frameworks for building client interfaces, tools for analyzing and debugging XML documents and HTTP requests, a set of converters from various formats to RDF and much more.

')
To solve our problem, we are well suited by one of the frameworks,
Exhibit , designed to display a set of data classified by a set of features, with the ability for the user to build samples by specifying classifiers of interest to him. Let's try to implement the mode of displaying the portfolio using this framework. Our solution is purely demonstrative in nature, we will not pay attention to the question of the performance of database queries, the subtleties of layout and style sheets, etc.
A set of examples on the main page of the framework site is enough to penetrate its capabilities. We will give a brief overview of the structure of the application using the framework and pay attention to some essential details.
Database
The database, most often formed by a web service as JSON data, is a set of items, each of which contains a set of properties.
Each element belongs to a certain type, which is determined by the value of the service
type
property of the element. Generally speaking, the belonging of an element to one type or another does not impose strict requirements on the composition of the properties of an element, however, it is logical to expect that all elements of the same type have a similar set of attributes, although some elements may not have one or another value. If the item type is not specified explicitly, the default type,
Item
, is assumed.
The system uses a set of predefined utility properties, such as:
id
- the unique identifier of the element;label
- a text description of the element, does not have to be unique, but is used as the id, if id is not specified;type
- the actual type name of the element;uri
- element URI.
An element can also include an arbitrary set of user-defined properties of various types.
In addition to its own data, the database contains some meta-information, namely the description of types and properties. Metadata is described using the same format as data elements, that is, the
id
,
uri
and
label
properties are supported.
For types other than these standard properties, you can specify the
pluralLabel
property, which contains the name of the type in the plural (for example, “Projects” for the type “Project”).
In turn, each property can be characterized by an additional property
valueType
, showing what type the property values belong to, as well as a set of additional properties that determine the test display of the value in the user interface. It is important that the type of property values can be either primitive (
text
,
number
,
date
, etc.) or object (
Item
or a value of a custom type).
Physically, the database is represented as a JSON object of the following form:
{ types: { 'type1': { // }, // }, properties: { 'property1': { // }, // }, items: [ { id: 'item 1', // }, // ] }
The framework tries to use reasonable defaults as much as possible, so that in the first approximation, you can restrict yourself to data definitions in general. In general, the data model is very close to RDF; in general, RDF is at the core of many SIMILE project products.
Portfolio data
Let's return to our task. The initial database of projects is a typical legacy database (we are currently working on a new, simpler and more logical structure). However, we can get a list of projects, classified by the following features:
- Date of publication;
- Publication year (as a means of grouping);
- Client (there are a lot of empty values, the consequence of insufficient detailing of the base to a certain stage in the development of the company);
- Profile (client's area of activity);
- Site type (store, business card, news site, etc.)
Now we will write a script that will generate a JSON file.
<?php Core::load('DB', 'IO'); $db = DB::Connection('mysql://www:www@localhost/exhibit'); IO::stdout()-> write("{\n\"types\": ")-> write(json_encode(array( '' => array('pluralLabel' => '', 'label' => '' ), '' => array('pluralLabel' => '', 'label' => '' ), '' => array('pluralLabel' => '', 'label' => '' ), '' => array('pluralLabel' => '', 'label' => '' ))))-> write(",\n\"properties\": ")-> write(json_encode(array( '' => array('valueType' => ''), '' => array('valueType' => ''), '' => array('valueType' => ''))))-> write(",\n\"items\": ["); foreach (array(<<<SQL SELECT '' `type`, CONCAT('wp-', wp.id) id, wp.name label, CONCAT('http://', wp.url) url, DATE_FORMAT(FROM_UNIXTIME(wp.publ_date), '%Y-%m-%d') ``, YEAR(FROM_UNIXTIME(wp.publ_date)) ``, CONCAT('http://www.techart.ru/', wp.screenshot) ``, IF(wp.profiles_id > 0, CONCAT('pr-', wp.profiles_id), NULL) ``, IF(wp.type_web_id > 0, CONCAT('ct-', wp.type_web_id), NULL) ``, IF(pr.clients_id > 0, CONCAT('cl-', pr.clients_id), NULL) `` FROM web_projects wp, projects pr WHERE wp.projects_id = pr.id AND wp.screenshot <> '' SQL , <<<SQL SELECT '' `type`, CONCAT('pr-', id) id, name label FROM profiles SQL , <<<SQL SELECT '' `type`, CONCAT('ct-', type_web_id) id, name label FROM type_web; SQL , <<<SQL SELECT DISTINCT '' `type`, CONCAT('cl-', c.id) id, c.name label FROM web_projects wp, projects p, clients c WHERE wp.projects_id = p.id AND p.clients_id = c.id SQL ) as $i => $sql) { IO::stdout()->write($i > 0 ? ",\n" : ''); foreach ($db->prepare($sql) as $j => $p) IO::stdout()-> write($j > 0 ? ",\n" : '')-> write(json_encode($p)); } IO::stdout()->write("\n] }"); ?>
There are a couple of significant points.
First, we use Russian names of types and attributes. Generally speaking, it does not look very natural, you can use English names. However, to localize the application with this approach is somewhat easier. The issue of localization is generally not very simple, we will return to it.
Secondly, we generate artificial record identifiers that do not carry any meaning. This is correct from the point of view of a relational database, but not very correctly in the sense of a semantic-web. We assume this is the cost of working with legacy data, if we designed the base from scratch, the approach could be different.
Actually, the application
The exhibit application is implemented as a set of JavaScript libraries and is entirely executed on the client side. The user creates an HTML page of a specific format in which the application describes in a declarative manner.
The main elements of the application are views, data display templates (lens) and filters (facet).
Representations - interface elements that are responsible for, in fact, the display of selected data. Several standard display options are supported:
- Tile - default template, linear list of elements;
- Thumbnail - a more compact display in the form of a gallery;
- Map - display of data elements on Google Maps;
- Timeline - display of data elements in the form of a timeline, this is another great technology SIMILE, worthy of a separate detailed description;
- Tabular - tabular display;
- Timeplot - display in the form of a graph, convenient for visualizing numerical data changing with time.
To display a single data element within a view, display templates (lenses) are used. The template allows the user to determine how a particular data item should look inside the view. If no pattern is specified, the environment uses a simple default pattern. Inside the template, you can use not only the field values of the data element, but also perform
various calculations on them.
Filters (facets) allow the user to specify various selection criteria. Filters are different too:
- List - the user selects the values of interest;
- Numeric Range - the user selects a range of values;
- Text Search - well, everything is clear;
- Tag Cloud - the name speaks for itself;
- Slider - the value can be selected using the slider, sometimes conveniently.
Armed with all this knowledge and reading the documentation, we will create a minimal framework for the application:
<body> <div ex:role="exhibit-collection" ex:itemTypes=""></div> <table id="layout"> <tr valign="top"> <td ex:role="viewPanel" class="workplace"> <div ex:role="view" ex:viewClass="Thumbnail" ex:abbreviatedCount="40"> <div ex:role="lens" class="thumbnail-lens" style="display: none;"> <div class="item"> <div class="screenshot"><a ex:href-content=".url" target="_blank"><img ex:src-content="." /></a></div> <div class="label" ex:content=".label"></div> </div> </div> </div> </td> <td class="toolbar"> <div ex:role="facet" ex:expression="." ex:facetLabel="" ex:height="150px" ex:sortDirection="reverse"></div> <div ex:role="facet" ex:expression="..label" ex:facetLabel=" " ex:height="150px"></div> <div ex:role="facet" ex:expression="..label" ex:facetLabel=" " ex:height="150px"></div> <div ex:role="facet" ex:expression="..label" ex:facetLabel="" ex:height="150px"></div> </td> </tr> </table> </body>
We have created a table, the large left column of which contains a data view, and the right column contains a set of filters. It can be seen that all parameters related to exhibit are specified using attributes with the
ex
prefix.
Let's pay attention to several important points:
- In order for the filters to show names, not identifiers, we use expressions of the form
"..label"
; - The block with the attribute
ex:role="exhibit-collection"
indicates the default data type with which we work, it allows the framework to immediately display the correct type in the information bar at the top, showing how many records are selected. In general, it makes sense to write such a declaration if the database contains data of different types. - The view template definition is placed inside the view definition. This indicates that this template and this view must be used together. In general, there can be several representations and templates.
It remains to load the library and data. With the data is a clear question:
<link href="/portfolio.js" type="application/json" rel="exhibit/data" />
In this case, we assume that the file we generated is directly in the root directory.
The code is more complicated. The simplest thing you can do is download the scripts directly from the project server as follows:
<code>
<script src = "http://static.simile.mit.edu/exhibit/api-2.0/exhibit-api.js" type = "text / javascript"> </ script>
</ code>
In this way, everything is good, except for one problem: localization. That is, there is localization in the product, there is Spanish, or German, but there is no Russian there.
Therefore, if we want to quickly get a Russian-language interface, you need to download the library and download it from your server.
Local Library Version
Best guided by the document
Running Exhibit 2.0 yourself . When installing you need to consider two things.
Firstly, due to the large size of the libraries, when used in production, it makes sense to rebuild it into a bundle after making any changes to the code. To do this, the source file has an ant-file, respectively, need ant. For the prototype, this can not be done.
Secondly, and more significantly, by default, the project is configured to use a local jetty web server running on port 8888. As far as I understand, this is due to the fact that some features imply some kind of server-side processing. If this behavior is undesirable, it is necessary to replace the addresses starting with
127.0.0.1:8888
127.0.0.1:8888
to the corresponding path in the root directory of the server without specifying the host name. Such changes need to be made in the
exhibit/webapp/api/exhibit-api.js
.
Thus, in our case, after installing the libraries in / exhibit, the download code will look like this:
<script src="/exhibit/ajax/api/simile-ajax-api.js?bundle=false" type="text/javascript"></script> <script src="/exhibit/webapp/api/exhibit-api.js?bundle=false&locale=ru" type="text/javascript"></script>
Note the
bundle=false
parameter, which prohibits the use of assemblies, we could rebuild everything using ant and get rid of this parameter (and of course, significantly speed up the download).
The
locale
parameter sets the used locale, by default there is no Russian locale, but it is easy to make it yourself.
Russian localization
To do this, go to the
webapp/api/locales
and make a copy of the
en
directory with the name
ru
. Then we open the
locale.js
file in this directory and edit the path from
en
to
ru
. After that, you can edit everything else, the main thing to remember is that there are cases in Russian, so you need to at least change the word order in some places, and as a maximum - implement a JavaScript with cases in JavaScript.
In this sense, the
webapp/api/locales/ru/scripts/data/database-l10n.js
is of particular
webapp/api/locales/ru/scripts/data/database-l10n.js
, in particular the message generation line about the number of selected records, which we, without thinking twice, rewrote so:
span.innerHTML = label + ": <span class='" + countStyleClass + "'>" + count + "</span> "
Alternative representations
Since it is always interesting to see the dynamics of work over time, we implement an alternative representation in the form of a
Timeline representation. This view is available as a separate product and can be used either by itself or integrated into the Exhibition. Actually, it is better to see it once, than to talk about it for a long time, this product is worthy of a separate post.
So, add the view:
For the Timeline view, we use our own display template defined inside the view element.
<div ex:role="view" ex:viewClass="Timeline" ex:timelineHeight="760" ex:start="." ex:topBandUnit="month" ex:bottomBandUnit="year" > <div ex:role="lens" class="timeline-lens" style="display: none;"> <div class="item"> <div class="screenshot"><a ex:href-content=".url" target="_blank"><img ex:src-content="." /></a></div> <div class="label"> <p><b ex:content=".label"></b></p> <p>: <span ex:content="."></span></p> <p>: <span ex:content="if(exists(.),..label, ' ')"></span></p> <p>: <span ex:content="if(exists(.),..label, ' ')"></span></p> <p>: <span ex:content="if(exists(.),..label, ' ')"></span></p> </div> </div> </div> </div>
Now we can switch between two types of views.
Thus, having prepared the data, having created one HTML-page and having fiddled a bit with localization, we got
just such a picture (it can slow down, it can be optimized for production). High speed of development makes the system suitable for rapid prototyping and analysis of information, for example, when analyzing data obtained as a result of research. For web development, there are also a lot of applications: displaying statistics, various descriptions, products in online stores, etc.
And of course, we have considered the simplest use case, in more complex cases, you can extend the framework code, implement alternative representations, and so on.
Restrictions
Like any technology, Exhibit has its limitations.
First of all, the data must be transferred to the client and processed by the client. This imposes certain restrictions on the number of records that can be viewed this way. In terms of processing speed, I would say that it is unwise to use more than a thousand entries (although browsers are improving, not least in terms of the speed with which JavaScript is executed). From the point of view of transferring a large amount of JSON data, it is strongly recommended to use gzip compression by installing the appropriate module on the server, the result is noticeable with the naked eye.
The solution works in the most common browsers, however problems with Opera and IE6 are possible.