📜 ⬆️ ⬇️

Successful Portfolio 2: More Interactivity with SIMILE Exhibit

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:

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:
  1. Date of publication;
  2. Publication year (as a means of grouping);
  3. 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);
  4. Profile (client's area of ​​activity);
  5. 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:

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:

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:
  1. In order for the filters to show names, not identifiers, we use expressions of the form "..label" ;
  2. 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.
  3. 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.

Source: https://habr.com/ru/post/82156/


All Articles