├── .gitignore ├── config.ini ├── doc └── tablinker.html ├── img └── tablinker-logo-150dpi.png ├── input └── simple_marked.xls ├── output └── placeholder ├── readme.mdown └── src ├── bottle.py ├── tablinker.py ├── tl-service.py ├── untablinker.py └── views ├── css ├── bootstrap-theme.css ├── bootstrap-theme.min.css ├── bootstrap.css ├── bootstrap.min.css └── starter-template.css ├── fonts ├── glyphicons-halflings-regular.eot ├── glyphicons-halflings-regular.svg ├── glyphicons-halflings-regular.ttf └── glyphicons-halflings-regular.woff ├── img ├── favicon.ico └── tablinker-logo-150dpi.png ├── js ├── bootstrap.js └── bootstrap.min.js └── tl-service.tpl /.gitignore: -------------------------------------------------------------------------------- 1 | .project 2 | .pydevproject 3 | output/* 4 | -------------------------------------------------------------------------------- /config.ini: -------------------------------------------------------------------------------- 1 | [general] 2 | format = n3 ;N3 is supported by serializer -and- parser. Turtle is only supported by serializer 3 | 4 | 5 | [paths] 6 | ;File mask for annotated XLS files that will be converted to RDF (Turtle) 7 | ;Paths are relative to the file being executed (in the src dir) 8 | srcMask = ../input/*_marked.xls 9 | ;Target folder for RDF (Turtle) files 10 | targetFolder = ../output/ 11 | 12 | [debug] 13 | verbose = 0 14 | 15 | [dataCell] 16 | ;Configuration settings for the cell specified as 'Tablink Data' 17 | ;It the data cell may have mixed content (literals), leave this value empty 18 | literalType = http://www.w3.org/2001/XMLSchema#integer 19 | ;Name of data cell property. Leave empty to use default 'hasValue' 20 | propertyName = populationSize 21 | ;label for this property. Labels are ':::' delimited, where a label definition is 'lang-->label' 22 | ;leave this option empty if no labels should be added 23 | labels = en-->Population Size:::nl-->Populatie Grootte 24 | ;Set to 1 if blank data cells implicitly contain zeros 25 | implicitZeros = 0 26 | 27 | [annotations] 28 | enabled = 0 29 | ;Data model for annotations (oa=Open Annotation, np=Nanopublications) 30 | model = oa 31 | -------------------------------------------------------------------------------- /doc/tablinker.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Python: module tablinker 4 | 5 | 6 | 7 | 8 |
 
9 |  
tablinker
index
/home/lrd900/code/tablinker/src/tablinker.py
12 |

Created on 19 Sep 2011
13 | Modified on 31 Jan 2012
14 |  
15 | Authors:    Rinke Hoekstra, Laurens Rietveld
16 | Copyright:  VU University Amsterdam, 2011/2012
17 | License:    LGPLv3

18 |

19 | 20 | 21 | 23 | 24 | 25 |
 
22 | Modules
       
glob
26 | logging
27 |
os
28 | re
29 |
sys
30 | urllib
31 |

32 | 33 | 34 | 36 | 37 | 38 |
 
35 | Classes
       
39 |
__builtin__.object 40 |
41 |
42 |
TabLinker 43 |
44 |
45 |
46 |

47 | 48 | 49 | 51 | 52 | 53 |
 
50 | class TabLinker(__builtin__.object)
    Methods defined here:
54 |
__init__(self, filename, config, level=10)
TabLinker constructor
55 |  
56 | Keyword arguments:
57 | filename -- String containing the name of the current Excel file being examined
58 | config -- Configuration object, loaded from .ini file
59 | level -- A logging level as defined in the logging module
60 | 61 |
addDataCellProperty(self)
Add definition of data cell resource to graph
62 | 63 |
addValue(self, source_cell_value, altLabel=None)
Add a "value" + optional label to the graph for a cell in the source Excel sheet. The value is typically the value stored in the source cell itself, but may also be a copy of another cell (e.g. in the case of 'idem.').
64 |  
65 | Arguments:
66 | source_cell_value -- The string representing the value of the source cell
67 |  
68 | Returns:
69 | source_cell_value_qname -- a valid QName for the value of the source cell
70 | 71 |
doLink(self)
Start tablinker for all sheets in workbook
72 | 73 |
getQName(self, names)
Create a valid QName from a string or dictionary of names
74 |  
75 | Arguments:
76 | names -- Either dictionary of names or string of a name.
77 |  
78 | Returns:
79 | qname -- a valid QName for the dictionary or string
80 | 81 |
getType(self, style)
Get type for a given excel style. Style name must be prefixed by 'TL '
82 |  
83 | Arguments:
84 | style -- Style (string) to check type for
85 |  
86 | Returns:
87 | String -- The type of this field. In case none is found, 'unknown'
88 | 89 |
getValidRowsCols(self)
Determine the number of non-empty rows and columns in the Excel sheet
90 |  
91 | Returns:
92 | rowns -- number of rows
93 | colns -- number of columns
94 | 95 |
initGraph(self)
Initialize the graph, set default namespaces, and add schema information
96 | 97 |
isEmpty(self, i, j)
Check whether cell is empty.
98 |  
99 | Arguments:
100 | i -- row
101 | j -- column
102 |  
103 | Returns:
104 | True/False -- depending on whether the cell is empty
105 | 106 |
isEmptyColumn(self, j, rowns)
Determine whether the column 'j' is empty by iterating over all its cells
107 |  
108 | Arguments:
109 | j     -- The index of the column to be checked.
110 | rowns -- The number of rows to be checked
111 |  
112 | Returns:
113 | true  -- if the column is empty
114 | false -- if the column is not empty
115 | 116 |
isEmptyRow(self, i, colns)
Determine whether the row 'i' is empty by iterating over all its cells
117 |  
118 | Arguments:
119 | i     -- The index of the row to be checked.
120 | colns -- The number of columns to be checked
121 |  
122 | Returns:
123 | true  -- if the row is empty
124 | false -- if the row is not empty
125 | 126 |
parseColHeader(self, i, j)
Create relevant triples for the cell marked as Header (i, j are row and column)
127 | 128 |
parseData(self, i, j)
Create relevant triples for the cell marked as Data (i, j are row and column)
129 | 130 |
parseHierarchicalRowHeader(self, i, j)
Create relevant triples for the cell marked as HierarchicalRowHeader (i, j are row and column)
131 | 132 |
parseRowHeader(self, i, j)
Create relevant triples for the cell marked as RowHeader (i, j are row and column)
133 | 134 |
parseRowLabel(self, i, j)
Create relevant triples for the cell marked as Label (i, j are row and column)
135 | 136 |
parseRowProperty(self, i, j)
Create relevant triples for the cell marked as Property (i, j are row and column)
137 | 138 |
parseSheet(self)
Parses the currently selected sheet in the workbook, takes no arguments. Iterates over all cells in the Excel sheet and produces relevant RDF Triples.
139 | 140 |
parseTitle(self, i, j)
Create relevant triples for the cell marked as Title (i, j are row and column)
141 | 142 |
processString(self, string)
Remove illegal characters (comma, brackets, etc) from string, and replace it with underscore. Useful for URIs
143 |  
144 | Arguments:
145 | string -- The string representing the value of the source cell
146 |  
147 | Returns:
148 | processedString -- The processed string
149 | 150 |
setScope(self, fileBasename)
Set the default namespace and base for all URIs of the current workbook
151 | 152 |
updateRowHierarchy(self, i, j)
Build up lists for hierarchical row headers. Cells marked as hierarchical row header are often empty meaning that their intended value is stored somewhere else in the Excel sheet.
153 |  
154 | Keyword arguments:
155 | int i -- row number
156 | int j -- col number
157 |  
158 | Returns:
159 | New row hierarchy dictionary
160 | 161 |
162 | Data descriptors defined here:
163 |
__dict__
164 |
dictionary for instance variables (if defined)
165 |
166 |
__weakref__
167 |
list of weak references to the object (if defined)
168 |
169 |
170 | Data and other attributes defined here:
171 |
defaultNamespacePrefix = 'http://www.data2semantics.org/data/'
172 | 173 |
namespaces = {'d2s': Namespace('http://www.data2semantics.org/core/'), 'dcterms': Namespace('http://purl.org/dc/terms/'), 'owl': Namespace('http://www.w3.org/2002/07/owl#'), 'qb': Namespace('http://purl.org/linked-data/cube#'), 'skos': Namespace('http://www.w3.org/2004/02/skos/core#')}
174 | 175 |

176 | 177 | 178 | 180 | 181 | 182 |
 
179 | Data
       RDF = rdf.namespace.ClosedNamespace('http://www.w3.org/1999/02/22-rdf-syntax-ns#')
183 | RDFS = rdf.namespace.ClosedNamespace('http://www.w3.org/2000/01/rdf-schema#')
184 | XL_CELL_BLANK = 6
185 | XL_CELL_EMPTY = 0
186 | -------------------------------------------------------------------------------- /img/tablinker-logo-150dpi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Data2Semantics/TabLinker/888c5d9fac979e456b5474f0fdae05f9f97e26a8/img/tablinker-logo-150dpi.png -------------------------------------------------------------------------------- /input/simple_marked.xls: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Data2Semantics/TabLinker/888c5d9fac979e456b5474f0fdae05f9f97e26a8/input/simple_marked.xls -------------------------------------------------------------------------------- /output/placeholder: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Data2Semantics/TabLinker/888c5d9fac979e456b5474f0fdae05f9f97e26a8/output/placeholder -------------------------------------------------------------------------------- /readme.mdown: -------------------------------------------------------------------------------- 1 | 2 | 3 | # TabLinker v0.2 4 | **Authors**: [Rinke Hoekstra](http://github.com/RinkeHoekstra), [Laurens Rietveld](http://github.com/LaurensRietveld), [Albert Meroño-Peñuela](http://github.com/albertmeronyo) 5 | 6 | **Copyright**: VU University Amsterdam 7 | 8 | **License**: [LGPL v3.0](http://www.gnu.org/licenses/lgpl.html) 9 | 10 | ## About TabLinker 11 | 12 | TabLinker is experimental software for converting manually annotated Microsoft Excel workbooks to the [RDF Data Cube vocabulary](http://publishing-statistical-data.googlecode.com/svn/trunk/specs/src/main/html/cube.html). It is used in the context of the [Data2Semantics](http://www.data2semantics.org) project to investigate the use of Linked Data for humanities research ([Dutch census data](http://www.volkstellingen.nl) produced by [DANS](http://dans.knaw.nl)). 13 | 14 | TabLinker was designed for converting Excel or CSV files to RDF (triplification, RDF-izing) that have a complex layout and cannot be handled by fully automatic csv2rdf scripts. 15 | 16 | A presentation about Linked Census Data, including TabLinker is [available from SlideShare](http://www.slideshare.net/rinkehoekstra/linked-census-data). 17 | 18 | ## Using TabLinker 19 | 20 | TabLinker takes annotated Excel files (found using the `srcMask` option in the [config.ini](http://github.com/Data2Semantics/TabLinker/tree/master/config.ini) file) and converts them to RDF. This RDF is serialized to the target folder specified using the `targetFolder` option in [config.ini](http://github.com/Data2Semantics/TabLinker/tree/master/config.ini). 21 | 22 | Annotations in the Excel file should be done using the built-in **style** functionality of Excel (you can specify these by hand). TabLinker currently recognises seven styles: 23 | 24 | * **TL Title** - The cell containing the title of a sheet 25 | * **TL Data** - A cell that contains data, e.g. a number for the population size 26 | * **TL ColHeader** - Used for the headers of columns 27 | * **TL RowHeader** - Used for row headers 28 | * **TL HRowHeader** - Hierarchical header, used for multi-column row headers with subsumption/taxonomic relations between the values of the columns 29 | * **TL RowProperty** - Typically used for the header cells directly above RowHeader or HierarchicalRowHeader cells. Cell values are the properties that relate Data cells to RowHeader and HierarchicalRowHeader cells. 30 | * **TL RowLabel** - Used for cells that contain a label for one of the HierarchicalRowHeader cells in the same row. 31 | 32 | An eight style, **TL Metadata**, is currently ignored (See #3). 33 | 34 | An [example of such an annotated Excel file](http://github.com/Data2Semantics/TabLinker/tree/master/input/BRT_1889_02_T1_marked.xls) is provided in the [input](http://github.com/Data2Semantics/TabLinker/tree/master/input/) directory. There are ways to import the styles defined in that file into your own Excel files. 35 | 36 | **Tip:** If your table contains totals for HierarchicalRowHeader cell values, use a non-TabLink style to mark the cells between the level to which the total belongs, and the cell that contains the name of the total. Have a look at the example [annotated Excel file](http://github.com/Data2Semantics/TabLinker/tree/master/input/BRT_1889_02_T1_marked.xls) to see how this is done (up to row 428). 37 | 38 | Once you're all set, start the TabLinker by cd-ing to the [src](http://github.com/Data2Semantics/TabLinker/tree/master/src/) folder, and running: 39 | 40 | ```python tablinker.py``` 41 | 42 | ## Configuration file 43 | 44 | Editing the config.ini file can help you to get a more customized RDF output. Options are: 45 | 46 | ### general 47 | * **format** is the desired RDF serialization format ('n3' by default). 48 | 49 | ### paths 50 | * **srcMask** is a mask matching the input XLS files. It works as a directory specifier, as well as wildcard specifier to filter only desired files. 51 | * **targetFolder** is the target directory where you want the RDF files to be written to. 52 | 53 | ### debug 54 | * **verbose** with value 1 (default) enables verbose mode (0 disables it). 55 | 56 | ### dataCell 57 | * **literalType** is a URI specifying the datatype for the data cells in the spreadsheet (e.g. [http://www.w3.org/2001/XMLSchema#int]) 58 | * **propertyName** is the name of data cell property. Leave blank to use default 'hasValue'. 59 | * **labels** are language literals to be added for that property name (if needed). 60 | 61 | ### annotations 62 | * **enabled** with value 1 (default) generates an additional graph with the annotations (Excel comments) contained in the XLS files (if any) (0 disables it). 63 | * **model** specifies the data model ('oa' by default) for generating the annotations graph. 64 | 65 | ## Requirements 66 | 67 | TabLinker was developed under the following environment: 68 | 69 | * Python 2.7 70 | * The xlutils and xlrd packages from 71 | * RDFLib 4.0.1, from 72 | 73 | ## API Documentation 74 | 75 | [API documentation](http://github.com/Data2Semantics/TabLinker/tree/master/doc/tablinker.html) (generated using `pydoc`) is available in the [doc](http://github.com/Data2Semantics/TabLinker/tree/master/doc/) folder. 76 | -------------------------------------------------------------------------------- /src/tablinker.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | """ 4 | Created on 19 Sep 2011 5 | Modified on 22 Feb 2014 6 | 7 | Authors: Rinke Hoekstra, Laurens Rietveld, Albert Meronyo-Penyuela 8 | Copyright: VU University Amsterdam, 2011, 2012, 2013, 2014 9 | License: LGPLv3 10 | 11 | """ 12 | from xlutils.margins import number_of_good_cols, number_of_good_rows 13 | from xlutils.copy import copy 14 | from xlutils.styles import Styles 15 | from xlrd import open_workbook, XL_CELL_EMPTY, XL_CELL_BLANK, cellname, colname 16 | import glob 17 | from rdflib import ConjunctiveGraph, Namespace, Literal, RDF, RDFS, XSD, BNode, URIRef 18 | import re 19 | from ConfigParser import SafeConfigParser 20 | import urllib 21 | from urlparse import urlparse 22 | import logging 23 | import os 24 | import time 25 | import datetime 26 | import isodate 27 | #set default encoding to latin-1, to avoid encode/decode errors for special chars 28 | #(laurens: actually don't know why encoding/decoding is not sufficient) 29 | #(rinke: this is a specific requirment for the xlrd and xlutils packages) 30 | 31 | import sys 32 | reload(sys) 33 | import traceback 34 | sys.setdefaultencoding("utf8") #@UndefinedVariable 35 | 36 | 37 | class TabLinker(object): 38 | defaultNamespacePrefix = 'http://lod.cedar-project.nl/resource/' 39 | annotationsNamespacePrefix = 'http://lod.cedar-project.nl/annotations/' 40 | namespaces = { 41 | 'dcterms':Namespace('http://purl.org/dc/terms/'), 42 | 'skos':Namespace('http://www.w3.org/2004/02/skos/core#'), 43 | 'd2s':Namespace('http://lod.cedar-project.nl/core/'), 44 | 'qb':Namespace('http://purl.org/linked-data/cube#'), 45 | 'owl':Namespace('http://www.w3.org/2002/07/owl#') 46 | } 47 | annotationNamespaces = { 48 | 'np':Namespace('http://www.nanopub.org/nschema#'), 49 | 'oa':Namespace('http://www.w3.org/ns/openannotation/core/'), 50 | 'xsd':Namespace('http://www.w3.org/2001/XMLSchema#'), 51 | 'dct':Namespace('http://purl.org/dc/terms/') 52 | } 53 | 54 | def __init__(self, filename, config, level = logging.DEBUG): 55 | """TabLinker constructor 56 | 57 | Keyword arguments: 58 | filename -- String containing the name of the current Excel file being examined 59 | config -- Configuration object, loaded from .ini file 60 | level -- A logging level as defined in the logging module 61 | """ 62 | self.config = config 63 | self.filename = filename 64 | 65 | self.log = logging.getLogger("TabLinker") 66 | self.log.setLevel(level) 67 | 68 | self.log.debug('Initializing Graphs') 69 | self.initGraphs() 70 | 71 | self.log.debug('Setting Scope') 72 | basename = os.path.basename(filename) 73 | basename = re.search('(.*)\.xls',basename).group(1) 74 | self.setScope(basename) 75 | 76 | self.log.debug('Loading Excel file {0}.'.format(filename)) 77 | self.rb = open_workbook(filename, formatting_info=True) 78 | 79 | self.log.debug('Reading styles') 80 | self.styles = Styles(self.rb) 81 | 82 | self.log.debug('Copied Workbook to writable copy') 83 | self.wb = copy(self.rb) 84 | 85 | 86 | def initGraphs(self): 87 | """Initialize the graphs, set default namespaces, and add schema information""" 88 | 89 | self.graph = ConjunctiveGraph() 90 | # Create a separate graph for annotations 91 | self.annotationGraph = ConjunctiveGraph() 92 | 93 | self.log.debug('Adding namespaces to graphs') 94 | # Bind namespaces to graphs 95 | for namespace in self.namespaces: 96 | self.graph.namespace_manager.bind(namespace, self.namespaces[namespace]) 97 | 98 | # Same for annotation graph 99 | for namespace in self.annotationNamespaces: 100 | self.annotationGraph.namespace_manager.bind(namespace, self.annotationNamespaces[namespace]) 101 | 102 | self.log.debug('Adding some schema information (dimension and measure properties) ') 103 | self.addDataCellProperty() 104 | 105 | self.graph.add((self.namespaces['d2s']['dimension'], RDF.type, self.namespaces['qb']['DimensionProperty'])) 106 | 107 | self.graph.add((self.namespaces['d2s']['label'], RDF.type, RDF['Property'])) 108 | 109 | def addDataCellProperty(self): 110 | """Add definition of data cell resource to graph""" 111 | 112 | if len(self.config.get('dataCell', 'propertyName')) > 0 : 113 | self.dataCellPropertyName = self.config.get('dataCell', 'propertyName') 114 | else : 115 | self.dataCellPropertyName = 'hasValue' 116 | 117 | self.graph.add((self.namespaces['d2s'][self.dataCellPropertyName], RDF.type, self.namespaces['qb']['MeasureProperty'])) 118 | 119 | #Take labels from config 120 | if len(self.config.get('dataCell', 'labels')) > 0 : 121 | labels = self.config.get('dataCell', 'labels').split(':::') 122 | for label in labels : 123 | labelProperties = label.split('-->') 124 | if len(labelProperties[0]) > 0 and len(labelProperties[1]) > 0 : 125 | self.graph.add((self.namespaces['d2s'][self.dataCellPropertyName], RDFS.label, Literal(labelProperties[1],labelProperties[0]))) 126 | 127 | if len(self.config.get('dataCell', 'literalType')) > 0 : 128 | self.graph.add((self.namespaces['d2s'][self.dataCellPropertyName], RDFS.range, URIRef(self.config.get('dataCell', 'literalType')))) 129 | 130 | def setScope(self, fileBasename): 131 | """Set the default namespace and base for all URIs of the current workbook""" 132 | self.fileBasename = fileBasename 133 | scopeNamespace = self.defaultNamespacePrefix + fileBasename + '/' 134 | 135 | # Annotations go to a different namespace 136 | annotationScopeNamespace = self.annotationsNamespacePrefix + fileBasename + '/' 137 | 138 | self.log.debug('Adding namespace for {0}: {1}'.format(fileBasename, scopeNamespace)) 139 | 140 | self.namespaces['scope'] = Namespace(scopeNamespace) 141 | self.annotationNamespaces['scope'] = Namespace(annotationScopeNamespace) 142 | self.graph.namespace_manager.bind('', self.namespaces['scope']) 143 | self.annotationGraph.namespace_manager.bind('', self.annotationNamespaces['scope']) 144 | 145 | def doLink(self): 146 | """Start tablinker for all sheets in workbook""" 147 | self.log.info('Starting TabLinker for all sheets in workbook') 148 | 149 | for n in range(self.rb.nsheets) : 150 | self.log.debug('Starting with sheet {0}'.format(n)) 151 | self.r_sheet = self.rb.sheet_by_index(n) 152 | self.w_sheet = self.wb.get_sheet(n) 153 | 154 | self.rowns, self.colns = self.getValidRowsCols() 155 | 156 | self.sheet_qname = urllib.quote(re.sub('\s','_',self.r_sheet.name)) 157 | self.log.debug('Base for QName generator set to: {0}'.format(self.sheet_qname)) 158 | 159 | self.log.debug('Starting parser') 160 | self.parseSheet() 161 | 162 | ### 163 | # Utility Functions 164 | ### 165 | 166 | def insideMergeBox(self, i, j): 167 | """ 168 | Check if the specified cell is inside a merge box 169 | 170 | Arguments: 171 | i -- row 172 | j -- column 173 | 174 | Returns: 175 | True/False -- depending on whether the cell is inside a merge box 176 | """ 177 | self.merged_cells = self.r_sheet.merged_cells 178 | for crange in self.merged_cells: 179 | rlo, rhi, clo, chi = crange 180 | if i <= rhi - 1 and i >= rlo and j <= chi - 1 and j >= clo: 181 | return True 182 | return False 183 | 184 | 185 | def getMergeBoxCoord(self, i, j): 186 | """ 187 | Get the top-left corner cell of the merge box containing the specified cell 188 | 189 | Arguments: 190 | i -- row 191 | j -- column 192 | 193 | Returns: 194 | (k, l) -- Coordinates of the top-left corner of the merge box 195 | """ 196 | if not self.insideMergeBox(i,j): 197 | return (-1, -1) 198 | 199 | self.merged_cells = self.r_sheet.merged_cells 200 | for crange in self.merged_cells: 201 | rlo, rhi, clo, chi = crange 202 | if i <= rhi - 1 and i >= rlo and j <= chi - 1 and j >= clo: 203 | return (rlo, clo) 204 | 205 | def getType(self, style): 206 | """Get type for a given excel style. Style name must be prefixed by 'TL ' 207 | 208 | Arguments: 209 | style -- Style (string) to check type for 210 | 211 | Returns: 212 | String -- The type of this field. In case none is found, 'unknown' 213 | """ 214 | typematch = re.search('TL\s(.*)',style) 215 | if typematch : 216 | cellType = typematch.group(1) 217 | else : 218 | cellType = 'Unknown' 219 | return cellType 220 | 221 | def isEmpty(self, i,j): 222 | """Check whether cell is empty. 223 | 224 | Arguments: 225 | i -- row 226 | j -- column 227 | 228 | Returns: 229 | True/False -- depending on whether the cell is empty 230 | """ 231 | if (self.r_sheet.cell(i,j).ctype == XL_CELL_EMPTY or self.r_sheet.cell(i,j).ctype == XL_CELL_BLANK) or self.r_sheet.cell(i,j).value == '' : 232 | return True 233 | else : 234 | return False 235 | 236 | def isEmptyRow(self, i, colns): 237 | """ 238 | Determine whether the row 'i' is empty by iterating over all its cells 239 | 240 | Arguments: 241 | i -- The index of the row to be checked. 242 | colns -- The number of columns to be checked 243 | 244 | Returns: 245 | true -- if the row is empty 246 | false -- if the row is not empty 247 | """ 248 | for j in range(0,colns) : 249 | if not self.isEmpty(i,j): 250 | return False 251 | return True 252 | 253 | def isEmptyColumn(self, j, rowns ): 254 | """ 255 | Determine whether the column 'j' is empty by iterating over all its cells 256 | 257 | Arguments: 258 | j -- The index of the column to be checked. 259 | rowns -- The number of rows to be checked 260 | 261 | Returns: 262 | true -- if the column is empty 263 | false -- if the column is not empty 264 | """ 265 | for i in range(0,rowns) : 266 | if not self.isEmpty(i,j): 267 | return False 268 | return True 269 | 270 | def getValidRowsCols(self) : 271 | """ 272 | Determine the number of non-empty rows and columns in the Excel sheet 273 | 274 | Returns: 275 | rowns -- number of rows 276 | colns -- number of columns 277 | """ 278 | colns = number_of_good_cols(self.r_sheet) 279 | rowns = number_of_good_rows(self.r_sheet) 280 | 281 | # Check whether the number of good columns and rows are correct 282 | while self.isEmptyRow(rowns-1, colns) : 283 | rowns = rowns - 1 284 | while self.isEmptyColumn(colns-1, rowns) : 285 | colns = colns - 1 286 | 287 | self.log.debug('Number of rows with content: {0}'.format(rowns)) 288 | self.log.debug('Number of columns with content: {0}'.format(colns)) 289 | return rowns, colns 290 | 291 | def getQName(self, names): 292 | """ 293 | Create a valid QName from a string or dictionary of names 294 | 295 | Arguments: 296 | names -- Either dictionary of names or string of a name. 297 | 298 | Returns: 299 | qname -- a valid QName for the dictionary or string 300 | """ 301 | 302 | if type(names) == dict : 303 | qname = self.sheet_qname 304 | for k in names : 305 | qname = qname + '/' + self.processString(names[k]) 306 | else : 307 | qname = self.sheet_qname + '/' + self.processString(names) 308 | 309 | self.log.debug('Minted new QName: {}'.format(qname)) 310 | return qname 311 | 312 | 313 | def processString(self, string): 314 | """ 315 | Remove illegal characters (comma, brackets, etc) from string, and replace it with underscore. Useful for URIs 316 | 317 | Arguments: 318 | string -- The string representing the value of the source cell 319 | 320 | Returns: 321 | processedString -- The processed string 322 | """ 323 | 324 | return urllib.quote(re.sub('\s|\(|\)|,|\.','_',unicode(string).strip()).encode('utf-8', 'ignore')) 325 | 326 | 327 | def addValue(self, source_cell_value, altLabel=None): 328 | """ 329 | Add a "value" + optional label to the graph for a cell in the source Excel sheet. The value is typically the value stored in the source cell itself, but may also be a copy of another cell (e.g. in the case of 'idem.'). 330 | 331 | Arguments: 332 | source_cell_value -- The string representing the value of the source cell 333 | 334 | Returns: 335 | source_cell_value_qname -- a valid QName for the value of the source cell 336 | """ 337 | source_cell_value_qname = self.getQName(source_cell_value) 338 | self.graph.add((self.namespaces['scope'][source_cell_value_qname],self.namespaces['qb']['dataSet'],self.namespaces['scope'][self.sheet_qname])) 339 | 340 | self.graph.add((self.namespaces['scope'][self.source_cell_qname],self.namespaces['d2s']['value'],self.namespaces['scope'][source_cell_value_qname])) 341 | 342 | # If the source_cell_value is actually a dictionary (e.g. in the case of HierarchicalRowHeaders), then use the last element of the row hierarchy as prefLabel 343 | # Otherwise just use the source_cell_value as prefLabel 344 | if type(source_cell_value) == dict : 345 | self.graph.add((self.namespaces['scope'][source_cell_value_qname],self.namespaces['skos'].prefLabel,Literal(source_cell_value.values()[-1],'nl'))) 346 | 347 | if altLabel and altLabel != source_cell_value.values()[-1]: 348 | # If altLabel has a value (typically for HierarchicalRowHeaders) different from the last element in the row hierarchy, we add it as alternative label. 349 | self.graph.add((self.namespaces['scope'][source_cell_value_qname],self.namespaces['skos'].altLabel,Literal(altLabel,'nl'))) 350 | else : 351 | # Try to parse a date to add the appropriate datatype to the literal 352 | try: 353 | isodate.parse_datetime(source_cell_value) 354 | self.log.debug("Datetime on this cell: %s" % source_cell_value) 355 | self.graph.add((self.namespaces['scope'][source_cell_value_qname],self.namespaces['skos'].prefLabel,Literal(source_cell_value,datatype=XSD.datetime))) 356 | except (ValueError, isodate.isoerror.ISO8601Error, AttributeError): 357 | self.log.debug("No datetime on this cell") 358 | self.graph.add((self.namespaces['scope'][source_cell_value_qname],self.namespaces['skos'].prefLabel,Literal(source_cell_value,'nl'))) 359 | 360 | if altLabel and altLabel != source_cell_value: 361 | # If altLabel has a value (typically for HierarchicalRowHeaders) different from the source_cell_value, we add it as alternative label. 362 | self.graph.add((self.namespaces['scope'][source_cell_value_qname],self.namespaces['skos'].altLabel,Literal(altLabel,'nl'))) 363 | 364 | return source_cell_value_qname 365 | 366 | 367 | 368 | 369 | 370 | 371 | def parseSheet(self): 372 | """ 373 | Parses the currently selected sheet in the workbook, takes no arguments. Iterates over all cells in the Excel sheet and produces relevant RDF Triples. 374 | """ 375 | self.log.info("Parsing {0} rows and {1} columns.".format(self.rowns,self.colns)) 376 | 377 | self.column_dimensions = {} 378 | self.property_dimensions = {} 379 | self.row_dimensions = {} 380 | self.rowhierarchy = {} 381 | 382 | # Get dictionary of annotations 383 | self.annotations = self.r_sheet.cell_note_map 384 | 385 | for i in range(0,self.rowns): 386 | self.rowhierarchy[i] = {} 387 | 388 | for j in range(0, self.colns): 389 | # Parse cell data 390 | self.source_cell = self.r_sheet.cell(i,j) 391 | self.source_cell_name = cellname(i,j) 392 | self.style = self.styles[self.source_cell].name 393 | self.cellType = self.getType(self.style) 394 | self.source_cell_qname = self.getQName(self.source_cell_name) 395 | 396 | self.log.debug("({},{}) {}/{}: \"{}\"". format(i,j,self.cellType, self.source_cell_name, self.source_cell.value)) 397 | 398 | # Try to parse ints to avoid ugly _0 URIs 399 | try: 400 | if int(self.source_cell.value) == self.source_cell.value: 401 | self.source_cell.value = int(self.source_cell.value) 402 | except ValueError: 403 | self.log.debug("(%s.%s) No parseable int" % (i,j)) 404 | 405 | # Parse annotation (if any) 406 | if self.config.get('annotations', 'enabled') == "1": 407 | if (i,j) in self.annotations: 408 | self.parseAnnotation(i, j) 409 | 410 | # Parse even if empty 411 | if (self.cellType == 'HRowHeader') : 412 | self.updateRowHierarchy(i, j) 413 | if self.cellType == 'Data': 414 | self.parseData(i, j) 415 | if self.cellType == 'ColHeader' : 416 | self.parseColHeader(i, j) 417 | if self.cellType == 'RowProperty' : 418 | self.parseRowProperty(i, j) 419 | 420 | if not self.isEmpty(i,j) : 421 | self.graph.add((self.namespaces['scope'][self.source_cell_qname],RDF.type,self.namespaces['d2s'][self.cellType])) 422 | self.graph.add((self.namespaces['scope'][self.source_cell_qname],self.namespaces['d2s']['cell'],Literal(self.source_cell_name))) 423 | #self.graph.add((self.namespaces['scope'][self.source_cell_qname],self.namespaces['d2s']['col'],Literal(colname(j)))) 424 | #self.graph.add((self.namespaces['scope'][self.source_cell_qname],self.namespaces['d2s']['row'],Literal(i+1))) 425 | #self.graph.add((self.namespaces['scope'][self.source_cell_qname] isrow row 426 | if self.cellType == 'Title' : 427 | self.parseTitle(i, j) 428 | 429 | elif self.cellType == 'RowHeader' : 430 | self.parseRowHeader(i, j) 431 | 432 | elif self.cellType == 'HRowHeader' : 433 | self.parseHierarchicalRowHeader(i, j) 434 | 435 | elif self.cellType == 'RowLabel' : 436 | self.parseRowLabel(i, j) 437 | 438 | self.log.info("Done parsing...") 439 | 440 | def updateRowHierarchy(self, i, j) : 441 | """ 442 | Build up lists for hierarchical row headers. Cells marked as hierarchical row header are often empty meaning that their intended value is stored somewhere else in the Excel sheet. 443 | 444 | Keyword arguments: 445 | int i -- row number 446 | int j -- col number 447 | 448 | Returns: 449 | New row hierarchy dictionary 450 | """ 451 | if (self.isEmpty(i,j) or str(self.source_cell.value).lower().strip() == 'id.') : 452 | # If the cell is empty, and a HierarchicalRowHeader, add the value of the row header above it. 453 | # If the cell above is not in the rowhierarchy, don't do anything. 454 | # If the cell is exactly 'id.', add the value of the row header above it. 455 | try : 456 | self.rowhierarchy[i][j] = self.rowhierarchy[i-1][j] 457 | self.log.debug("({},{}) Copied from above\nRow hierarchy: {}".format(i,j,self.rowhierarchy[i])) 458 | except : 459 | # REMOVED because of double slashes in uris 460 | # self.rowhierarchy[i][j] = self.source_cell.value 461 | self.log.debug("({},{}) Top row, added nothing\nRow hierarchy: {}".format(i,j,self.rowhierarchy[i])) 462 | elif str(self.source_cell.value).lower().startswith('id.') or str(self.source_cell.value).lower().startswith('id '): 463 | # If the cell starts with 'id.', add the value of the row above it, and append the rest of the cell's value. 464 | suffix = self.source_cell.value[3:] 465 | try : 466 | self.rowhierarchy[i][j] = self.rowhierarchy[i-1][j]+suffix 467 | self.log.debug("({},{}) Copied from above+suffix\nRow hierarchy {}".format(i,j,self.rowhierarchy[i])) 468 | except : 469 | self.rowhierarchy[i][j] = self.source_cell.value 470 | self.log.debug("({},{}) Top row, added value\nRow hierarchy {}".format(i,j,self.rowhierarchy[i])) 471 | elif not self.isEmpty(i,j) : 472 | self.rowhierarchy[i][j] = self.source_cell.value 473 | self.log.debug("({},{}) Added value\nRow hierarchy {}".format(i,j,self.rowhierarchy[i])) 474 | return self.rowhierarchy 475 | 476 | def parseHierarchicalRowHeader(self, i, j) : 477 | """ 478 | Create relevant triples for the cell marked as HierarchicalRowHeader (i, j are row and column) 479 | """ 480 | 481 | # Use the rowhierarchy to create a unique qname for the cell's contents, give the source_cell's original value as extra argument 482 | self.log.debug("Parsing HierarchicalRowHeader") 483 | 484 | self.source_cell_value_qname = self.addValue(self.rowhierarchy[i], altLabel=self.source_cell.value) 485 | 486 | 487 | # Now that we know the source cell's value qname, add a d2s:isDimension link and the skos:Concept type 488 | self.graph.add((self.namespaces['scope'][self.source_cell_qname], self.namespaces['d2s']['isDimension'], self.namespaces['scope'][self.source_cell_value_qname])) 489 | self.graph.add((self.namespaces['scope'][self.source_cell_qname], RDF.type, self.namespaces['skos'].Concept)) 490 | 491 | hierarchy_items = self.rowhierarchy[i].items() 492 | try: 493 | parent_values = dict(hierarchy_items[:-1]) 494 | self.log.debug(i,j, "Parent value: " + str(parent_values)) 495 | parent_value_qname = self.getQName(parent_values) 496 | self.graph.add((self.namespaces['scope'][self.source_cell_value_qname], self.namespaces['skos']['broader'], self.namespaces['scope'][parent_value_qname])) 497 | except : 498 | self.log.debug(i,j, "Top of hierarchy") 499 | 500 | # Get the properties to use for the row headers 501 | try : 502 | properties = [] 503 | for dim_qname in self.property_dimensions[j] : 504 | properties.append(dim_qname) 505 | except KeyError : 506 | self.log.debug("({}.{}) No row dimension for cell".format(i,j)) 507 | 508 | self.row_dimensions.setdefault(i, []).append((self.source_cell_value_qname, properties)) 509 | 510 | def parseRowLabel(self, i, j): 511 | """ 512 | Create relevant triples for the cell marked as Label (i, j are row and column) 513 | """ 514 | 515 | self.log.debug("Parsing Row Label") 516 | 517 | # Get the QName of the HierarchicalRowHeader cell that this label belongs to, based on the rowhierarchy for this row (i) 518 | hierarchicalRowHeader_value_qname = self.getQName(self.rowhierarchy[i]) 519 | 520 | prefLabels = self.graph.objects(self.namespaces['scope'][hierarchicalRowHeader_value_qname], self.namespaces['skos'].prefLabel) 521 | for label in prefLabels : 522 | # If the hierarchicalRowHeader QName already has a preferred label, turn it into a skos:altLabel 523 | self.graph.remove((self.namespaces['scope'][hierarchicalRowHeader_value_qname],self.namespaces['skos'].prefLabel,label)) 524 | self.graph.add((self.namespaces['scope'][hierarchicalRowHeader_value_qname],self.namespaces['skos'].altLabel,label)) 525 | self.log.debug("Turned skos:prefLabel {} for {} into a skos:altLabel".format(label, hierarchicalRowHeader_value_qname)) 526 | 527 | # Add the value of the label cell as skos:prefLabel to the header cell 528 | self.graph.add((self.namespaces['scope'][hierarchicalRowHeader_value_qname], self.namespaces['skos'].prefLabel, Literal(self.source_cell.value, 'nl'))) 529 | 530 | # Record that this source_cell_qname is the label for the HierarchicalRowHeader cell 531 | self.graph.add((self.namespaces['scope'][self.source_cell_qname], self.namespaces['d2s']['isLabel'], self.namespaces['scope'][hierarchicalRowHeader_value_qname])) 532 | 533 | def parseRowHeader(self, i, j) : 534 | """ 535 | Create relevant triples for the cell marked as RowHeader (i, j are row and column) 536 | """ 537 | rowHeaderValue = "" 538 | 539 | # Don't attach the cell value to the namespace if it's already a URI 540 | isURI = urlparse(str(self.source_cell.value)) 541 | if isURI.scheme and isURI.netloc: 542 | rowHeaderValue = URIRef(self.source_cell.value) 543 | else: 544 | self.source_cell_value_qname = self.addValue(self.source_cell.value) 545 | rowHeaderValue = self.namespaces['scope'][self.source_cell_value_qname] 546 | 547 | self.graph.add((self.namespaces['scope'][self.source_cell_qname], 548 | self.namespaces['d2s']['isDimension'], 549 | rowHeaderValue)) 550 | self.graph.add((rowHeaderValue, 551 | RDF.type, 552 | self.namespaces['d2s']['Dimension'])) 553 | self.graph.add((rowHeaderValue, 554 | RDF.type, 555 | self.namespaces['skos'].Concept)) 556 | 557 | # Get the properties to use for the row headers 558 | try : 559 | properties = [] 560 | for dim_qname in self.property_dimensions[j] : 561 | properties.append(dim_qname) 562 | except KeyError : 563 | self.log.debug("({}.{}) No properties for cell".format(i,j)) 564 | self.row_dimensions.setdefault(i,[]).append((rowHeaderValue, properties)) 565 | 566 | # Use the column dimensions dictionary to find the objects of the d2s:dimension property 567 | try : 568 | for dim_qname in self.column_dimensions[j] : 569 | self.graph.add((rowHeaderValue, 570 | self.namespaces['d2s']['dimension'], 571 | self.namespaces['scope'][dim_qname])) 572 | except KeyError : 573 | self.log.debug("({}.{}) No column dimension for cell".format(i,j)) 574 | 575 | return 576 | 577 | def parseColHeader(self, i, j) : 578 | """ 579 | Create relevant triples for the cell marked as Header (i, j are row and column) 580 | """ 581 | if self.isEmpty(i,j): 582 | if self.insideMergeBox(i,j): 583 | k, l = self.getMergeBoxCoord(i,j) 584 | self.source_cell_value_qname = self.addValue(self.r_sheet.cell(k,l).value) 585 | else: 586 | return 587 | else: 588 | self.source_cell_value_qname = self.addValue(self.source_cell.value) 589 | 590 | self.graph.add((self.namespaces['scope'][self.source_cell_qname], 591 | self.namespaces['d2s']['isDimension'], 592 | self.namespaces['scope'][self.source_cell_value_qname])) 593 | self.graph.add((self.namespaces['scope'][self.source_cell_value_qname], 594 | RDF.type, 595 | self.namespaces['d2s']['Dimension'])) 596 | self.graph.add((self.namespaces['scope'][self.source_cell_qname], 597 | RDF.type, 598 | self.namespaces['skos'].Concept)) 599 | 600 | # Add the value qname to the column_dimensions list for that column 601 | self.column_dimensions.setdefault(j,[]).append(self.source_cell_value_qname) 602 | 603 | return 604 | 605 | def parseRowProperty(self, i, j) : 606 | """ 607 | Create relevant triples for the cell marked as Property (i, j are row and column) 608 | """ 609 | if self.isEmpty(i,j): 610 | if self.insideMergeBox(i,j): 611 | k, l = self.getMergeBoxCoord(i,j) 612 | self.source_cell_value_qname = self.addValue(self.r_sheet.cell(k,l).value) 613 | else: 614 | return 615 | else: 616 | self.source_cell_value_qname = self.addValue(self.source_cell.value) 617 | self.graph.add((self.namespaces['scope'][self.source_cell_qname],self.namespaces['d2s']['isDimensionProperty'],self.namespaces['scope'][self.source_cell_value_qname])) 618 | self.graph.add((self.namespaces['scope'][self.source_cell_value_qname],RDF.type,self.namespaces['qb']['DimensionProperty'])) 619 | self.graph.add((self.namespaces['scope'][self.source_cell_value_qname],RDF.type,RDF['Property'])) 620 | 621 | self.property_dimensions.setdefault(j,[]).append(self.source_cell_value_qname) 622 | 623 | return 624 | 625 | def parseTitle(self, i, j) : 626 | """ 627 | Create relevant triples for the cell marked as Title (i, j are row and column) 628 | """ 629 | 630 | self.source_cell_value_qname = self.addValue(self.source_cell.value) 631 | self.graph.add((self.namespaces['scope'][self.sheet_qname], self.namespaces['d2s']['title'], self.namespaces['scope'][self.source_cell_value_qname])) 632 | self.graph.add((self.namespaces['scope'][self.source_cell_value_qname],RDF.type,self.namespaces['d2s']['Dimension'])) 633 | 634 | return 635 | 636 | 637 | def parseData(self, i,j) : 638 | """ 639 | Create relevant triples for the cell marked as Data (i, j are row and column) 640 | """ 641 | 642 | if self.isEmpty(i,j) and self.config.get('dataCell', 'implicitZeros') == '0': 643 | return 644 | 645 | observation = BNode() 646 | 647 | self.graph.add((self.namespaces['scope'][self.source_cell_qname], 648 | self.namespaces['d2s']['isObservation'], 649 | observation)) 650 | self.graph.add((observation, 651 | RDF.type, 652 | self.namespaces['qb']['Observation'])) 653 | self.graph.add((observation, 654 | self.namespaces['qb']['dataSet'], 655 | self.namespaces['scope'][self.sheet_qname])) 656 | if self.isEmpty(i,j) and self.config.get('dataCell', 'implicitZeros') == '1': 657 | self.graph.add((observation, 658 | self.namespaces['d2s'][self.dataCellPropertyName], 659 | Literal(0))) 660 | else: 661 | self.graph.add((observation, 662 | self.namespaces['d2s'][self.dataCellPropertyName], 663 | Literal(self.source_cell.value))) 664 | 665 | # Use the row dimensions dictionary to find the properties that link data values to row headers 666 | try : 667 | for (dim_qname, properties) in self.row_dimensions[i] : 668 | for p in properties: 669 | self.graph.add((observation, 670 | self.namespaces['d2s'][p], 671 | dim_qname)) 672 | except KeyError : 673 | self.log.debug("({}.{}) No row dimension for cell".format(i,j)) 674 | 675 | # Use the column dimensions dictionary to find the objects of the d2s:dimension property 676 | try : 677 | for dim_qname in self.column_dimensions[j] : 678 | self.graph.add((observation, 679 | self.namespaces['d2s']['dimension'], 680 | self.namespaces['scope'][dim_qname])) 681 | except KeyError : 682 | self.log.debug("({}.{}) No column dimension for cell".format(i,j)) 683 | 684 | def parseAnnotation(self, i, j) : 685 | """ 686 | Create relevant triples for the annotation attached to cell (i, j) 687 | """ 688 | 689 | if self.config.get('annotations', 'model') == 'oa': 690 | # Create triples according to Open Annotation model 691 | 692 | body = BNode() 693 | 694 | self.annotationGraph.add((self.annotationNamespaces['scope'][self.source_cell_qname], 695 | RDF.type, 696 | self.annotationNamespaces['oa']['Annotation'] 697 | )) 698 | self.annotationGraph.add((self.annotationNamespaces['scope'][self.source_cell_qname], 699 | self.annotationNamespaces['oa']['hasBody'], 700 | body 701 | )) 702 | self.annotationGraph.add((body, 703 | RDF.value, 704 | Literal(self.annotations[(i,j)].text.replace("\n", " ").replace("\r", " ").replace("\r\n", " ").encode('utf-8')) 705 | )) 706 | self.annotationGraph.add((self.annotationNamespaces['scope'][self.source_cell_qname], 707 | self.annotationNamespaces['oa']['hasTarget'], 708 | self.namespaces['scope'][self.source_cell_qname] 709 | )) 710 | self.annotationGraph.add((self.annotationNamespaces['scope'][self.source_cell_qname], 711 | self.annotationNamespaces['oa']['annotator'], 712 | Literal(self.annotations[(i,j)].author.encode('utf-8')) 713 | )) 714 | self.annotationGraph.add((self.annotationNamespaces['scope'][self.source_cell_qname], 715 | self.annotationNamespaces['oa']['annotated'], 716 | Literal(datetime.datetime.fromtimestamp(os.path.getmtime(self.filename)).strftime("%Y-%m-%d"),datatype=self.annotationNamespaces['xsd']['date']) 717 | )) 718 | self.annotationGraph.add((self.annotationNamespaces['scope'][self.source_cell_qname], 719 | self.annotationNamespaces['oa']['generator'], 720 | URIRef("https://github.com/Data2Semantics/TabLinker") 721 | )) 722 | self.annotationGraph.add((self.annotationNamespaces['scope'][self.source_cell_qname], 723 | self.annotationNamespaces['oa']['generated'], 724 | Literal(datetime.datetime.now().strftime("%Y-%m-%d"), datatype=self.annotationNamespaces['xsd']['date']) 725 | )) 726 | self.annotationGraph.add((self.annotationNamespaces['scope'][self.source_cell_qname], 727 | self.annotationNamespaces['oa']['modelVersion'], 728 | URIRef("http://www.openannotation.org/spec/core/20120509.html") 729 | )) 730 | else: 731 | # Create triples according to Nanopublications model 732 | print "Nanopublications not implemented yet!" 733 | 734 | 735 | 736 | if __name__ == '__main__': 737 | """ 738 | Start the TabLinker for every file specified in the configuration file (../config.ini) 739 | """ 740 | logging.basicConfig(level=logging.INFO) 741 | logging.info('Reading configuration file') 742 | 743 | config = SafeConfigParser() 744 | try : 745 | config.read('../config.ini') 746 | srcMask = config.get('paths', 'srcMask') 747 | targetFolder = config.get('paths','targetFolder') 748 | verbose = config.get('debug','verbose') 749 | if verbose == "1" : 750 | logLevel = logging.DEBUG 751 | else : 752 | logLevel = logging.INFO 753 | except : 754 | logging.error("Could not find configuration file, using default settings!") 755 | srcMask = '../input/*_marked.xls' 756 | targetFolder = config.get('paths', 'targetFolder') 757 | logLevel = logging.DEBUG 758 | 759 | logging.basicConfig(level=logLevel) 760 | 761 | # Get list of annotated XLS files 762 | files = glob.glob(srcMask) 763 | logging.info("Found {0} files to convert.".format(len(files))) 764 | 765 | if len(files) == 0 : 766 | logging.error("No files found. Are you sure the path to the annotated XLS files is correct?") 767 | logging.info("Path searched: " + srcMask) 768 | quit() 769 | 770 | for filename in files : 771 | logging.info('Starting TabLinker for {0}'.format(filename)) 772 | 773 | tLinker = TabLinker(filename, config, logLevel) 774 | 775 | logging.debug('Calling linker') 776 | tLinker.doLink() 777 | logging.debug('Done linking') 778 | 779 | turtleFile = targetFolder + tLinker.fileBasename +'.ttl' 780 | turtleFileAnnotations = targetFolder + tLinker.fileBasename +'_annotations.ttl' 781 | logging.info("Generated {} triples.".format(len(tLinker.graph))) 782 | logging.info("Serializing graph to file {}".format(turtleFile)) 783 | try : 784 | fileWrite = open(turtleFile, "w") 785 | #Avoid rdflib writing the graph itself, as this is buggy in windows. 786 | #Instead, retrieve string and then write (probably more memory intensive...) 787 | turtle = tLinker.graph.serialize(destination=None, format=config.get('general', 'format')) 788 | fileWrite.writelines(turtle) 789 | fileWrite.close() 790 | 791 | #Annotations 792 | if tLinker.config.get('annotations', 'enabled') == "1": 793 | logging.info("Generated {} triples.".format(len(tLinker.annotationGraph))) 794 | logging.info("Serializing annotations to file {}".format(turtleFileAnnotations)) 795 | fileWriteAnnotations = open(turtleFileAnnotations, "w") 796 | turtleAnnotations = tLinker.annotationGraph.serialize(None, format=config.get('general', 'format')) 797 | fileWriteAnnotations.writelines(turtleAnnotations) 798 | fileWriteAnnotations.close() 799 | except : 800 | logging.error("Whoops! Something went wrong in serializing to output file") 801 | logging.info(sys.exc_info()) 802 | traceback.print_exc(file=sys.stdout) 803 | 804 | logging.info("Done") 805 | 806 | 807 | 808 | -------------------------------------------------------------------------------- /src/tl-service.py: -------------------------------------------------------------------------------- 1 | from bottle import route, run, template, request, static_file 2 | from tablinker import TabLinker 3 | import logging 4 | from ConfigParser import SafeConfigParser 5 | import glob 6 | import sys 7 | import traceback 8 | import os 9 | from os import listdir 10 | from os.path import isfile, join 11 | 12 | fileList = [] 13 | 14 | 15 | @route('/tablinker/version') 16 | def version(): 17 | return "TabLinker version" 18 | 19 | @route('/tablinker') 20 | @route('/tablinker/') 21 | def tablinker(): 22 | inPath = "../input/" 23 | outPath = "../output/" 24 | inFiles = [ f for f in listdir(inPath) if isfile(join(inPath,f)) ] 25 | outFiles = [ f for f in listdir(outPath) if isfile(join(outPath,f)) ] 26 | return template('tl-service', state='start', inFiles=inFiles, outFiles=outFiles) 27 | 28 | @route('/tablinker/upload', method='POST') 29 | def upload(): 30 | # category = request.forms.get('category') 31 | upload = request.files.get('upload') 32 | name, ext = os.path.splitext(upload.filename) 33 | if ext not in ('.xls'): 34 | return 'File extension ' + ext + ' not allowed.' 35 | 36 | save_path = '../input/' + upload.filename 37 | fileList.append(upload.filename) 38 | upload.save(save_path, overwrite = True) # appends upload.filename automatically 39 | return template('tl-service', state='uploaded') 40 | 41 | @route('/tablinker/run') 42 | def tablinker(): 43 | logging.basicConfig(level=logging.INFO) 44 | logging.info('Reading configuration file') 45 | 46 | config = SafeConfigParser() 47 | try : 48 | config.read('../config.ini') 49 | srcMask = config.get('paths', 'srcMask') 50 | targetFolder = config.get('paths','targetFolder') 51 | verbose = config.get('debug','verbose') 52 | if verbose == "1" : 53 | logLevel = logging.DEBUG 54 | else : 55 | logLevel = logging.INFO 56 | except : 57 | logging.error("Could not find configuration file, using default settings!") 58 | srcMask = '../input/*_marked.xls' 59 | targetFolder = config.get('paths', 'targetFolder') 60 | logLevel = logging.DEBUG 61 | 62 | logging.basicConfig(level=logLevel) 63 | 64 | # Get list of annotated XLS files 65 | files = glob.glob(srcMask) 66 | logging.info("Found {0} files to convert.".format(len(files))) 67 | 68 | for filename in files : 69 | logging.info('Starting TabLinker for {0}'.format(filename)) 70 | 71 | tLinker = TabLinker(filename, config, logLevel) 72 | 73 | logging.debug('Calling linker') 74 | tLinker.doLink() 75 | logging.debug('Done linking') 76 | 77 | turtleFile = targetFolder + tLinker.fileBasename +'.ttl' 78 | turtleFileAnnotations = targetFolder + tLinker.fileBasename +'_annotations.ttl' 79 | logging.info("Generated {} triples.".format(len(tLinker.graph))) 80 | logging.info("Serializing graph to file {}".format(turtleFile)) 81 | try : 82 | fileWrite = open(turtleFile, "w") 83 | #Avoid rdflib writing the graph itself, as this is buggy in windows. 84 | #Instead, retrieve string and then write (probably more memory intensive...) 85 | turtle = tLinker.graph.serialize(destination=None, format=config.get('general', 'format')) 86 | fileWrite.writelines(turtle) 87 | fileWrite.close() 88 | 89 | #Annotations 90 | if tLinker.config.get('annotations', 'enabled') == "1": 91 | logging.info("Generated {} triples.".format(len(tLinker.annotationGraph))) 92 | logging.info("Serializing annotations to file {}".format(turtleFileAnnotations)) 93 | fileWriteAnnotations = open(turtleFileAnnotations, "w") 94 | turtleAnnotations = tLinker.annotationGraph.serialize(None, format=config.get('general', 'format')) 95 | fileWriteAnnotations.writelines(turtleAnnotations) 96 | fileWriteAnnotations.close() 97 | except : 98 | logging.error("Whoops! Something went wrong in serializing to output file") 99 | logging.info(sys.exc_info()) 100 | traceback.print_exc(file=sys.stdout) 101 | 102 | logging.info("Done") 103 | return template('tl-service', state='converted', numtriples=str(len(tLinker.graph))) 104 | 105 | @route('/tablinker/download') 106 | def download(): 107 | fileDownload = fileList[-1].split('.')[0] + '.ttl' 108 | return static_file(fileDownload, root = '../output/', download = fileDownload) 109 | 110 | # Static Routes 111 | @route('/js/') 112 | def javascripts(filename): 113 | return static_file(filename, root='views/js') 114 | 115 | @route('/css/') 116 | def stylesheets(filename): 117 | return static_file(filename, root='views/css') 118 | 119 | @route('/img/') 120 | def images(filename): 121 | return static_file(filename, root='views/img') 122 | 123 | @route('/fonts/') 124 | def fonts(filename): 125 | return static_file(filename, root='views/fonts') 126 | 127 | 128 | run(host = 'lod.cedar-project.nl', port = 8081, debug = True) 129 | 130 | -------------------------------------------------------------------------------- /src/untablinker.py: -------------------------------------------------------------------------------- 1 | """ 2 | Created on 19 Sep 2011 3 | Modified on 31 Jan 2012 4 | 5 | Authors: Rinke Hoekstra, Laurens Rietveld 6 | Copyright: VU University Amsterdam, 2011/2012 7 | License: LGPLv3 8 | 9 | """ 10 | import xlwt 11 | import glob 12 | from rdflib import ConjunctiveGraph, Namespace, plugin 13 | import rdflib 14 | import re 15 | from ConfigParser import SafeConfigParser 16 | import logging 17 | import os 18 | import sys 19 | import shutil 20 | try: 21 | import rdfextras#@UnusedImport 22 | except ImportError, e: 23 | print("RDF extras package is required for this script to work. Install by executing:") 24 | print("sudo easy_install rdfextras") 25 | quit() 26 | 27 | 28 | 29 | 30 | class UnTabLinker(object): 31 | defaultNamespacePrefix = 'http://www.data2semantics.org/data/' 32 | namespaces = { 33 | 'dcterms':Namespace('http://purl.org/dc/terms/'), 34 | 'skos':Namespace('http://www.w3.org/2004/02/skos/core#'), 35 | 'd2s':Namespace('http://www.data2semantics.org/core/'), 36 | 'qb':Namespace('http://purl.org/linked-data/cube#'), 37 | 'owl':Namespace('http://www.w3.org/2002/07/owl#') 38 | } 39 | 40 | def __init__(self, directory, config, level = logging.DEBUG): 41 | """TabLinker constructor 42 | 43 | Keyword arguments: 44 | directory -- String containing the name turtle file 45 | config -- Configuration object, loaded from .ini file 46 | level -- A logging level as defined in the logging module 47 | """ 48 | self.config = config 49 | self.log = logging.getLogger("TabLinker") 50 | self.log.setLevel(level) 51 | 52 | self.graph = ConjunctiveGraph() 53 | self.log.debug('Loading and parsing file') 54 | self.graph.parse(filename, format=config.get('general', 'format')) 55 | 56 | plugin.register('sparql', rdflib.query.Processor,'rdfextras.sparql.processor', 'Processor') 57 | plugin.register('sparql', rdflib.query.Result,'rdfextras.sparql.query', 'SPARQLQueryResult') 58 | 59 | 60 | 61 | 62 | def saveFiles(self, directory): 63 | """ 64 | Convert data in rdf to excel 65 | 66 | Keyword arguments: 67 | directory -- Directory to save files in 68 | """ 69 | 70 | #In the future, have a sparql query here to retrieve all files in this graph 71 | #Not possible now, because tablinker doesnt explicitly store filename yet 72 | self.saveFile(directory, "converted") 73 | 74 | 75 | 76 | def saveFile(self, directory, filename): 77 | """ 78 | Retrieve information from graph, and save file 79 | """ 80 | self.wbk = xlwt.Workbook() 81 | 82 | 83 | #Retrieve sheets for this file 84 | 85 | #In the future, have a sparql query here to retrieve all sheets for this file 86 | #Not possible now, because tablinker doesnt explicitly store sheet name yet 87 | self.addSheetToXls("sheetname") 88 | 89 | self.wbk.save(directory + filename + '.xls') 90 | 91 | def addSheetToXls(self, sheetName): 92 | """ 93 | Get values for this sheet, and store in excel object 94 | """ 95 | self.sheet = self.wbk.add_sheet(sheetName) 96 | 97 | #Get the row IDs from the RDF set 98 | queryResult = self.graph.query( 99 | """SELECT DISTINCT ?cell ?value 100 | WHERE { 101 | ?node ?cell . 102 | ?node ?value . 103 | }""", 104 | #Can't use prefix d2s. This produces parsing error (event though namespace is defined). 105 | #A bug in the query parser I guess 106 | #also, dont use [] in this query processor... 107 | initNs=self.namespaces 108 | ) 109 | if (len(queryResult) == 0): 110 | self.log.error("No rows found for sheet {0}".format(sheetName)) 111 | 112 | #Loop through cells and add values to excel 113 | for resultRow in queryResult.result: 114 | cell, value = resultRow 115 | col, row = self.cellname2index(cell) 116 | self.sheet.write(row, col, value) 117 | 118 | 119 | def cellname2index(self, cellname): 120 | matches = re.search('([A-Z]*)([0-9]*)',cellname) 121 | if (len(matches.groups()) != 2 ): 122 | logging.error("Failed to parse cell name {0}. Exiting...".format(cellname)) 123 | quit() 124 | col = reduce(lambda s,a:s*26+ord(a)-ord('A'), matches.group(1), 0) 125 | row = int(matches.group(2)) - 1 126 | return col,row 127 | 128 | def checkArg() : 129 | """ 130 | Checks validity of argument (filename) 131 | """ 132 | if (len(sys.argv) != 2): 133 | logging.error("Please provider turtlefile as parameter. exiting...") 134 | quit() 135 | if (os.path.isfile(sys.argv[1]) == False): 136 | logging.error("File {0} does not exist. exiting...".format(sys.argv[1])) 137 | quit() 138 | fileExtension = os.path.splitext(sys.argv[1])[1] 139 | if (fileExtension != '.ttl'): 140 | logging.error("Only ttl files are accepted. Current extension: {0}. exiting...".format(fileExtension)) 141 | quit() 142 | 143 | if __name__ == '__main__': 144 | """ 145 | Start the un-TabLinker for ttl file 146 | """ 147 | logging.basicConfig(level=logging.INFO) 148 | checkArg() 149 | filename = sys.argv[1] 150 | logging.info('Reading configuration file') 151 | config = SafeConfigParser() 152 | try : 153 | config.read('../config.ini') 154 | srcMask = config.get('paths', 'srcMask') 155 | targetFolder = config.get('paths','targetFolder') 156 | verbose = config.get('debug','verbose') 157 | if verbose == "1" : 158 | logLevel = logging.DEBUG 159 | else : 160 | logLevel = logging.INFO 161 | except : 162 | logging.error("Could not find configuration file. Exiting") 163 | quit() 164 | 165 | logging.basicConfig(level=logLevel) 166 | 167 | # Get list of annotated XLS files 168 | files = glob.glob(srcMask) 169 | logging.info("Found {0} files to convert.".format(len(files))) 170 | 171 | unLinker = UnTabLinker(filename, config, logLevel) 172 | 173 | basename = os.path.basename(filename) 174 | basename = re.search('(.*)\.ttl',basename).group(1) 175 | directory = config.get('paths', 'targetFolder') + basename + "/" 176 | 177 | if (os.path.isdir(directory)) : 178 | logging.debug('Output dir {0} already exists. Deleting'.format(directory)) 179 | shutil.rmtree(directory) 180 | logging.debug('Creating dir {0}'.format(directory)) 181 | os.makedirs(directory) 182 | unLinker.saveFiles(directory) 183 | 184 | 185 | logging.info("Done") 186 | 187 | 188 | 189 | -------------------------------------------------------------------------------- /src/views/css/bootstrap-theme.css: -------------------------------------------------------------------------------- 1 | .btn-default, 2 | .btn-primary, 3 | .btn-success, 4 | .btn-info, 5 | .btn-warning, 6 | .btn-danger { 7 | text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2); 8 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075); 9 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075); 10 | } 11 | 12 | .btn-default:active, 13 | .btn-primary:active, 14 | .btn-success:active, 15 | .btn-info:active, 16 | .btn-warning:active, 17 | .btn-danger:active, 18 | .btn-default.active, 19 | .btn-primary.active, 20 | .btn-success.active, 21 | .btn-info.active, 22 | .btn-warning.active, 23 | .btn-danger.active { 24 | -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); 25 | box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); 26 | } 27 | 28 | .btn:active, 29 | .btn.active { 30 | background-image: none; 31 | } 32 | 33 | .btn-default { 34 | text-shadow: 0 1px 0 #fff; 35 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#ffffff), to(#e6e6e6)); 36 | background-image: -webkit-linear-gradient(top, #ffffff, 0%, #e6e6e6, 100%); 37 | background-image: -moz-linear-gradient(top, #ffffff 0%, #e6e6e6 100%); 38 | background-image: linear-gradient(to bottom, #ffffff 0%, #e6e6e6 100%); 39 | background-repeat: repeat-x; 40 | border-color: #e0e0e0; 41 | border-color: #ccc; 42 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe6e6e6', GradientType=0); 43 | } 44 | 45 | .btn-default:active, 46 | .btn-default.active { 47 | background-color: #e6e6e6; 48 | border-color: #e0e0e0; 49 | } 50 | 51 | .btn-primary { 52 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#428bca), to(#3071a9)); 53 | background-image: -webkit-linear-gradient(top, #428bca, 0%, #3071a9, 100%); 54 | background-image: -moz-linear-gradient(top, #428bca 0%, #3071a9 100%); 55 | background-image: linear-gradient(to bottom, #428bca 0%, #3071a9 100%); 56 | background-repeat: repeat-x; 57 | border-color: #2d6ca2; 58 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3071a9', GradientType=0); 59 | } 60 | 61 | .btn-primary:active, 62 | .btn-primary.active { 63 | background-color: #3071a9; 64 | border-color: #2d6ca2; 65 | } 66 | 67 | .btn-success { 68 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#5cb85c), to(#449d44)); 69 | background-image: -webkit-linear-gradient(top, #5cb85c, 0%, #449d44, 100%); 70 | background-image: -moz-linear-gradient(top, #5cb85c 0%, #449d44 100%); 71 | background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%); 72 | background-repeat: repeat-x; 73 | border-color: #419641; 74 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0); 75 | } 76 | 77 | .btn-success:active, 78 | .btn-success.active { 79 | background-color: #449d44; 80 | border-color: #419641; 81 | } 82 | 83 | .btn-warning { 84 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#f0ad4e), to(#ec971f)); 85 | background-image: -webkit-linear-gradient(top, #f0ad4e, 0%, #ec971f, 100%); 86 | background-image: -moz-linear-gradient(top, #f0ad4e 0%, #ec971f 100%); 87 | background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%); 88 | background-repeat: repeat-x; 89 | border-color: #eb9316; 90 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0); 91 | } 92 | 93 | .btn-warning:active, 94 | .btn-warning.active { 95 | background-color: #ec971f; 96 | border-color: #eb9316; 97 | } 98 | 99 | .btn-danger { 100 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#d9534f), to(#c9302c)); 101 | background-image: -webkit-linear-gradient(top, #d9534f, 0%, #c9302c, 100%); 102 | background-image: -moz-linear-gradient(top, #d9534f 0%, #c9302c 100%); 103 | background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%); 104 | background-repeat: repeat-x; 105 | border-color: #c12e2a; 106 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0); 107 | } 108 | 109 | .btn-danger:active, 110 | .btn-danger.active { 111 | background-color: #c9302c; 112 | border-color: #c12e2a; 113 | } 114 | 115 | .btn-info { 116 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#5bc0de), to(#31b0d5)); 117 | background-image: -webkit-linear-gradient(top, #5bc0de, 0%, #31b0d5, 100%); 118 | background-image: -moz-linear-gradient(top, #5bc0de 0%, #31b0d5 100%); 119 | background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%); 120 | background-repeat: repeat-x; 121 | border-color: #2aabd2; 122 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0); 123 | } 124 | 125 | .btn-info:active, 126 | .btn-info.active { 127 | background-color: #31b0d5; 128 | border-color: #2aabd2; 129 | } 130 | 131 | .thumbnail, 132 | .img-thumbnail { 133 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075); 134 | box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075); 135 | } 136 | 137 | .dropdown-menu > li > a:hover, 138 | .dropdown-menu > li > a:focus, 139 | .dropdown-menu > .active > a, 140 | .dropdown-menu > .active > a:hover, 141 | .dropdown-menu > .active > a:focus { 142 | background-color: #357ebd; 143 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#428bca), to(#357ebd)); 144 | background-image: -webkit-linear-gradient(top, #428bca, 0%, #357ebd, 100%); 145 | background-image: -moz-linear-gradient(top, #428bca 0%, #357ebd 100%); 146 | background-image: linear-gradient(to bottom, #428bca 0%, #357ebd 100%); 147 | background-repeat: repeat-x; 148 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0); 149 | } 150 | 151 | .navbar { 152 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#ffffff), to(#f8f8f8)); 153 | background-image: -webkit-linear-gradient(top, #ffffff, 0%, #f8f8f8, 100%); 154 | background-image: -moz-linear-gradient(top, #ffffff 0%, #f8f8f8 100%); 155 | background-image: linear-gradient(to bottom, #ffffff 0%, #f8f8f8 100%); 156 | background-repeat: repeat-x; 157 | border-radius: 4px; 158 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0); 159 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075); 160 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075); 161 | } 162 | 163 | .navbar .navbar-nav > .active > a { 164 | background-color: #f8f8f8; 165 | } 166 | 167 | .navbar-brand, 168 | .navbar-nav > li > a { 169 | text-shadow: 0 1px 0 rgba(255, 255, 255, 0.25); 170 | } 171 | 172 | .navbar-inverse { 173 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#3c3c3c), to(#222222)); 174 | background-image: -webkit-linear-gradient(top, #3c3c3c, 0%, #222222, 100%); 175 | background-image: -moz-linear-gradient(top, #3c3c3c 0%, #222222 100%); 176 | background-image: linear-gradient(to bottom, #3c3c3c 0%, #222222 100%); 177 | background-repeat: repeat-x; 178 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0); 179 | } 180 | 181 | .navbar-inverse .navbar-nav > .active > a { 182 | background-color: #222222; 183 | } 184 | 185 | .navbar-inverse .navbar-brand, 186 | .navbar-inverse .navbar-nav > li > a { 187 | text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); 188 | } 189 | 190 | .navbar-static-top, 191 | .navbar-fixed-top, 192 | .navbar-fixed-bottom { 193 | border-radius: 0; 194 | } 195 | 196 | .alert { 197 | text-shadow: 0 1px 0 rgba(255, 255, 255, 0.2); 198 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05); 199 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05); 200 | } 201 | 202 | .alert-success { 203 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#dff0d8), to(#c8e5bc)); 204 | background-image: -webkit-linear-gradient(top, #dff0d8, 0%, #c8e5bc, 100%); 205 | background-image: -moz-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%); 206 | background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%); 207 | background-repeat: repeat-x; 208 | border-color: #b2dba1; 209 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0); 210 | } 211 | 212 | .alert-info { 213 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#d9edf7), to(#b9def0)); 214 | background-image: -webkit-linear-gradient(top, #d9edf7, 0%, #b9def0, 100%); 215 | background-image: -moz-linear-gradient(top, #d9edf7 0%, #b9def0 100%); 216 | background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%); 217 | background-repeat: repeat-x; 218 | border-color: #9acfea; 219 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0); 220 | } 221 | 222 | .alert-warning { 223 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#fcf8e3), to(#f8efc0)); 224 | background-image: -webkit-linear-gradient(top, #fcf8e3, 0%, #f8efc0, 100%); 225 | background-image: -moz-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%); 226 | background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%); 227 | background-repeat: repeat-x; 228 | border-color: #f5e79e; 229 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0); 230 | } 231 | 232 | .alert-danger { 233 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#f2dede), to(#e7c3c3)); 234 | background-image: -webkit-linear-gradient(top, #f2dede, 0%, #e7c3c3, 100%); 235 | background-image: -moz-linear-gradient(top, #f2dede 0%, #e7c3c3 100%); 236 | background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%); 237 | background-repeat: repeat-x; 238 | border-color: #dca7a7; 239 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0); 240 | } 241 | 242 | .progress { 243 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#ebebeb), to(#f5f5f5)); 244 | background-image: -webkit-linear-gradient(top, #ebebeb, 0%, #f5f5f5, 100%); 245 | background-image: -moz-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%); 246 | background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%); 247 | background-repeat: repeat-x; 248 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0); 249 | } 250 | 251 | .progress-bar { 252 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#428bca), to(#3071a9)); 253 | background-image: -webkit-linear-gradient(top, #428bca, 0%, #3071a9, 100%); 254 | background-image: -moz-linear-gradient(top, #428bca 0%, #3071a9 100%); 255 | background-image: linear-gradient(to bottom, #428bca 0%, #3071a9 100%); 256 | background-repeat: repeat-x; 257 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3071a9', GradientType=0); 258 | } 259 | 260 | .progress-bar-success { 261 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#5cb85c), to(#449d44)); 262 | background-image: -webkit-linear-gradient(top, #5cb85c, 0%, #449d44, 100%); 263 | background-image: -moz-linear-gradient(top, #5cb85c 0%, #449d44 100%); 264 | background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%); 265 | background-repeat: repeat-x; 266 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0); 267 | } 268 | 269 | .progress-bar-info { 270 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#5bc0de), to(#31b0d5)); 271 | background-image: -webkit-linear-gradient(top, #5bc0de, 0%, #31b0d5, 100%); 272 | background-image: -moz-linear-gradient(top, #5bc0de 0%, #31b0d5 100%); 273 | background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%); 274 | background-repeat: repeat-x; 275 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0); 276 | } 277 | 278 | .progress-bar-warning { 279 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#f0ad4e), to(#ec971f)); 280 | background-image: -webkit-linear-gradient(top, #f0ad4e, 0%, #ec971f, 100%); 281 | background-image: -moz-linear-gradient(top, #f0ad4e 0%, #ec971f 100%); 282 | background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%); 283 | background-repeat: repeat-x; 284 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0); 285 | } 286 | 287 | .progress-bar-danger { 288 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#d9534f), to(#c9302c)); 289 | background-image: -webkit-linear-gradient(top, #d9534f, 0%, #c9302c, 100%); 290 | background-image: -moz-linear-gradient(top, #d9534f 0%, #c9302c 100%); 291 | background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%); 292 | background-repeat: repeat-x; 293 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0); 294 | } 295 | 296 | .list-group { 297 | border-radius: 4px; 298 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075); 299 | box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075); 300 | } 301 | 302 | .list-group-item.active, 303 | .list-group-item.active:hover, 304 | .list-group-item.active:focus { 305 | text-shadow: 0 -1px 0 #3071a9; 306 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#428bca), to(#3278b3)); 307 | background-image: -webkit-linear-gradient(top, #428bca, 0%, #3278b3, 100%); 308 | background-image: -moz-linear-gradient(top, #428bca 0%, #3278b3 100%); 309 | background-image: linear-gradient(to bottom, #428bca 0%, #3278b3 100%); 310 | background-repeat: repeat-x; 311 | border-color: #3278b3; 312 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3278b3', GradientType=0); 313 | } 314 | 315 | .panel { 316 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); 317 | box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); 318 | } 319 | 320 | .panel-default > .panel-heading { 321 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#f5f5f5), to(#e8e8e8)); 322 | background-image: -webkit-linear-gradient(top, #f5f5f5, 0%, #e8e8e8, 100%); 323 | background-image: -moz-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); 324 | background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%); 325 | background-repeat: repeat-x; 326 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0); 327 | } 328 | 329 | .panel-primary > .panel-heading { 330 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#428bca), to(#357ebd)); 331 | background-image: -webkit-linear-gradient(top, #428bca, 0%, #357ebd, 100%); 332 | background-image: -moz-linear-gradient(top, #428bca 0%, #357ebd 100%); 333 | background-image: linear-gradient(to bottom, #428bca 0%, #357ebd 100%); 334 | background-repeat: repeat-x; 335 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0); 336 | } 337 | 338 | .panel-success > .panel-heading { 339 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#dff0d8), to(#d0e9c6)); 340 | background-image: -webkit-linear-gradient(top, #dff0d8, 0%, #d0e9c6, 100%); 341 | background-image: -moz-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%); 342 | background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%); 343 | background-repeat: repeat-x; 344 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0); 345 | } 346 | 347 | .panel-info > .panel-heading { 348 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#d9edf7), to(#c4e3f3)); 349 | background-image: -webkit-linear-gradient(top, #d9edf7, 0%, #c4e3f3, 100%); 350 | background-image: -moz-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%); 351 | background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%); 352 | background-repeat: repeat-x; 353 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0); 354 | } 355 | 356 | .panel-warning > .panel-heading { 357 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#fcf8e3), to(#faf2cc)); 358 | background-image: -webkit-linear-gradient(top, #fcf8e3, 0%, #faf2cc, 100%); 359 | background-image: -moz-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%); 360 | background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%); 361 | background-repeat: repeat-x; 362 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0); 363 | } 364 | 365 | .panel-danger > .panel-heading { 366 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#f2dede), to(#ebcccc)); 367 | background-image: -webkit-linear-gradient(top, #f2dede, 0%, #ebcccc, 100%); 368 | background-image: -moz-linear-gradient(top, #f2dede 0%, #ebcccc 100%); 369 | background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%); 370 | background-repeat: repeat-x; 371 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0); 372 | } 373 | 374 | .well { 375 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#e8e8e8), to(#f5f5f5)); 376 | background-image: -webkit-linear-gradient(top, #e8e8e8, 0%, #f5f5f5, 100%); 377 | background-image: -moz-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%); 378 | background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%); 379 | background-repeat: repeat-x; 380 | border-color: #dcdcdc; 381 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0); 382 | -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1); 383 | box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1); 384 | } -------------------------------------------------------------------------------- /src/views/css/bootstrap-theme.min.css: -------------------------------------------------------------------------------- 1 | .btn-default,.btn-primary,.btn-success,.btn-info,.btn-warning,.btn-danger{text-shadow:0 -1px 0 rgba(0,0,0,0.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.15),0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 0 rgba(255,255,255,0.15),0 1px 1px rgba(0,0,0,0.075)}.btn-default:active,.btn-primary:active,.btn-success:active,.btn-info:active,.btn-warning:active,.btn-danger:active,.btn-default.active,.btn-primary.active,.btn-success.active,.btn-info.active,.btn-warning.active,.btn-danger.active{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn:active,.btn.active{background-image:none}.btn-default{text-shadow:0 1px 0 #fff;background-image:-webkit-gradient(linear,left 0,left 100%,from(#fff),to(#e6e6e6));background-image:-webkit-linear-gradient(top,#fff,0%,#e6e6e6,100%);background-image:-moz-linear-gradient(top,#fff 0,#e6e6e6 100%);background-image:linear-gradient(to bottom,#fff 0,#e6e6e6 100%);background-repeat:repeat-x;border-color:#e0e0e0;border-color:#ccc;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff',endColorstr='#ffe6e6e6',GradientType=0)}.btn-default:active,.btn-default.active{background-color:#e6e6e6;border-color:#e0e0e0}.btn-primary{background-image:-webkit-gradient(linear,left 0,left 100%,from(#428bca),to(#3071a9));background-image:-webkit-linear-gradient(top,#428bca,0%,#3071a9,100%);background-image:-moz-linear-gradient(top,#428bca 0,#3071a9 100%);background-image:linear-gradient(to bottom,#428bca 0,#3071a9 100%);background-repeat:repeat-x;border-color:#2d6ca2;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca',endColorstr='#ff3071a9',GradientType=0)}.btn-primary:active,.btn-primary.active{background-color:#3071a9;border-color:#2d6ca2}.btn-success{background-image:-webkit-gradient(linear,left 0,left 100%,from(#5cb85c),to(#449d44));background-image:-webkit-linear-gradient(top,#5cb85c,0%,#449d44,100%);background-image:-moz-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:linear-gradient(to bottom,#5cb85c 0,#449d44 100%);background-repeat:repeat-x;border-color:#419641;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c',endColorstr='#ff449d44',GradientType=0)}.btn-success:active,.btn-success.active{background-color:#449d44;border-color:#419641}.btn-warning{background-image:-webkit-gradient(linear,left 0,left 100%,from(#f0ad4e),to(#ec971f));background-image:-webkit-linear-gradient(top,#f0ad4e,0%,#ec971f,100%);background-image:-moz-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:linear-gradient(to bottom,#f0ad4e 0,#ec971f 100%);background-repeat:repeat-x;border-color:#eb9316;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e',endColorstr='#ffec971f',GradientType=0)}.btn-warning:active,.btn-warning.active{background-color:#ec971f;border-color:#eb9316}.btn-danger{background-image:-webkit-gradient(linear,left 0,left 100%,from(#d9534f),to(#c9302c));background-image:-webkit-linear-gradient(top,#d9534f,0%,#c9302c,100%);background-image:-moz-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:linear-gradient(to bottom,#d9534f 0,#c9302c 100%);background-repeat:repeat-x;border-color:#c12e2a;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f',endColorstr='#ffc9302c',GradientType=0)}.btn-danger:active,.btn-danger.active{background-color:#c9302c;border-color:#c12e2a}.btn-info{background-image:-webkit-gradient(linear,left 0,left 100%,from(#5bc0de),to(#31b0d5));background-image:-webkit-linear-gradient(top,#5bc0de,0%,#31b0d5,100%);background-image:-moz-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:linear-gradient(to bottom,#5bc0de 0,#31b0d5 100%);background-repeat:repeat-x;border-color:#2aabd2;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de',endColorstr='#ff31b0d5',GradientType=0)}.btn-info:active,.btn-info.active{background-color:#31b0d5;border-color:#2aabd2}.thumbnail,.img-thumbnail{-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.075);box-shadow:0 1px 2px rgba(0,0,0,0.075)}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus,.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{background-color:#357ebd;background-image:-webkit-gradient(linear,left 0,left 100%,from(#428bca),to(#357ebd));background-image:-webkit-linear-gradient(top,#428bca,0%,#357ebd,100%);background-image:-moz-linear-gradient(top,#428bca 0,#357ebd 100%);background-image:linear-gradient(to bottom,#428bca 0,#357ebd 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca',endColorstr='#ff357ebd',GradientType=0)}.navbar{background-image:-webkit-gradient(linear,left 0,left 100%,from(#fff),to(#f8f8f8));background-image:-webkit-linear-gradient(top,#fff,0%,#f8f8f8,100%);background-image:-moz-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:linear-gradient(to bottom,#fff 0,#f8f8f8 100%);background-repeat:repeat-x;border-radius:4px;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff',endColorstr='#fff8f8f8',GradientType=0);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.15),0 1px 5px rgba(0,0,0,0.075);box-shadow:inset 0 1px 0 rgba(255,255,255,0.15),0 1px 5px rgba(0,0,0,0.075)}.navbar .navbar-nav>.active>a{background-color:#f8f8f8}.navbar-brand,.navbar-nav>li>a{text-shadow:0 1px 0 rgba(255,255,255,0.25)}.navbar-inverse{background-image:-webkit-gradient(linear,left 0,left 100%,from(#3c3c3c),to(#222));background-image:-webkit-linear-gradient(top,#3c3c3c,0%,#222,100%);background-image:-moz-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:linear-gradient(to bottom,#3c3c3c 0,#222 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c',endColorstr='#ff222222',GradientType=0)}.navbar-inverse .navbar-nav>.active>a{background-color:#222}.navbar-inverse .navbar-brand,.navbar-inverse .navbar-nav>li>a{text-shadow:0 -1px 0 rgba(0,0,0,0.25)}.navbar-static-top,.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}.alert{text-shadow:0 1px 0 rgba(255,255,255,0.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.25),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 1px 0 rgba(255,255,255,0.25),0 1px 2px rgba(0,0,0,0.05)}.alert-success{background-image:-webkit-gradient(linear,left 0,left 100%,from(#dff0d8),to(#c8e5bc));background-image:-webkit-linear-gradient(top,#dff0d8,0%,#c8e5bc,100%);background-image:-moz-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:linear-gradient(to bottom,#dff0d8 0,#c8e5bc 100%);background-repeat:repeat-x;border-color:#b2dba1;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8',endColorstr='#ffc8e5bc',GradientType=0)}.alert-info{background-image:-webkit-gradient(linear,left 0,left 100%,from(#d9edf7),to(#b9def0));background-image:-webkit-linear-gradient(top,#d9edf7,0%,#b9def0,100%);background-image:-moz-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:linear-gradient(to bottom,#d9edf7 0,#b9def0 100%);background-repeat:repeat-x;border-color:#9acfea;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7',endColorstr='#ffb9def0',GradientType=0)}.alert-warning{background-image:-webkit-gradient(linear,left 0,left 100%,from(#fcf8e3),to(#f8efc0));background-image:-webkit-linear-gradient(top,#fcf8e3,0%,#f8efc0,100%);background-image:-moz-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:linear-gradient(to bottom,#fcf8e3 0,#f8efc0 100%);background-repeat:repeat-x;border-color:#f5e79e;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3',endColorstr='#fff8efc0',GradientType=0)}.alert-danger{background-image:-webkit-gradient(linear,left 0,left 100%,from(#f2dede),to(#e7c3c3));background-image:-webkit-linear-gradient(top,#f2dede,0%,#e7c3c3,100%);background-image:-moz-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:linear-gradient(to bottom,#f2dede 0,#e7c3c3 100%);background-repeat:repeat-x;border-color:#dca7a7;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede',endColorstr='#ffe7c3c3',GradientType=0)}.progress{background-image:-webkit-gradient(linear,left 0,left 100%,from(#ebebeb),to(#f5f5f5));background-image:-webkit-linear-gradient(top,#ebebeb,0%,#f5f5f5,100%);background-image:-moz-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:linear-gradient(to bottom,#ebebeb 0,#f5f5f5 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb',endColorstr='#fff5f5f5',GradientType=0)}.progress-bar{background-image:-webkit-gradient(linear,left 0,left 100%,from(#428bca),to(#3071a9));background-image:-webkit-linear-gradient(top,#428bca,0%,#3071a9,100%);background-image:-moz-linear-gradient(top,#428bca 0,#3071a9 100%);background-image:linear-gradient(to bottom,#428bca 0,#3071a9 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca',endColorstr='#ff3071a9',GradientType=0)}.progress-bar-success{background-image:-webkit-gradient(linear,left 0,left 100%,from(#5cb85c),to(#449d44));background-image:-webkit-linear-gradient(top,#5cb85c,0%,#449d44,100%);background-image:-moz-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:linear-gradient(to bottom,#5cb85c 0,#449d44 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c',endColorstr='#ff449d44',GradientType=0)}.progress-bar-info{background-image:-webkit-gradient(linear,left 0,left 100%,from(#5bc0de),to(#31b0d5));background-image:-webkit-linear-gradient(top,#5bc0de,0%,#31b0d5,100%);background-image:-moz-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:linear-gradient(to bottom,#5bc0de 0,#31b0d5 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de',endColorstr='#ff31b0d5',GradientType=0)}.progress-bar-warning{background-image:-webkit-gradient(linear,left 0,left 100%,from(#f0ad4e),to(#ec971f));background-image:-webkit-linear-gradient(top,#f0ad4e,0%,#ec971f,100%);background-image:-moz-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:linear-gradient(to bottom,#f0ad4e 0,#ec971f 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e',endColorstr='#ffec971f',GradientType=0)}.progress-bar-danger{background-image:-webkit-gradient(linear,left 0,left 100%,from(#d9534f),to(#c9302c));background-image:-webkit-linear-gradient(top,#d9534f,0%,#c9302c,100%);background-image:-moz-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:linear-gradient(to bottom,#d9534f 0,#c9302c 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f',endColorstr='#ffc9302c',GradientType=0)}.list-group{border-radius:4px;-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.075);box-shadow:0 1px 2px rgba(0,0,0,0.075)}.list-group-item.active,.list-group-item.active:hover,.list-group-item.active:focus{text-shadow:0 -1px 0 #3071a9;background-image:-webkit-gradient(linear,left 0,left 100%,from(#428bca),to(#3278b3));background-image:-webkit-linear-gradient(top,#428bca,0%,#3278b3,100%);background-image:-moz-linear-gradient(top,#428bca 0,#3278b3 100%);background-image:linear-gradient(to bottom,#428bca 0,#3278b3 100%);background-repeat:repeat-x;border-color:#3278b3;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca',endColorstr='#ff3278b3',GradientType=0)}.panel{-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.05);box-shadow:0 1px 2px rgba(0,0,0,0.05)}.panel-default>.panel-heading{background-image:-webkit-gradient(linear,left 0,left 100%,from(#f5f5f5),to(#e8e8e8));background-image:-webkit-linear-gradient(top,#f5f5f5,0%,#e8e8e8,100%);background-image:-moz-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5',endColorstr='#ffe8e8e8',GradientType=0)}.panel-primary>.panel-heading{background-image:-webkit-gradient(linear,left 0,left 100%,from(#428bca),to(#357ebd));background-image:-webkit-linear-gradient(top,#428bca,0%,#357ebd,100%);background-image:-moz-linear-gradient(top,#428bca 0,#357ebd 100%);background-image:linear-gradient(to bottom,#428bca 0,#357ebd 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca',endColorstr='#ff357ebd',GradientType=0)}.panel-success>.panel-heading{background-image:-webkit-gradient(linear,left 0,left 100%,from(#dff0d8),to(#d0e9c6));background-image:-webkit-linear-gradient(top,#dff0d8,0%,#d0e9c6,100%);background-image:-moz-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:linear-gradient(to bottom,#dff0d8 0,#d0e9c6 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8',endColorstr='#ffd0e9c6',GradientType=0)}.panel-info>.panel-heading{background-image:-webkit-gradient(linear,left 0,left 100%,from(#d9edf7),to(#c4e3f3));background-image:-webkit-linear-gradient(top,#d9edf7,0%,#c4e3f3,100%);background-image:-moz-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:linear-gradient(to bottom,#d9edf7 0,#c4e3f3 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7',endColorstr='#ffc4e3f3',GradientType=0)}.panel-warning>.panel-heading{background-image:-webkit-gradient(linear,left 0,left 100%,from(#fcf8e3),to(#faf2cc));background-image:-webkit-linear-gradient(top,#fcf8e3,0%,#faf2cc,100%);background-image:-moz-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:linear-gradient(to bottom,#fcf8e3 0,#faf2cc 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3',endColorstr='#fffaf2cc',GradientType=0)}.panel-danger>.panel-heading{background-image:-webkit-gradient(linear,left 0,left 100%,from(#f2dede),to(#ebcccc));background-image:-webkit-linear-gradient(top,#f2dede,0%,#ebcccc,100%);background-image:-moz-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:linear-gradient(to bottom,#f2dede 0,#ebcccc 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede',endColorstr='#ffebcccc',GradientType=0)}.well{background-image:-webkit-gradient(linear,left 0,left 100%,from(#e8e8e8),to(#f5f5f5));background-image:-webkit-linear-gradient(top,#e8e8e8,0%,#f5f5f5,100%);background-image:-moz-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:linear-gradient(to bottom,#e8e8e8 0,#f5f5f5 100%);background-repeat:repeat-x;border-color:#dcdcdc;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8',endColorstr='#fff5f5f5',GradientType=0);-webkit-box-shadow:inset 0 1px 3px rgba(0,0,0,0.05),0 1px 0 rgba(255,255,255,0.1);box-shadow:inset 0 1px 3px rgba(0,0,0,0.05),0 1px 0 rgba(255,255,255,0.1)} -------------------------------------------------------------------------------- /src/views/css/starter-template.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding-top: 50px; 3 | } 4 | .starter-template { 5 | padding: 40px 15px; 6 | text-align: center; 7 | } -------------------------------------------------------------------------------- /src/views/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Data2Semantics/TabLinker/888c5d9fac979e456b5474f0fdae05f9f97e26a8/src/views/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /src/views/fonts/glyphicons-halflings-regular.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | -------------------------------------------------------------------------------- /src/views/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Data2Semantics/TabLinker/888c5d9fac979e456b5474f0fdae05f9f97e26a8/src/views/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /src/views/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Data2Semantics/TabLinker/888c5d9fac979e456b5474f0fdae05f9f97e26a8/src/views/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /src/views/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Data2Semantics/TabLinker/888c5d9fac979e456b5474f0fdae05f9f97e26a8/src/views/img/favicon.ico -------------------------------------------------------------------------------- /src/views/img/tablinker-logo-150dpi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Data2Semantics/TabLinker/888c5d9fac979e456b5474f0fdae05f9f97e26a8/src/views/img/tablinker-logo-150dpi.png -------------------------------------------------------------------------------- /src/views/js/bootstrap.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * bootstrap.js v3.0.0 by @fat and @mdo 3 | * Copyright 2013 Twitter Inc. 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | if(!jQuery)throw new Error("Bootstrap requires jQuery");+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]}}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one(a.support.transition.end,function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b()})}(window.jQuery),+function(a){"use strict";var b='[data-dismiss="alert"]',c=function(c){a(c).on("click",b,this.close)};c.prototype.close=function(b){function c(){f.trigger("closed.bs.alert").remove()}var d=a(this),e=d.attr("data-target");e||(e=d.attr("href"),e=e&&e.replace(/.*(?=#[^\s]*$)/,""));var f=a(e);b&&b.preventDefault(),f.length||(f=d.hasClass("alert")?d:d.parent()),f.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(f.removeClass("in"),a.support.transition&&f.hasClass("fade")?f.one(a.support.transition.end,c).emulateTransitionEnd(150):c())};var d=a.fn.alert;a.fn.alert=function(b){return this.each(function(){var d=a(this),e=d.data("bs.alert");e||d.data("bs.alert",e=new c(this)),"string"==typeof b&&e[b].call(d)})},a.fn.alert.Constructor=c,a.fn.alert.noConflict=function(){return a.fn.alert=d,this},a(document).on("click.bs.alert.data-api",b,c.prototype.close)}(window.jQuery),+function(a){"use strict";var b=function(c,d){this.$element=a(c),this.options=a.extend({},b.DEFAULTS,d)};b.DEFAULTS={loadingText:"loading..."},b.prototype.setState=function(a){var b="disabled",c=this.$element,d=c.is("input")?"val":"html",e=c.data();a+="Text",e.resetText||c.data("resetText",c[d]()),c[d](e[a]||this.options[a]),setTimeout(function(){"loadingText"==a?c.addClass(b).attr(b,b):c.removeClass(b).removeAttr(b)},0)},b.prototype.toggle=function(){var a=this.$element.closest('[data-toggle="buttons"]');if(a.length){var b=this.$element.find("input").prop("checked",!this.$element.hasClass("active")).trigger("change");"radio"===b.prop("type")&&a.find(".active").removeClass("active")}this.$element.toggleClass("active")};var c=a.fn.button;a.fn.button=function(c){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof c&&c;e||d.data("bs.button",e=new b(this,f)),"toggle"==c?e.toggle():c&&e.setState(c)})},a.fn.button.Constructor=b,a.fn.button.noConflict=function(){return a.fn.button=c,this},a(document).on("click.bs.button.data-api","[data-toggle^=button]",function(b){var c=a(b.target);c.hasClass("btn")||(c=c.closest(".btn")),c.button("toggle"),b.preventDefault()})}(window.jQuery),+function(a){"use strict";var b=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=this.sliding=this.interval=this.$active=this.$items=null,"hover"==this.options.pause&&this.$element.on("mouseenter",a.proxy(this.pause,this)).on("mouseleave",a.proxy(this.cycle,this))};b.DEFAULTS={interval:5e3,pause:"hover",wrap:!0},b.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},b.prototype.getActiveIndex=function(){return this.$active=this.$element.find(".item.active"),this.$items=this.$active.parent().children(),this.$items.index(this.$active)},b.prototype.to=function(b){var c=this,d=this.getActiveIndex();return b>this.$items.length-1||0>b?void 0:this.sliding?this.$element.one("slid",function(){c.to(b)}):d==b?this.pause().cycle():this.slide(b>d?"next":"prev",a(this.$items[b]))},b.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition.end&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},b.prototype.next=function(){return this.sliding?void 0:this.slide("next")},b.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},b.prototype.slide=function(b,c){var d=this.$element.find(".item.active"),e=c||d[b](),f=this.interval,g="next"==b?"left":"right",h="next"==b?"first":"last",i=this;if(!e.length){if(!this.options.wrap)return;e=this.$element.find(".item")[h]()}this.sliding=!0,f&&this.pause();var j=a.Event("slide.bs.carousel",{relatedTarget:e[0],direction:g});if(!e.hasClass("active")){if(this.$indicators.length&&(this.$indicators.find(".active").removeClass("active"),this.$element.one("slid",function(){var b=a(i.$indicators.children()[i.getActiveIndex()]);b&&b.addClass("active")})),a.support.transition&&this.$element.hasClass("slide")){if(this.$element.trigger(j),j.isDefaultPrevented())return;e.addClass(b),e[0].offsetWidth,d.addClass(g),e.addClass(g),d.one(a.support.transition.end,function(){e.removeClass([b,g].join(" ")).addClass("active"),d.removeClass(["active",g].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger("slid")},0)}).emulateTransitionEnd(600)}else{if(this.$element.trigger(j),j.isDefaultPrevented())return;d.removeClass("active"),e.addClass("active"),this.sliding=!1,this.$element.trigger("slid")}return f&&this.cycle(),this}};var c=a.fn.carousel;a.fn.carousel=function(c){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},b.DEFAULTS,d.data(),"object"==typeof c&&c),g="string"==typeof c?c:f.slide;e||d.data("bs.carousel",e=new b(this,f)),"number"==typeof c?e.to(c):g?e[g]():f.interval&&e.pause().cycle()})},a.fn.carousel.Constructor=b,a.fn.carousel.noConflict=function(){return a.fn.carousel=c,this},a(document).on("click.bs.carousel.data-api","[data-slide], [data-slide-to]",function(b){var c,d=a(this),e=a(d.attr("data-target")||(c=d.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"")),f=a.extend({},e.data(),d.data()),g=d.attr("data-slide-to");g&&(f.interval=!1),e.carousel(f),(g=d.attr("data-slide-to"))&&e.data("bs.carousel").to(g),b.preventDefault()}),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var b=a(this);b.carousel(b.data())})})}(window.jQuery),+function(a){"use strict";var b=function(c,d){this.$element=a(c),this.options=a.extend({},b.DEFAULTS,d),this.transitioning=null,this.options.parent&&(this.$parent=a(this.options.parent)),this.options.toggle&&this.toggle()};b.DEFAULTS={toggle:!0},b.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},b.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b=a.Event("show.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.$parent&&this.$parent.find("> .panel > .in");if(c&&c.length){var d=c.data("bs.collapse");if(d&&d.transitioning)return;c.collapse("hide"),d||c.data("bs.collapse",null)}var e=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[e](0),this.transitioning=1;var f=function(){this.$element.removeClass("collapsing").addClass("in")[e]("auto"),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return f.call(this);var g=a.camelCase(["scroll",e].join("-"));this.$element.one(a.support.transition.end,a.proxy(f,this)).emulateTransitionEnd(350)[e](this.$element[0][g])}}},b.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse").removeClass("in"),this.transitioning=1;var d=function(){this.transitioning=0,this.$element.trigger("hidden.bs.collapse").removeClass("collapsing").addClass("collapse")};return a.support.transition?(this.$element[c](0).one(a.support.transition.end,a.proxy(d,this)).emulateTransitionEnd(350),void 0):d.call(this)}}},b.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()};var c=a.fn.collapse;a.fn.collapse=function(c){return this.each(function(){var d=a(this),e=d.data("bs.collapse"),f=a.extend({},b.DEFAULTS,d.data(),"object"==typeof c&&c);e||d.data("bs.collapse",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.collapse.Constructor=b,a.fn.collapse.noConflict=function(){return a.fn.collapse=c,this},a(document).on("click.bs.collapse.data-api","[data-toggle=collapse]",function(b){var c,d=a(this),e=d.attr("data-target")||b.preventDefault()||(c=d.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,""),f=a(e),g=f.data("bs.collapse"),h=g?"toggle":d.data(),i=d.attr("data-parent"),j=i&&a(i);g&&g.transitioning||(j&&j.find('[data-toggle=collapse][data-parent="'+i+'"]').not(d).addClass("collapsed"),d[f.hasClass("in")?"addClass":"removeClass"]("collapsed")),f.collapse(h)})}(window.jQuery),+function(a){"use strict";function b(){a(d).remove(),a(e).each(function(b){var d=c(a(this));d.hasClass("open")&&(d.trigger(b=a.Event("hide.bs.dropdown")),b.isDefaultPrevented()||d.removeClass("open").trigger("hidden.bs.dropdown"))})}function c(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}var d=".dropdown-backdrop",e="[data-toggle=dropdown]",f=function(b){a(b).on("click.bs.dropdown",this.toggle)};f.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=c(e),g=f.hasClass("open");if(b(),!g){if("ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a('

'}),b.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),b.prototype.constructor=b,b.prototype.getDefaults=function(){return b.DEFAULTS},b.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content")[this.options.html?"html":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},b.prototype.hasContent=function(){return this.getTitle()||this.getContent()},b.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},b.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")},b.prototype.tip=function(){return this.$tip||(this.$tip=a(this.options.template)),this.$tip};var c=a.fn.popover;a.fn.popover=function(c){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof c&&c;e||d.data("bs.popover",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.popover.Constructor=b,a.fn.popover.noConflict=function(){return a.fn.popover=c,this}}(window.jQuery),+function(a){"use strict";function b(c,d){var e,f=a.proxy(this.process,this);this.$element=a(c).is("body")?a(window):a(c),this.$body=a("body"),this.$scrollElement=this.$element.on("scroll.bs.scroll-spy.data-api",f),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||(e=a(c).attr("href"))&&e.replace(/.*(?=#[^\s]+$)/,"")||"")+" .nav li > a",this.offsets=a([]),this.targets=a([]),this.activeTarget=null,this.refresh(),this.process()}b.DEFAULTS={offset:10},b.prototype.refresh=function(){var b=this.$element[0]==window?"offset":"position";this.offsets=a([]),this.targets=a([]);var c=this;this.$body.find(this.selector).map(function(){var d=a(this),e=d.data("target")||d.attr("href"),f=/^#\w/.test(e)&&a(e);return f&&f.length&&[[f[b]().top+(!a.isWindow(c.$scrollElement.get(0))&&c.$scrollElement.scrollTop()),e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){c.offsets.push(this[0]),c.targets.push(this[1])})},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.$scrollElement[0].scrollHeight||this.$body[0].scrollHeight,d=c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(b>=d)return g!=(a=f.last()[0])&&this.activate(a);for(a=e.length;a--;)g!=f[a]&&b>=e[a]&&(!e[a+1]||b<=e[a+1])&&this.activate(f[a])},b.prototype.activate=function(b){this.activeTarget=b,a(this.selector).parents(".active").removeClass("active");var c=this.selector+'[data-target="'+b+'"],'+this.selector+'[href="'+b+'"]',d=a(c).parents("li").addClass("active");d.parent(".dropdown-menu").length&&(d=d.closest("li.dropdown").addClass("active")),d.trigger("activate")};var c=a.fn.scrollspy;a.fn.scrollspy=function(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.scrollspy.Constructor=b,a.fn.scrollspy.noConflict=function(){return a.fn.scrollspy=c,this},a(window).on("load",function(){a('[data-spy="scroll"]').each(function(){var b=a(this);b.scrollspy(b.data())})})}(window.jQuery),+function(a){"use strict";var b=function(b){this.element=a(b)};b.prototype.show=function(){var b=this.element,c=b.closest("ul:not(.dropdown-menu)"),d=b.attr("data-target");if(d||(d=b.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,"")),!b.parent("li").hasClass("active")){var e=c.find(".active:last a")[0],f=a.Event("show.bs.tab",{relatedTarget:e});if(b.trigger(f),!f.isDefaultPrevented()){var g=a(d);this.activate(b.parent("li"),c),this.activate(g,g.parent(),function(){b.trigger({type:"shown.bs.tab",relatedTarget:e})})}}},b.prototype.activate=function(b,c,d){function e(){f.removeClass("active").find("> .dropdown-menu > .active").removeClass("active"),b.addClass("active"),g?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu")&&b.closest("li.dropdown").addClass("active"),d&&d()}var f=c.find("> .active"),g=d&&a.support.transition&&f.hasClass("fade");g?f.one(a.support.transition.end,e).emulateTransitionEnd(150):e(),f.removeClass("in")};var c=a.fn.tab;a.fn.tab=function(c){return this.each(function(){var d=a(this),e=d.data("bs.tab");e||d.data("bs.tab",e=new b(this)),"string"==typeof c&&e[c]()})},a.fn.tab.Constructor=b,a.fn.tab.noConflict=function(){return a.fn.tab=c,this},a(document).on("click.bs.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"]',function(b){b.preventDefault(),a(this).tab("show")})}(window.jQuery),+function(a){"use strict";var b=function(c,d){this.options=a.extend({},b.DEFAULTS,d),this.$window=a(window).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(c),this.affixed=this.unpin=null,this.checkPosition()};b.RESET="affix affix-top affix-bottom",b.DEFAULTS={offset:0},b.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},b.prototype.checkPosition=function(){if(this.$element.is(":visible")){var c=a(document).height(),d=this.$window.scrollTop(),e=this.$element.offset(),f=this.options.offset,g=f.top,h=f.bottom;"object"!=typeof f&&(h=g=f),"function"==typeof g&&(g=f.top()),"function"==typeof h&&(h=f.bottom());var i=null!=this.unpin&&d+this.unpin<=e.top?!1:null!=h&&e.top+this.$element.height()>=c-h?"bottom":null!=g&&g>=d?"top":!1;this.affixed!==i&&(this.unpin&&this.$element.css("top",""),this.affixed=i,this.unpin="bottom"==i?e.top-d:null,this.$element.removeClass(b.RESET).addClass("affix"+(i?"-"+i:"")),"bottom"==i&&this.$element.offset({top:document.body.offsetHeight-h-this.$element.height()}))}};var c=a.fn.affix;a.fn.affix=function(c){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof c&&c;e||d.data("bs.affix",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.affix.Constructor=b,a.fn.affix.noConflict=function(){return a.fn.affix=c,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var b=a(this),c=b.data();c.offset=c.offset||{},c.offsetBottom&&(c.offset.bottom=c.offsetBottom),c.offsetTop&&(c.offset.top=c.offsetTop),b.affix(c)})})}(window.jQuery); -------------------------------------------------------------------------------- /src/views/tl-service.tpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | TabLinker 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 21 | 22 | 23 | 24 | 25 | 44 | 45 |
46 | 47 |
48 | 49 | 50 |

Supervised Excel/CSV to RDF Converter http://www.data2semantics.org

51 |
52 | 53 | %if state == 'start': 54 |
55 |
56 | 57 |
58 |

Select a CSV/Excel file previously marked-up from your disk.

59 |
60 | 61 |
62 | 63 |

64 | 65 | 66 | 67 | %for file in inFiles: 68 | 69 | 70 | 71 | %end 72 |
Input files
{{file}}
73 | 74 | 75 | %for file in outFiles: 76 | 77 | 78 | 79 | %end 80 |
Output files
{{file}}
81 | 82 | %elif state == 'uploaded': 83 |
84 |

Upload OK

85 | 86 |
87 | 88 | %else: 89 |
90 |

TabLinker generated {{numtriples}} triples successfully

91 | 92 | 93 |
94 | %end 95 | 96 |
97 | 98 |
99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | --------------------------------------------------------------------------------