├── build.xml ├── demo ├── diaspora-france │ ├── dspl.zip │ ├── french-abroad-2011-linked.rdf │ ├── french-abroad-2011.csv │ └── french-diaspora.PNG └── ratp │ ├── ratp-trafic-2011.jpg │ ├── ratp-trafic-2011.rdf │ └── ratp_trafic_annuel_2011.csv ├── doc ├── d4-workflow.JPG ├── datacube-ontology.png ├── future-data │ ├── Ponctualite_TGV_aou12.csv │ ├── Ponctualite_TGV_avr12.csv │ ├── Ponctualite_TGV_jul12.csv │ ├── Ponctualite_TGV_jun12.csv │ ├── Ponctualite_TGV_mai12.csv │ ├── Ponctualite_TGV_mar12.csv │ ├── Ponctualite_TGV_sep12.csv │ └── liste_gares_ferroviaires_DRR2012_23-11-2011.csv ├── screenshots │ ├── graph-view-bis.PNG │ ├── graph-view.PNG │ ├── metadata-view.PNG │ ├── statistics-view.PNG │ └── table-view.PNG └── slides-d4.pdf ├── live-build.xml ├── readme.md └── src ├── java └── org │ └── datalift │ └── d4 │ ├── D4Controller.java │ ├── D4Model.java │ ├── ModuleController.java │ ├── ModuleModel.java │ ├── datacube │ ├── DataSet.java │ ├── DataSetORM.java │ └── Observation.java │ └── package.html └── web ├── d4-form.vm ├── public └── static │ ├── css │ ├── DT_bootstrap.css │ ├── back_disabled.png │ ├── back_enabled.png │ ├── back_enabled_hover.png │ ├── bootstrap-responsive.css │ ├── bootstrap.css │ ├── forward_disabled.png │ ├── forward_enabled.png │ ├── forward_enabled_hover.png │ ├── glyphicons-halflings-white.png │ ├── glyphicons-halflings.png │ ├── main.css │ ├── sort_asc.png │ ├── sort_asc_disabled.png │ ├── sort_both.png │ ├── sort_desc.png │ └── sort_desc_disabled.png │ └── js │ ├── charts.js │ ├── jqgrid.common.js │ ├── main.coffee │ ├── main.js │ ├── plugins.coffee │ ├── plugins.js │ ├── stats.coffee │ ├── stats.js │ └── vendor │ ├── DT_bootstrap.js │ ├── bootstrap.js │ ├── d3.v2.js │ ├── jquery-1.8.2.js │ ├── jquery.dataTables.js │ ├── underscore.min.js │ └── underscore.string.min.js ├── resources_en.properties └── resources_fr.properties /build.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 23 | 24 | 32 | 33 | 34 | 35 | 36 | 37 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 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 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 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 | 182 | 183 | 185 | 186 | 187 | 188 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 228 | 229 | 231 | 232 | 237 | 238 | 239 | 240 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 252 | 253 | 254 | 255 | 256 | 257 | 259 | 260 | 261 | 262 | 263 | 264 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 281 | 282 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 298 | 299 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | -------------------------------------------------------------------------------- /demo/diaspora-france/dspl.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thibaudcolas/d4/992726e87a6449cc0be91c78325c79994bdc1407/demo/diaspora-france/dspl.zip -------------------------------------------------------------------------------- /demo/diaspora-france/french-abroad-2011.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thibaudcolas/d4/992726e87a6449cc0be91c78325c79994bdc1407/demo/diaspora-france/french-abroad-2011.csv -------------------------------------------------------------------------------- /demo/diaspora-france/french-diaspora.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thibaudcolas/d4/992726e87a6449cc0be91c78325c79994bdc1407/demo/diaspora-france/french-diaspora.PNG -------------------------------------------------------------------------------- /demo/ratp/ratp-trafic-2011.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thibaudcolas/d4/992726e87a6449cc0be91c78325c79994bdc1407/demo/ratp/ratp-trafic-2011.jpg -------------------------------------------------------------------------------- /demo/ratp/ratp_trafic_annuel_2011.csv: -------------------------------------------------------------------------------- 1 | ,Trafic des entrants (*) par station pour l'ensemble de l'année 2011,,,,,,,,,, 2 | ,,,,,,,,,,, 3 | ,Rang,Réseau,Station,Trafic,Ligne de correspondances RATP,,,,,Ville,Arrondissement pour Paris 4 | ,1,Métro,GARE DU NORD,48 146 629,4,5,0,0,0,Paris,10 5 | ,2,Métro,SAINT-LAZARE ,46 790 941,3,9,12,13,14,Paris,08 6 | ,3,Métro,GARE DE LYON ,34 523 049,1,14,A,,,Paris,12 7 | ,4,Métro,MONTPARNASSE-BIENVENUE,31 152 275,4,6,12,13,0,Paris,14 8 | ,5,Métro,GARE DE L'EST,19 671 320,4,5,7,0,0,Paris,10 9 | ,6,Métro,REPUBLIQUE,17 095 806,3,5,8,9,11,Paris,11 10 | ,7,Métro,BIBLIOTHEQUE,15 826 727,14,0,0,0,0,Paris,13 11 | ,8,Métro,CHATELET,14 440 964,1,4,7,11,14,Paris,01 12 | ,9,Métro,LA DEFENSE,13 853 216,1,0,0,0,0,Puteaux, 13 | ,10,Métro,LES HALLES,13 113 834,4,0,0,0,0,Paris,01 14 | ,11,Métro,HOTEL DE VILLE,12 760 823,1,11,0,0,0,Paris,04 15 | ,12,Métro,FRANKLIN D. ROOSEVELT,12 640 577,1,9,0,0,0,Paris,08 16 | ,13,Métro,BASTILLE,12 517 181,1,5,8,0,0,Paris,12 17 | ,14,Métro,OPERA,12 389 715,3,7,8,0,0,Paris,09 18 | ,15,Métro,PLACE D'ITALIE,12 168 442,5,6,7,0,0,Paris,13 19 | ,16,Métro,BELLEVILLE,11 794 952,2,11,0,0,0,Paris,10 20 | ,17,Métro,PORTE D'ORLEANS,9 598 097,4,0,0,0,0,Paris,14 21 | ,18,Métro,PALAIS-ROYAL,9 559 535,1,7,0,0,0,Paris,01 22 | ,19,Métro,NATION,9 481 963,1,2,6,9,0,Paris,12 23 | ,20,Métro,BARBES-ROCHECHOUART,9 323 474,2,4,0,0,0,Paris,18 24 | ,21,Métro,PLACE CLICHY,9 255 747,2,13,0,0,0,Paris,17 25 | ,22,Métro,SAINT-MICHEL,9 159 024,4,0,0,0,0,Paris,05 26 | ,23,Métro,GARE D'AUSTERLITZ,9 063 260,5,10,0,0,0,Paris,13 27 | ,24,Métro,PORTE DE CLIGNANCOURT,9 000 298,4,0,0,0,0,Paris,18 28 | ,25,Métro,CHARLES DE GAULLE-ETOILE,8 980 015,1,2,6,0,0,Paris,08 29 | ,26,Métro,PORTE MAILLOT,8 852 329,1,0,0,0,0,Paris,16 30 | ,27,Métro,ESPLANADE DE LA DEFENSE,8 721 277,1,0,0,0,0,Puteaux, 31 | ,28,Métro,TROCADERO,8 455 619,6,9,0,0,0,Paris,16 32 | ,29,Métro,HAVRE-CAUMARTIN,8 343 415,3,9,0,0,0,Paris,09 33 | ,30,Métro,LA MOTTE-PICQUET-GRENELLE,8 099 346,6,8,10,0,0,Paris,15 34 | ,31,Métro,BIR-HAKEIM,7 825 821,6,0,0,0,0,Paris,15 35 | ,32,Métro,CHAUSSEE D'ANTIN-LA FAYETTE,7 778 782,7,9,0,0,0,Paris,09 36 | ,33,Métro,GAMBETTA,7 576 845,3,3bis,0,0,0,Paris,20 37 | ,34,Métro,MADELEINE,7 412 746,8,12,14,0,0,Paris,08 38 | ,35,Métro,AUBERVILLIERS-PANTIN-QUATRE CHEMINS,7 356 008,7,0,0,0,0,Aubervilliers, 39 | ,36,Métro,STRASBOURG-SAINT-DENIS,7 346 063,4,8,9,0,0,Paris,02 40 | ,37,Métro,OLYMPIADES,7 245 724,14,0,0,0,0,Paris,13 41 | ,38,Métro,CRIMEE,7 183 997,7,0,0,0,0,Paris,19 42 | ,39,Métro,INVALIDES,6 931 062,8,13,0,0,0,Paris,07 43 | ,40,Métro,STALINGRAD,6 906 194,2,5,7,0,0,Paris,19 44 | ,41,Métro,LA CHAPELLE,6 903 632,2,0,0,0,0,Paris,09 45 | ,42,Métro,MAIRIE DE CLICHY,6 892 451,13,0,0,0,0,Clichy, 46 | ,43,Métro,CONCORDE,6 866 631,1,8,12,0,0,Paris,08 47 | ,44,Métro,GRANDS BOULEVARDS,6 826 718,8,9,0,0,0,Paris,09 48 | ,45,Métro,CHATEAU ROUGE,6 798 017,4,0,0,0,0,Paris,18 49 | ,46,Métro,PONT DE NEUILLY,6 794 402,1,0,0,0,0,Neuilly Sur Seine, 50 | ,47,Métro,BOBIGNY-PABLO PICASSO,6 722 182,5,0,0,0,0,Bobigny, 51 | ,48,Métro,ANVERS,6 667 509,2,0,0,0,0,Paris,18 52 | ,49,Métro,CHATILLON-MONTROUGE,6 540 563,13,0,0,0,0,Chatillon, 53 | ,50,Métro,GALLIENI,6 314 009,3,0,0,0,0,Bagnolet, 54 | ,51,Métro,MAIRIE DE MONTREUIL,6 300 355,9,0,0,0,0,Montreuil, 55 | ,52,Métro,PORTE DE VERSAILLES,6 275 922,12,0,0,0,0,Paris,15 56 | ,53,Métro,SAINT-PAUL,6 265 848,1,0,0,0,0,Paris,04 57 | ,54,Métro,ODEON,6 239 672,4,10,0,0,0,Paris,06 58 | ,55,Métro,VILLIERS,6 193 182,2,3,0,0,0,Paris,17 59 | ,56,Métro,GEORGE V,6 192 199,1,0,0,0,0,Paris,08 60 | ,57,Métro,PIGALLE,6 172 069,2,12,0,0,0,Paris,18 61 | ,58,Métro,MARCADET-POISSONNIERS,6 064 992,4,12,0,0,0,Paris,18 62 | ,59,Métro,JAURES,6 046 012,2,5,7bis,0,0,Paris,19 63 | ,60,Métro,SAINT-MANDE-TOURELLE,5 973 612,1,0,0,0,0,Saint Mandé, 64 | ,61,Métro,REAUMUR-SEBASTOPOL,5 860 077,3,4,0,0,0,Paris,03 65 | ,62,Métro,MARCEL SEMBAT,5 816 044,9,0,0,0,0,Boulogne Billancourt, 66 | ,63,Métro,CONVENTION,5 780 845,12,0,0,0,0,Paris,15 67 | ,64,Métro,MIROMESNIL,5 769 023,9,13,0,0,0,Paris,08 68 | ,65,Métro,LA COURNEUVE-8 MAI 1945,5 707 118,7,0,0,0,0,La Courneuve, 69 | ,66,Métro,VILLEJUIF-LOUIS ARAGON,5 650 185,7,0,0,0,0,Villejuif, 70 | ,67,Métro,SEVRES-BABYLONE,5 618 445,10,12,0,0,0,Paris,07 71 | ,68,Métro,BERCY,5 565 249,6,14,0,0,0,Paris,12 72 | ,69,Métro,COUR SAINT-EMILION,5 550 822,14,0,0,0,0,Paris,12 73 | ,70,Métro,REUILLY-DIDEROT,5 451 933,1,8,0,0,0,Paris,12 74 | ,71,Métro,DAUMESNIL,5 435 445,6,8,0,0,0,Paris,12 75 | ,72,Métro,LES SABLONS,5 430 088,1,0,0,0,0,Neuilly Sur Seine, 76 | ,73,Métro,BASILIQUE DE SAINT-DENIS,5 415 515,13,0,0,0,0,Saint Denis, 77 | ,74,Métro,GABRIEL PERI,5 377 830,13,0,0,0,0,Asnières, 78 | ,75,Métro,SAINT-DENIS-UNIVERSITE,5 365 746,13,0,0,0,0,Saint Denis, 79 | ,76,Métro,ALESIA,5 317 934,4,0,0,0,0,Paris,14 80 | ,77,Métro,RICHELIEU-DROUOT,5 297 670,8,9,0,0,0,Paris,09 81 | ,78,Métro,VOLTAIRE,5 273 953,9,0,0,0,0,Paris,11 82 | ,79,Métro,HOCHE,5 217 935,5,0,0,0,0,Pantin, 83 | ,80,Métro,BONNE NOUVELLE,5 196 140,8,9,0,0,0,Paris,10 84 | ,81,Métro,PASTEUR,5 089 504,6,12,0,0,0,Paris,09 85 | ,82,Métro,PONT DE LEVALLOIS-BECON,5 085 695,3,0,0,0,0,Levallois-Perret, 86 | ,83,Métro,PYRAMIDES,5 084 082,7,14,0,0,0,Paris,01 87 | ,84,Métro,CROIX DE CHAVAUX,4 978 787,9,0,0,0,0,Montreuil, 88 | ,85,Métro,PORTE DE SAINT-CLOUD,4 932 701,9,0,0,0,0,Paris,16 89 | ,86,Métro,PERE LACHAISE,4 880 201,2,3,0,0,0,Paris,20 90 | ,87,Métro,CRETEIL-PREFECTURE,4 865 573,8,0,0,0,0,Créteil, 91 | ,88,Métro,CHATEAU DE VINCENNES,4 858 849,1,0,0,0,0,Vincennes, 92 | ,89,Métro,CHATEAU D'EAU,4 842 822,4,0,0,0,0,Paris,10 93 | ,90,Métro,PORTE DE MONTREUIL,4 822 893,9,0,0,0,0,Paris,20 94 | ,91,Métro,PORTE DE BAGNOLET,4 751 655,3,0,0,0,0,Paris,20 95 | ,92,Métro,GLACIERE,4 739 497,6,0,0,0,0,Paris,13 96 | ,93,Métro,PORTE DE VANVES,4 715 395,13,0,0,0,0,Paris,14 97 | ,94,Métro,PEREIRE,4 678 888,3,0,0,0,0,Paris,17 98 | ,95,Métro,PLAISANCE,4 645 997,13,0,0,0,0,Paris,14 99 | ,96,Métro,MAIRIE DES LILAS,4 602 681,11,0,0,0,0,Les Lilas, 100 | ,97,Métro,COLONEL FABIEN,4 554 566,2,0,0,0,0,Paris,10 101 | ,98,Métro,SAINT-GERMAIN DES PRES,4 548 167,4,0,0,0,0,Paris,06 102 | ,99,Métro,GUY MOQUET,4 524 101,13,0,0,0,0,Paris,17 103 | ,100,Métro,LAUMIERE,4 518 075,5,0,0,0,0,Paris,19 104 | ,101,Métro,CHAMPS-ELYSEES-CLEMENCEAU,4 502 691,1,13,0,0,0,Paris,08 105 | ,102,Métro,LE KREMLIN-BICETRE,4 492 364,7,0,0,0,0,Kremlin-Bicêtre, 106 | ,103,Métro,RAMBUTEAU,4 491 306,11,0,0,0,0,Paris,03 107 | ,104,Métro,PORTE DE PANTIN,4 477 959,5,0,0,0,0,Paris,19 108 | ,105,Métro,ECOLE MILITAIRE,4 403 029,8,0,0,0,0,Paris,07 109 | ,106,Métro,JULES JOFFRIN,4 384 318,12,0,0,0,0,Paris,18 110 | ,107,Métro,PORTE DE VINCENNES,4 363 767,1,0,0,0,0,Paris,12 111 | ,108,Métro,PONT DE SEVRES,4 352 829,9,0,0,0,0,Boulogne Billancourt, 112 | ,109,Métro,DENFERT-ROCHEREAU,4 335 086,4,6,0,0,0,Paris,14 113 | ,110,Métro,ROBESPIERRE,4 317 097,9,0,0,0,0,Montreuil, 114 | ,111,Métro,MENILMONTANT,4 294 888,2,0,0,0,0,Paris,11 115 | ,112,Métro,BALARD,4 263 152,8,0,0,0,0,Paris,15 116 | ,113,Métro,BLANCHE,4 252 071,2,0,0,0,0,Paris,09 117 | ,114,Métro,OBERKAMPF,4 198 870,5,9,0,0,0,Paris,11 118 | ,115,Métro,MAIRIE D'ISSY,4 194 027,12,0,0,0,0,Issy Les Moulineaux, 119 | ,116,Métro,PORTE DE CLICHY,4 156 619,13,0,0,0,0,Paris,17 120 | ,117,Métro,LA MUETTE,4 112 862,9,0,0,0,0,Paris,16 121 | ,118,Métro,JUSSIEU,4 080 680,7,10,0,0,0,Paris,05 122 | ,119,Métro,ALMA-MARCEAU,4 080 301,9,0,0,0,0,Paris,08 123 | ,120,Métro,VICTOR HUGO,3 966 843,2,0,0,0,0,Paris,16 124 | ,121,Métro,TERNES,3 930 362,2,0,0,0,0,Paris,17 125 | ,122,Métro,ALEXANDRE DUMAS,3 926 266,2,0,0,0,0,Paris,11 126 | ,123,Métro,CHARONNE,3 924 516,9,0,0,0,0,Paris,12 127 | ,124,Métro,PASSY,3 918 954,6,0,0,0,0,Paris,16 128 | ,125,Métro,MALAKOFF-PLATEAU DE VANVES,3 912 036,13,0,0,0,0,Malakoff, 129 | ,126,Métro,PORTE DES LILAS,3 899 020,3bis,11,0,0,0,Paris,20 130 | ,127,Métro,CHARLES MICHELS,3 892 728,10,0,0,0,0,Paris,15 131 | ,128,Métro,OURCQ,3 887 042,5,0,0,0,0,Paris,19 132 | ,129,Métro,ARTS ET METIERS,3 859 066,3,11,0,0,0,Paris,03 133 | ,130,Métro,DUPLEIX,3 855 503,6,0,0,0,0,Paris,15 134 | ,131,Métro,VAUGIRARD,3 853 128,12,0,0,0,0,Paris,15 135 | ,132,Métro,LES GOBELINS,3 830 231,7,0,0,0,0,Paris,13 136 | ,133,Métro,LEDRU-ROLLIN,3 829 461,8,0,0,0,0,Paris,12 137 | ,134,Métro,PORTE DE SAINT-OUEN,3 814 464,13,0,0,0,0,Paris,17 138 | ,135,Métro,CENSIER-DAUBENTON,3 766 777,7,0,0,0,0,Paris,06 139 | ,136,Métro,BOULOGNE-JEAN JAURES,3 748 047,10,0,0,0,0,Boulogne Billancourt, 140 | ,137,Métro,MAIRIE DE SAINT-OUEN,3 732 408,13,0,0,0,0,Saint Ouen, 141 | ,138,Métro,ANATOLE FRANCE,3 702 732,3,0,0,0,0,Levallois-Perret, 142 | ,139,Métro,CADET,3 650 405,7,0,0,0,0,Paris,09 143 | ,140,Métro,GONCOURT,3 648 533,11,0,0,0,0,Paris,11 144 | ,141,Métro,EGLISE DE PANTIN,3 632 300,5,0,0,0,0,Pantin, 145 | ,142,Métro,TOLBIAC,3 612 890,7,0,0,0,0,Paris,13 146 | ,143,Métro,MARX DORMOY,3 586 107,12,0,0,0,0,Paris,18 147 | ,144,Métro,POISSONNIERE,3 570 062,7,0,0,0,0,Paris,10 148 | ,145,Métro,PORTE DE CHAMPERRET,3 559 682,3,0,0,0,0,Paris,17 149 | ,146,Métro,SAINT-DENIS-PORTE DE PARIS,3 517 853,13,0,0,0,0,Saint Denis, 150 | ,147,Métro,FUNICULAIRE,3 488 052,2,0,0,0,0,Paris,18 151 | ,148,Métro,FORT D'AUBERVILLIERS,3 455 421,7,0,0,0,0,Aubervilliers, 152 | ,149,Métro,PORTE DE LA CHAPELLE,3 431 552,12,0,0,0,0,Paris,18 153 | ,150,Métro,JOURDAIN,3 418 732,11,0,0,0,0,Paris,20 154 | ,151,Métro,SAINT-PLACIDE,3 404 889,4,0,0,0,0,Paris,06 155 | ,152,Métro,BROCHANT,3 401 859,13,0,0,0,0,Paris,17 156 | ,153,Métro,CORENTIN CELTON,3 385 005,12,0,0,0,0,Issy Les Moulineaux, 157 | ,154,Métro,LES COURTILLES,3 377 106,13,0,0,0,0,Asnières sur Seine, 158 | ,155,Métro,ECOLE VETERINAIRE DE MAISONS-ALFORT,3 371 386,8,0,0,0,0,Maisons -Alfort, 159 | ,156,Métro,PARMENTIER,3 357 432,3,0,0,0,0,Paris,11 160 | ,157,Métro,BOURSE,3 350 796,3,0,0,0,0,Paris,02 161 | ,158,Métro,COURONNES,3 324 335,2,0,0,0,0,Paris,11 162 | ,159,Métro,SENTIER,3 318 660,3,0,0,0,0,Paris,03 163 | ,160,Métro,PYRENEES,3 279 317,11,0,0,0,0,Paris,20 164 | ,161,Métro,PERNETY,3 275 971,13,0,0,0,0,Paris,14 165 | ,162,Métro,PLACE DES FETES,3 265 485,7bis,11,0,0,0,Paris,19 166 | ,163,Métro,LOUISE MICHEL,3 240 270,3,0,0,0,0,Levallois-Perret, 167 | ,164,Métro,GAITE,3 223 366,13,0,0,0,0,Paris,14 168 | ,165,Métro,DUROC,3 218 029,10,13,0,0,0,Paris,07 169 | ,166,Métro,PORTE DE LA VILLETTE,3 209 252,7,0,0,0,0,Paris,19 170 | ,167,Métro,CAMBRONNE,3 155 480,6,0,0,0,0,Paris,15 171 | ,168,Métro,PORTE DE CHOISY,3 116 890,7,0,0,0,0,Paris,13 172 | ,169,Métro,CRETEIL-UNIVERSITE,3 116 625,8,0,0,0,0,Créteil, 173 | ,170,Métro,CORENTIN CARIOU,3 090 833,7,0,0,0,0,Paris,19 174 | ,171,Métro,GARIBALDI,3 088 530,13,0,0,0,0,Saint Ouen, 175 | ,172,Métro,QUAI DE LA GARE,3 085 406,6,0,0,0,0,Paris,13 176 | ,173,Métro,RUE DE LA POMPE,3 081 914,9,0,0,0,0,Paris,16 177 | ,174,Métro,LAMARCK-CAULAINCOURT,3 054 528,12,0,0,0,0,Paris,18 178 | ,175,Métro,CHEVALERET,2 999 174,6,0,0,0,0,Paris,13 179 | ,176,Métro,SIMPLON,2 996 051,4,0,0,0,0,Paris,18 180 | ,177,Métro,MARAICHERS,2 986 282,9,0,0,0,0,Paris,20 181 | ,178,Métro,CHARENTON-ECOLES,2 975 899,8,0,0,0,0,Charenton, 182 | ,179,Métro,LA FOURCHE,2 975 434,13,0,0,0,0,Paris,08 183 | ,180,Métro,FAIDHERBE-CHALIGNY,2 974 641,8,0,0,0,0,Paris,12 184 | ,181,Métro,MAIRIE D'IVRY,2 973 671,7,0,0,0,0,Ivry, 185 | ,182,Métro,PLACE MONGE,2 927 184,7,0,0,0,0,Paris,06 186 | ,183,Métro,SAINT-AUGUSTIN,2 923 768,9,0,0,0,0,Paris,08 187 | ,184,Métro,BOULOGNE-PONT DE SAINT-CLOUD,2 900 015,10,0,0,0,0,Boulogne Billancourt, 188 | ,185,Métro,NOTRE-DAME-DE-LORETTE,2 849 349,12,0,0,0,0,Paris,09 189 | ,186,Métro,SAINT-MAUR,2 846 171,3,0,0,0,0,Paris,11 190 | ,187,Métro,PORTE DAUPHINE,2 840 152,2,0,0,0,0,Paris,16 191 | ,188,Métro,BOUCICAUT,2 826 775,8,0,0,0,0,Paris,15 192 | ,189,Métro,ARGENTINE,2 820 883,1,0,0,0,0,Paris,17 193 | ,190,Métro,SAINT-PHILIPPE-DU-ROULE,2 813 350,9,0,0,0,0,Paris,08 194 | ,191,Métro,SAINT-AMBROISE,2 758 716,9,0,0,0,0,Paris,11 195 | ,192,Métro,CARREFOUR PLEYEL,2 754 091,13,0,0,0,0,Saint Denis, 196 | ,193,Métro,ROME,2 747 395,2,0,0,0,0,Paris,17 197 | ,194,Métro,CLUNY LA SORBONNE,2 744 528,10,0,0,0,0,0, 198 | ,195,Métro,COMMERCE,2 700 300,8,0,0,0,0,Paris,15 199 | ,196,Métro,DUGOMMIER,2 694 014,6,0,0,0,0,Paris,12 200 | ,197,Métro,COURCELLES,2 685 096,2,0,0,0,0,Paris,17 201 | ,198,Métro,PORTE DOREE,2 675 992,8,0,0,0,0,Paris,12 202 | ,199,Métro,RUE DES BOULETS,2 669 336,9,0,0,0,0,Paris,11 203 | ,200,Métro,ETIENNE MARCEL,2 661 074,4,0,0,0,0,Paris,01 204 | ,201,Métro,BILLANCOURT,2 643 194,9,0,0,0,0,Boulogne Billancourt, 205 | ,202,Métro,NATIONALE,2 626 967,6,0,0,0,0,Paris,13 206 | ,203,Métro,JACQUES BONSERGENT,2 622 801,5,0,0,0,0,Paris,10 207 | ,204,Métro,LIBERTE,2 607 263,8,0,0,0,0,Charenton, 208 | ,205,Métro,CORVISART,2 604 292,6,0,0,0,0,Paris,13 209 | ,206,Métro,LOUVRE,2 582 023,1,0,0,0,0,Paris,01 210 | ,207,Métro,WAGRAM,2 575 735,3,0,0,0,0,Paris,17 211 | ,208,Métro,RUE DU BAC,2 563 169,12,0,0,0,0,Paris,07 212 | ,209,Métro,PORTE D'ITALIE,2 562 120,7,0,0,0,0,Paris,13 213 | ,210,Métro,MAUBERT-MUTUALITE,2 549 853,10,0,0,0,0,Paris,05 214 | ,211,Métro,VOLONTAIRES,2 547 550,12,0,0,0,0,Paris,15 215 | ,212,Métro,MALESHERBES,2 546 859,3,0,0,0,0,Paris,17 216 | ,213,Métro,ABBESSES,2 545 127,12,0,0,0,0,Paris,18 217 | ,214,Métro,BERAULT,2 531 531,1,0,0,0,0,Vincennes, 218 | ,215,Métro,LOUIS BLANC,2 523 039,7,7bis,0,0,0,Paris,10 219 | ,216,Métro,RIQUET,2 519 241,7,0,0,0,0,Paris,19 220 | ,217,Métro,TUILERIES,2 473 945,1,0,0,0,0,Paris,01 221 | ,218,Métro,JAVEL-ANDRE CITROEN,2 473 278,10,0,0,0,0,Paris,15 222 | ,219,Métro,SOLFERINO,2 449 915,12,0,0,0,0,Paris,07 223 | ,220,Métro,BREGUET-SABIN,2 448 167,5,0,0,0,0,Paris,11 224 | ,221,Métro,LOURMEL,2 441 226,8,0,0,0,0,Paris,15 225 | ,222,Métro,SAINT-SULPICE,2 432 022,4,0,0,0,0,Paris,06 226 | ,223,Métro,SEVRES-LECOURBE,2 416 242,6,0,0,0,0,Paris,15 227 | ,224,Métro,VILLEJUIF-LEO LAGRANGE,2 392 154,7,0,0,0,0,Villejuif, 228 | ,225,Métro,BEL AIR,2 369 793,6,0,0,0,0,Paris,12 229 | ,226,Métro,LES AGNETTES,2 367 677,13,0,0,0,0,Asnières sur Seine, 230 | ,227,Métro,CITE,2 364 993,4,0,0,0,0,Paris,04 231 | ,228,Métro,RANELAGH,2 315 446,9,0,0,0,0,Paris,16 232 | ,229,Métro,BOBIGNY-PANTIN-RAYMOND QUENEAU,2 251 347,5,0,0,0,0,Pantin, 233 | ,230,Métro,VAVIN,2 242 491,4,0,0,0,0,Paris,14 234 | ,231,Métro,LE PELETIER,2 235 088,7,0,0,0,0,Paris,09 235 | ,232,Métro,EDGAR QUINET,2 224 650,6,0,0,0,0,Paris,14 236 | ,233,Métro,MICHEL-ANGE-AUTEUIL,2 206 285,9,10,0,0,0,Paris,16 237 | ,234,Métro,TRINITE-D'ESTIENNE D'ORVES,2 192 391,12,0,0,0,0,Paris,09 238 | ,235,Métro,TELEGRAPHE,2 160 412,11,0,0,0,0,Paris,20 239 | ,236,Métro,MAISON BLANCHE,2 155 971,7,0,0,0,0,Paris,13 240 | ,237,Métro,BOISSIERE,2 145 714,6,0,0,0,0,Paris,16 241 | ,238,Métro,VILLEJUIF-PAUL VAILLANT-COUTURIER,2 118 126,7,0,0,0,0,Villejuif, 242 | ,239,Métro,IENA,2 115 003,9,0,0,0,0,Paris,16 243 | ,240,Métro,MICHEL BIZOT,2 111 494,8,0,0,0,0,Paris,12 244 | ,241,Métro,CRETEIL-L'ECHAT,2 109 885,8,0,0,0,0,Créteil, 245 | ,242,Métro,JASMIN,2 065 058,9,0,0,0,0,Paris,16 246 | ,243,Métro,LA TOUR-MAUBOURG,2 055 583,8,0,0,0,0,Paris,07 247 | ,244,Métro,NOTRE-DAME-DES-CHAMPS,2 030 493,12,0,0,0,0,Paris,06 248 | ,245,Métro,SAINT-MARCEL,2 023 002,5,0,0,0,0,Paris,13 249 | ,246,Métro,EXELMANS,1 985 640,9,0,0,0,0,Paris,16 250 | ,247,Métro,MAISONS-ALFORT-LES JUILLIOTTES,1 976 516,8,0,0,0,0,Maisons -Alfort, 251 | ,248,Métro,MICHEL-ANGE-MOLITOR,1 939 507,9,10,0,0,0,Paris,16 252 | ,249,Métro,SAINT-JACQUES,1 937 457,6,0,0,0,0,Paris,13 253 | ,250,Métro,FELIX FAURE,1 932 459,8,0,0,0,0,Paris,15 254 | ,251,Métro,AVRON,1 922 250,2,0,0,0,0,Paris,11 255 | ,252,Métro,MABILLON,1 915 829,10,0,0,0,0,Paris,06 256 | ,253,Métro,PORTE D'IVRY,1 903 789,7,0,0,0,0,Paris,13 257 | ,254,Métro,BUZENVAL,1 853 733,9,0,0,0,0,Paris,20 258 | ,255,Métro,SAINT-FRANCOIS-XAVIER,1 850 407,13,0,0,0,0,Paris,07 259 | ,256,Métro,MALAKOFF-RUE ETIENNE DOLET,1 846 661,13,0,0,0,0,Malakoff, 260 | ,257,Métro,PHILIPPE AUGUSTE,1 813 811,2,0,0,0,0,Paris,11 261 | ,258,Métro,MONTGALLET,1 793 225,8,0,0,0,0,Paris,12 262 | ,259,Métro,RASPAIL,1 785 546,4,6,0,0,0,Paris,14 263 | ,260,Métro,QUATRE-SEPTEMBRE,1 751 431,3,0,0,0,0,Paris,02 264 | ,261,Métro,LIEGE,1 731 063,13,0,0,0,0,Paris,08 265 | ,262,Métro,MOUTON-DUVERNET,1 690 457,4,0,0,0,0,Paris,14 266 | ,263,Métro,SAINT-SEBASTIEN-FROISSART,1 680 231,8,0,0,0,0,Paris,03 267 | ,264,Métro,FILLES DU CALVAIRE,1 663 111,8,0,0,0,0,Paris,03 268 | ,265,Métro,CARDINAL LEMOINE,1 661 863,10,0,0,0,0,Paris,05 269 | ,266,Métro,RICHARD LENOIR,1 640 016,5,0,0,0,0,Paris,11 270 | ,267,Métro,SEGUR,1 627 318,10,0,0,0,0,Paris,15 271 | ,268,Métro,PONT NEUF,1 612 517,7,0,0,0,0,Paris,04 272 | ,269,Métro,SULLY-MORLAND,1 606 960,7,0,0,0,0,Paris,04 273 | ,270,Métro,MONCEAU,1 605 664,2,0,0,0,0,Paris,17 274 | ,271,Métro,CHATEAU-LANDON,1 600 022,7,0,0,0,0,Paris,10 275 | ,272,Métro,PONT MARIE,1 595 098,7,0,0,0,0,Paris,04 276 | ,273,Métro,EUROPE,1 536 565,3,0,0,0,0,Paris,08 277 | ,274,Métro,MAISONS-ALFORT-STADE,1 523 038,8,0,0,0,0,Maisons -Alfort, 278 | ,275,Métro,VARENNE,1 517 830,13,0,0,0,0,Paris,07 279 | ,276,Métro,PICPUS,1 496 432,6,0,0,0,0,Paris,12 280 | ,277,Métro,AVENUE EMILE ZOLA,1 479 756,10,0,0,0,0,Paris,15 281 | ,278,Métro,CHEMIN VERT,1 448 436,8,0,0,0,0,Paris,11 282 | ,279,Métro,PIERRE CURIE,1 416 989,7,0,0,0,0,Ivry, 283 | ,280,Métro,MIRABEAU,1 387 272,10,0,0,0,0,Paris,16 284 | ,281,Métro,RENNES,1 307 167,12,0,0,0,0,Paris,06 285 | ,282,Métro,TEMPLE,1 268 036,3,0,0,0,0,Paris,02 286 | ,283,Métro,SAINT-GEORGES,1 254 251,12,0,0,0,0,Paris,09 287 | ,284,Métro,CAMPO-FORMIO,1 234 237,5,0,0,0,0,Paris,13 288 | ,285,Métro,QUAI DE LA RAPEE,1 201 924,5,0,0,0,0,Paris,12 289 | ,286,Métro,KLEBER,1 150 016,6,0,0,0,0,Paris,16 290 | ,287,Métro,BOTZARIS,1 096 111,7bis,0,0,0,0,Paris,19 291 | ,288,Métro,ASSEMBLEE NATIONALE,1 085 710,12,0,0,0,0,Paris,07 292 | ,289,Métro,SAINT-FARGEAU,979 912,3bis,0,0,0,0,Paris,08 293 | ,290,Métro,PORTE DE CHARENTON,957 771,8,0,0,0,0,Paris,12 294 | ,291,Métro,VANEAU,928 476,10,0,0,0,0,Paris,07 295 | ,292,Métro,FALGUIERE,926 922,12,0,0,0,0,Paris,15 296 | ,293,Métro,PORTE D'AUTEUIL,721 009,10,0,0,0,0,Paris,16 297 | ,294,Métro,DANUBE,679 746,7bis,0,0,0,0,Paris,19 298 | ,295,Métro,CHARDON-LAGACHE,636 141,10,0,0,0,0,Paris,16 299 | ,296,Métro,BOLIVAR,582 328,7bis,0,0,0,0,Paris,19 300 | ,297,Métro,BUTTES-CHAUMONT,575 182,7bis,0,0,0,0,Paris,19 301 | ,298,Métro,PRE-SAINT-GERVAIS,451 729,7bis,0,0,0,0,Paris,19 302 | ,299,Métro,CRETEIL-POINTE DU LAC,389 235,8,0,0,0,0,, 303 | ,300,Métro,PELLEPORT,364 642,3bis,0,0,0,0,Paris,20 304 | ,301,Métro,EGLISE D'AUTEUIL,178 152,10,0,0,0,0,Paris,16 305 | ,1,RER,GARE DU NORD-RER,44 596 876,B,0,0,0,0,Paris,10 306 | ,2,RER,GARE DE LYON-RER,31 495 687,A,0,0,0,0,Paris,12 307 | ,3,RER,LA DEFENSE-RER,29 783 058,A,0,0,0,0,Puteaux , 308 | ,4,RER,NANTERRE-PREFECTURE,23 052 703,A,0,0,0,0,Nanterre , 309 | ,5,RER,CHATELET-LES HALLES-RER,12 986 517,A,B,0,0,0,Paris,01 310 | ,6,RER,VAL DE FONTENAY,11 369 665,A,0,0,0,0,Fontenay-sous-Bois, 311 | ,7,RER,CHARLES DE GAULLE-ETOILE-RER,9 974 724,A,0,0,0,0,Paris,17 312 | ,8,RER,MASSY-PALAISEAU,8 479 103,B,0,0,0,0,Massy, 313 | ,9,RER,SAINT-MICHEL-NOTRE-DAME,8 410 856,B,0,0,0,0,Paris,00 314 | ,10,RER,NOISY-LE-GRAND-MONT D'EST,6 866 727,A,0,0,0,0,Noisy-le-Grand, 315 | ,11,RER,RUEIL-MALMAISON,6 615 943,A,0,0,0,0,Rueil-Malmaison , 316 | ,12,RER,ANTONY,6 372 562,B,0,0,0,0,Antony, 317 | ,13,RER,CITE UNIVERSITAIRE,6 359 072,B,0,0,0,0,Paris,14 318 | ,14,RER,VINCENNES,5 874 104,A,0,0,0,0,Vincennes, 319 | ,15,RER,LUXEMBOURG,5 838 336,B,0,0,0,0,Paris,05 320 | ,16,RER,AUBER,5 667 365,A,0,0,0,0,Paris,09 321 | ,17,RER,NANTERRE-UNIVERSITE,5 214 188,A,0,0,0,0,Nanterre , 322 | ,18,RER,NOISY-CHAMPS,4 669 805,A,0,0,0,0,Champs-sur-Marne, 323 | ,19,RER,NEUILLY-PLAISANCE,4 647 935,A,0,0,0,0,Neuilly-sur-Seine, 324 | ,20,RER,MARNE-LA-VALLEE-CHESSY,4 578 909,A,0,0,0,0,Montévrain, 325 | ,21,RER,SAINT-GERMAIN-EN-LAYE,4 383 920,A,0,0,0,0,Saint-Germain-en-Laye, 326 | ,22,RER,BOURG-LA-REINE,4 309 137,B,0,0,0,0,Bourg la Reine, 327 | ,23,RER,DENFERT-ROCHEREAU-RER,4 262 370,B,0,0,0,0,Paris,14 328 | ,24,RER,NATION-RER,4 195 903,A,0,0,0,0,Paris,12 329 | ,25,RER,TORCY,3 712 967,A,0,0,0,0,Torcy , 330 | ,26,RER,SAINT-MAUR-CRETEIL,3 626 427,A,0,0,0,0,Saint-Maur des Fossés , 331 | ,27,RER,JOINVILLE-LE-PONT,3 535 908,A,0,0,0,0,Joinville-le-Pont, 332 | ,28,RER,NANTERRE-VILLE,3 408 388,A,0,0,0,0,Nanterre , 333 | ,29,RER,CHAMPIGNY,3 279 877,A,0,0,0,0,Saint-Maur des Fossés, 334 | ,30,RER,PORT ROYAL,3 242 096,B,0,0,0,0,Paris,05 335 | ,31,RER,VAL D'EUROPE,3 161 331,A,0,0,0,0,Serris , 336 | ,32,RER,LAPLACE,3 145 389,B,0,0,0,0,Arcueil, 337 | ,33,RER,ARCUEIL-CACHAN,3 101 352,B,0,0,0,0,Arcueil, 338 | ,34,RER,LA CROIX DE BERNY,2 832 453,B,0,0,0,0,Antony, 339 | ,35,RER,NOGENT-SUR-MARNE,2 792 732,A,0,0,0,0,Nogent-sur-Marne, 340 | ,36,RER,BUSSY-SAINT-GEORGES,2 766 096,A,0,0,0,0,Bussy-Saint-Georges , 341 | ,37,RER,NOISIEL,2 564 613,A,0,0,0,0,Noisiel, 342 | ,38,RER,FONTENAY-SOUS-BOIS,2 410 731,A,0,0,0,0,Fontenay-sous-Bois, 343 | ,39,RER,ORSAY-VILLE,2 290 579,B,0,0,0,0,Orsay, 344 | ,40,RER,ROBINSON,2 262 137,B,0,0,0,0,Sceaux, 345 | ,41,RER,BOISSY-SAINT-LEGER,2 235 716,A,0,0,0,0,Boissy-Saint-Léger, 346 | ,42,RER,GENTILLY,2 092 205,B,0,0,0,0,Gentilly, 347 | ,43,RER,LE VESINET-LE PECQ,2 078 606,A,0,0,0,0,Le Vésinet , 348 | ,44,RER,CHATOU-CROISSY,2 057 141,A,0,0,0,0,Chatou , 349 | ,45,RER,LE PARC DE SAINT-MAUR,2 048 678,A,0,0,0,0,Saint-Maur des Fossés, 350 | ,46,RER,BAGNEUX,1 985 787,B,0,0,0,0,Cachan, 351 | ,47,RER,SUCY-BONNEUIL,1 895 918,A,0,0,0,0,Sucy-en-Brie , 352 | ,48,RER,BRY-SUR-MARNE,1 874 920,A,0,0,0,0,Bry-sur-Marne , 353 | ,49,RER,LA VARENNE-CHENNEVIERES,1 830 795,A,0,0,0,0,saint-Maur des Fossés , 354 | ,50,RER,LES BACONNETS,1 592 744,B,0,0,0,0,Antony, 355 | ,51,RER,LOGNES,1 571 347,A,0,0,0,0,Lognes , 356 | ,52,RER,LE VESINET-CENTRE,1 218 399,A,0,0,0,0,Le Vésinet , 357 | ,53,RER,FONTENAY-AUX-ROSES,1 028 095,B,0,0,0,0,Fontenay aux roses, 358 | ,54,RER,PALAISEAU,964 017,B,0,0,0,0,Palaiseau, 359 | ,55,RER,SAINT-REMY-LES-CHEVREUSE,911 726,B,0,0,0,0,St-Rémy-Lès-Chevreuse, 360 | ,56,RER,LE GUICHET,865 385,B,0,0,0,0,Orsay, 361 | ,57,RER,PALAISEAU-VILLEBON,865 323,B,0,0,0,0,Palaiseau, 362 | ,58,RER,LOZERE,765 952,B,0,0,0,0,Palaiseau, 363 | ,59,RER,GIF-SUR-YVETTE,717 784,B,0,0,0,0,Gif sur Yvette, 364 | ,60,RER,SCEAUX,713 823,B,0,0,0,0,sceaux, 365 | ,61,RER,BURES-SUR-YVETTE,670 230,B,0,0,0,0,Bures sur Yvette, 366 | ,62,RER,FONTAINE-MICHALON,575 331,B,0,0,0,0,Antony, 367 | ,63,RER,MASSY-VERRIERES,572 805,B,0,0,0,0,Massy, 368 | ,64,RER,COURCELLE-SUR-YVETTE,571 411,B,0,0,0,0,Gif sur Yvette, 369 | ,65,RER,PARC DE SCEAUX,523 365,B,0,0,0,0,Antony, 370 | ,66,RER,LA HACQUINIERE,425 311,B,0,0,0,0,Bures sur Yvette, 371 | ,,,,,,,,,,, 372 | ," (*) il s'agit des entrants par station qui proviennent de l'exterieur du réseau RATP (issus de la voie publique, du reseau SNCF...)",,,,,,,,,, 373 | ,"Pour certaines gares et stations, une part de ces données releve d'estimation statistiques",,,,,,,,,, 374 | -------------------------------------------------------------------------------- /doc/d4-workflow.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thibaudcolas/d4/992726e87a6449cc0be91c78325c79994bdc1407/doc/d4-workflow.JPG -------------------------------------------------------------------------------- /doc/datacube-ontology.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thibaudcolas/d4/992726e87a6449cc0be91c78325c79994bdc1407/doc/datacube-ontology.png -------------------------------------------------------------------------------- /doc/future-data/Ponctualite_TGV_aou12.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thibaudcolas/d4/992726e87a6449cc0be91c78325c79994bdc1407/doc/future-data/Ponctualite_TGV_aou12.csv -------------------------------------------------------------------------------- /doc/future-data/Ponctualite_TGV_avr12.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thibaudcolas/d4/992726e87a6449cc0be91c78325c79994bdc1407/doc/future-data/Ponctualite_TGV_avr12.csv -------------------------------------------------------------------------------- /doc/future-data/Ponctualite_TGV_jul12.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thibaudcolas/d4/992726e87a6449cc0be91c78325c79994bdc1407/doc/future-data/Ponctualite_TGV_jul12.csv -------------------------------------------------------------------------------- /doc/future-data/Ponctualite_TGV_jun12.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thibaudcolas/d4/992726e87a6449cc0be91c78325c79994bdc1407/doc/future-data/Ponctualite_TGV_jun12.csv -------------------------------------------------------------------------------- /doc/future-data/Ponctualite_TGV_mai12.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thibaudcolas/d4/992726e87a6449cc0be91c78325c79994bdc1407/doc/future-data/Ponctualite_TGV_mai12.csv -------------------------------------------------------------------------------- /doc/future-data/Ponctualite_TGV_mar12.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thibaudcolas/d4/992726e87a6449cc0be91c78325c79994bdc1407/doc/future-data/Ponctualite_TGV_mar12.csv -------------------------------------------------------------------------------- /doc/future-data/Ponctualite_TGV_sep12.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thibaudcolas/d4/992726e87a6449cc0be91c78325c79994bdc1407/doc/future-data/Ponctualite_TGV_sep12.csv -------------------------------------------------------------------------------- /doc/future-data/liste_gares_ferroviaires_DRR2012_23-11-2011.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thibaudcolas/d4/992726e87a6449cc0be91c78325c79994bdc1407/doc/future-data/liste_gares_ferroviaires_DRR2012_23-11-2011.csv -------------------------------------------------------------------------------- /doc/screenshots/graph-view-bis.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thibaudcolas/d4/992726e87a6449cc0be91c78325c79994bdc1407/doc/screenshots/graph-view-bis.PNG -------------------------------------------------------------------------------- /doc/screenshots/graph-view.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thibaudcolas/d4/992726e87a6449cc0be91c78325c79994bdc1407/doc/screenshots/graph-view.PNG -------------------------------------------------------------------------------- /doc/screenshots/metadata-view.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thibaudcolas/d4/992726e87a6449cc0be91c78325c79994bdc1407/doc/screenshots/metadata-view.PNG -------------------------------------------------------------------------------- /doc/screenshots/statistics-view.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thibaudcolas/d4/992726e87a6449cc0be91c78325c79994bdc1407/doc/screenshots/statistics-view.PNG -------------------------------------------------------------------------------- /doc/screenshots/table-view.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thibaudcolas/d4/992726e87a6449cc0be91c78325c79994bdc1407/doc/screenshots/table-view.PNG -------------------------------------------------------------------------------- /doc/slides-d4.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thibaudcolas/d4/992726e87a6449cc0be91c78325c79994bdc1407/doc/slides-d4.pdf -------------------------------------------------------------------------------- /live-build.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 35 | 36 | 37 | 38 | 39 | 40 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 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 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # D4 2 | 3 | > DataLift, DataCube, D3 4 | 5 | ## tl;dr 6 | 7 | > D4 is a DataLift module to visualize RDF DataCube data. 8 | > 9 | > D4 is made with Twitter Bootstrap, D3.js, Underscore and CoffeeScript. 10 | 11 | ## Functionalities 12 | 13 | ### Metadata visualization 14 | 15 | D4's main interface displays the datasets' metadata : a previsouly defined set of metadata properties are retrieved via SPARQL and displayed according to a predefined scheme. Each property is translated to the user's language if available and so does the interface. 16 | 17 | ![Metadata view](doc/screenshots/metadata-view.PNG) 18 | 19 | We believe metadata availability is especially important in the case of statistical datasets to identify the data's origin and data retrieval methodology. 20 | 21 | D4 uses the Underscore.js library to format its String and number values according to the user's language. 22 | 23 | ### Data Table view 24 | 25 | A classic table displays all of the observations in a spreadsheet-like style. Rows can be ordered according to their cells' values, the user can search for values in the data, thus being able to see each data item individually if necessary. 26 | 27 | ![Table view](doc/screenshots/table-view.PNG) 28 | 29 | This view is necessary to be able to look for the greatest / lowest values and to be able to visualize every piece of data in a format that every user will know how to use. 30 | 31 | D4 uses the DataTables jQuery plugin to manage its table. 32 | 33 | ### Graph view 34 | 35 | Here, (at the moment) we display a pie chart ("camembert") which the summarizes all of the dataset's view. This rather simple graph has one main advantage : first, it is easily comprehensible by anyone looking at it, because you can understand the data distribution (size, patterns, outliers) really quickly. It is however also limited in a number of ways and has to be taken much more as a proof of concept rather than a polished result. 36 | 37 | ![Graph view](doc/screenshots/graph-view.PNG) 38 | 39 | ![Graph view bis](doc/screenshots/graph-view-bis.PNG) 40 | 41 | This really is one of the most important part of data visualisation : the ability to overview big data in small representations, to understand how the data is distributed without having to look at all the data items. 42 | 43 | D4 uses the D3.js library which creates graphs via SVG to display its pie chart. 44 | 45 | ### Statistic measures 46 | 47 | Here are displayed the most used statistic dispersion and position measures : the mean, the mode, the median, the variance, the standard deviation, the range, minimum and maximum values. Those measures aren't always available for all of the datasets, nor useful for some of them, but can be easily understood by beginner statisticians. 48 | 49 | ![Statistics view](doc/screenshots/statistics-view.PNG) 50 | 51 | Being able to create knowledge by processing data is amazing, and the modern deluge of information makes this automatic processing a necessity. Our module doesn't go as far as could be but this view is a statement that this kind of processing is needed for a good visualisation / data analysis tool. 52 | 53 | D4's statistical values are calculated using custom functions written in CoffeeScript, of course with well known formulas. 54 | 55 | ## Example datasets 56 | 57 | ### RATP 2011 trafic 58 | 59 | This dataset is from the new (as of november 2012) http://data.ratp.fr/ data portal. It contains the number of travelers entering each of RATP's 367 RER and metro stations during 2011. 60 | 61 | It is available on the RATP data portal as a CSV with metadata in an joint PDF file. It has been converted to DataCube RDF using Google Refine and DataLift. 62 | 63 | ### French diaspora 64 | 65 | This dataset is from the French Open Data portal (http://data.gouv.fr/). It counts the number of french people living in each of the world's countries. 66 | 67 | It is available as a CSV file and has been converted to DataCube RDF with Google Refine, Datalift, and then interlinked to DBpedia's countries using StringToURI. 68 | 69 | Before being used here, this dataset has been converted to Google's DSPL in order to try out this format's capabilities and Google's Public Data Explorer (https://www.google.com/publicdata/directory). 70 | The data has been put on a map where the number of french people in each country is displayed as a blue circle, the bigger the circle the more french people. 71 | 72 | ## Module architecture 73 | 74 | ### General workflow 75 | 76 | ![D4 Workflow](http://i.imgur.com/f8I4K.jpg) 77 | 78 | ### Back-end 79 | 80 | D4 is backed by DataLift, thus made with JEE, Jersey, Empire and a Sesame triple store. D4 retrieves the datasets' data via SPARQL , uses a little ORM-like translation and sends DataCube DataSets to its views which use Velocity. 81 | 82 | ### Front-end 83 | 84 | Data is included with Velocity and rendered on an HTML template written with Twitter Bootstrap. Graphs are made of SVG generated by D3.js, and the whole JavaScript code actually is compiled CoffeeScript. 85 | 86 | ## Installation 87 | 88 | You'll need DataLift's source code. 89 | 90 | - Put the d4 module directory inside of datalift's module source directory. 91 | - Add d4 to the list of modules in the main build.xml file. 92 | - ant dist 93 | - Put the resulting modules into DataLift's module directory. 94 | 95 | ## Development 96 | 97 | An alternative live-build.xml file is provided to fasten the development process. It is also worth pointing out that it is better to modify the .coffee files and recompile them with coffee -c rather than modify the JavaScript. 98 | 99 | ### Further improvements 100 | 101 | + Handle much more dataset types 102 | ++ There is a good example of interesting data to handle in the doc/ folder. 103 | + Correct the variance's formula 104 | + Add new charts / graphs. 105 | + Better process for alternative metadata properties 106 | 107 | ## External resources 108 | 109 | * https://github.com/thibaudcolas/d4 110 | * http://datalift.org 111 | * http://d3js.org/ 112 | * http://datatables.net/ 113 | * http://coffeescript.org/ 114 | * http://twitter.github.com/bootstrap/ 115 | * http://underscorejs.org/ 116 | * https://www.google.com/publicdata/directory 117 | -------------------------------------------------------------------------------- /src/java/org/datalift/d4/D4Controller.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright / LIRMM 2012 3 | * Contributor(s) : T. Colas, F. Scharffe 4 | * 5 | * Contact: thibaud.colas@etud.univ-montp2.fr 6 | * 7 | * This software is governed by the CeCILL license under French law and 8 | * abiding by the rules of distribution of free software. You can use, 9 | * modify and/or redistribute the software under the terms of the CeCILL 10 | * license as circulated by CEA, CNRS and INRIA at the following URL 11 | * "http://www.cecill.info". 12 | * 13 | * As a counterpart to the access to the source code and rights to copy, 14 | * modify and redistribute granted by the license, users are provided only 15 | * with a limited warranty and the software's author, the holder of the 16 | * economic rights, and the successive licensors have only limited 17 | * liability. 18 | * 19 | * In this respect, the user's attention is drawn to the risks associated 20 | * with loading, using, modifying and/or developing or reproducing the 21 | * software by the user in light of its specific status of free software, 22 | * that may mean that it is complicated to manipulate, and that also 23 | * therefore means that it is reserved for developers and experienced 24 | * professionals having in-depth computer knowledge. Users are therefore 25 | * encouraged to load and test the software's suitability as regards their 26 | * requirements in conditions enabling the security of their systems and/or 27 | * data to be ensured and, more generally, to use and operate it in the 28 | * same conditions as regards security. 29 | * 30 | * The fact that you are presently reading this means that you have had 31 | * knowledge of the CeCILL license and that you accept its terms. 32 | */ 33 | 34 | package org.datalift.d4; 35 | 36 | import java.io.ObjectStreamException; 37 | import java.net.URI; 38 | import java.net.URISyntaxException; 39 | import java.util.HashMap; 40 | import java.util.LinkedList; 41 | 42 | import javax.ws.rs.GET; 43 | import javax.ws.rs.Path; 44 | import javax.ws.rs.Produces; 45 | import javax.ws.rs.QueryParam; 46 | import javax.ws.rs.core.MediaType; 47 | import javax.ws.rs.core.Response; 48 | 49 | import org.datalift.d4.datacube.DataSet; 50 | import org.datalift.fwk.project.Project; 51 | 52 | /** 53 | * D4 main controller class, making its model and view cooperate. 54 | * Exposes /d4 as D4's main URL. 55 | * 56 | * @author tcolas 57 | * @version 071112 58 | */ 59 | @Path(D4Controller.MODULE_NAME) 60 | public class D4Controller extends ModuleController { 61 | //------------------------------------------------------------------------- 62 | // Constants 63 | //------------------------------------------------------------------------- 64 | 65 | /** The module's name. */ 66 | public static final String MODULE_NAME = "d4"; 67 | 68 | //------------------------------------------------------------------------- 69 | // Instance members 70 | //------------------------------------------------------------------------- 71 | 72 | /** The module's back-end logic handler. */ 73 | protected final D4Model model; 74 | 75 | //------------------------------------------------------------------------- 76 | // Constructors 77 | //------------------------------------------------------------------------- 78 | 79 | /** 80 | * Creates a new InterconnectionController instance. 81 | */ 82 | public D4Controller() { 83 | //TODO Switch to the right position. 84 | super(MODULE_NAME, 13371337); 85 | 86 | label = getTranslatedResource(MODULE_NAME + ".button"); 87 | model = new D4Model(MODULE_NAME); 88 | } 89 | 90 | //------------------------------------------------------------------------- 91 | // Project management 92 | //------------------------------------------------------------------------- 93 | 94 | /** 95 | * Tells the project manager to add a new button to projects with at least 96 | * two sources. 97 | * @param p Our current project. 98 | * @return The URI to our project's main page. 99 | */ 100 | public final UriDesc canHandle(Project p) { 101 | UriDesc uridesc = null; 102 | 103 | try { 104 | // The project can be handled if it has at least one RDF source. 105 | if (model.hasMultipleRDFSources(p, 1)) { 106 | // link URL, link label 107 | uridesc = new UriDesc(this.getName() + "?project=" + p.getUri(), this.label); 108 | 109 | if (this.position > 0) { 110 | uridesc.setPosition(this.position); 111 | } 112 | LOG.debug("Project {} can use D4", p.getTitle()); 113 | } 114 | else { 115 | LOG.debug("Project {} can not use D4", p.getTitle()); 116 | } 117 | } 118 | catch (URISyntaxException e) { 119 | LOG.fatal("Uh!", e); 120 | throw new RuntimeException(e); 121 | } 122 | return uridesc; 123 | } 124 | 125 | //------------------------------------------------------------------------- 126 | // Web services 127 | //------------------------------------------------------------------------- 128 | 129 | /** 130 | * Index page handler of the D4 module. 131 | * @param projectId the project using D4 132 | * @return Our module's interface. 133 | * @throws ObjectStreamException 134 | */ 135 | @GET 136 | @Produces(MediaType.TEXT_HTML) 137 | public Response getIndexPage(@QueryParam("project") URI projectId) throws ObjectStreamException { 138 | // Retrieve the current project and its sources. 139 | Project proj = this.getProject(projectId); 140 | LinkedList datasets = model.getProjectDataSets(proj); 141 | 142 | HashMap args = new HashMap(); 143 | args.put("it", proj); 144 | 145 | args.put("datasets", datasets); 146 | 147 | return Response.ok(this.newViewable("/d4-form.vm", args)).build(); 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /src/java/org/datalift/d4/D4Model.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright / LIRMM 2012 3 | * Contributor(s) : T. Colas, F. Scharffe 4 | * 5 | * Contact: thibaud.colas@etud.univ-montp2.fr 6 | * 7 | * This software is governed by the CeCILL license under French law and 8 | * abiding by the rules of distribution of free software. You can use, 9 | * modify and/or redistribute the software under the terms of the CeCILL 10 | * license as circulated by CEA, CNRS and INRIA at the following URL 11 | * "http://www.cecill.info". 12 | * 13 | * As a counterpart to the access to the source code and rights to copy, 14 | * modify and redistribute granted by the license, users are provided only 15 | * with a limited warranty and the software's author, the holder of the 16 | * economic rights, and the successive licensors have only limited 17 | * liability. 18 | * 19 | * In this respect, the user's attention is drawn to the risks associated 20 | * with loading, using, modifying and/or developing or reproducing the 21 | * software by the user in light of its specific status of free software, 22 | * that may mean that it is complicated to manipulate, and that also 23 | * therefore means that it is reserved for developers and experienced 24 | * professionals having in-depth computer knowledge. Users are therefore 25 | * encouraged to load and test the software's suitability as regards their 26 | * requirements in conditions enabling the security of their systems and/or 27 | * data to be ensured and, more generally, to use and operate it in the 28 | * same conditions as regards security. 29 | * 30 | * The fact that you are presently reading this means that you have had 31 | * knowledge of the CeCILL license and that you accept its terms. 32 | */ 33 | 34 | package org.datalift.d4; 35 | 36 | import java.util.LinkedList; 37 | 38 | import org.datalift.d4.datacube.DataSet; 39 | import org.datalift.d4.datacube.DataSetORM; 40 | import org.datalift.fwk.project.Project; 41 | import org.datalift.fwk.project.Source; 42 | import org.datalift.fwk.project.Source.SourceType; 43 | import org.datalift.fwk.project.TransformedRdfSource; 44 | 45 | /** 46 | * D4's backend main class. 47 | * Handles DataSets' retrieval and project compatibility check. 48 | * 49 | * @author tcolas 50 | * @version 071112 51 | */ 52 | public class D4Model extends ModuleModel { 53 | //------------------------------------------------------------------------- 54 | // Constructors 55 | //------------------------------------------------------------------------- 56 | 57 | /** 58 | * Creates a new D4Model instance. 59 | * @param name Name of the module. 60 | */ 61 | public D4Model(String name) { 62 | super(name); 63 | } 64 | 65 | //------------------------------------------------------------------------- 66 | // Sources management. 67 | //------------------------------------------------------------------------- 68 | 69 | /** 70 | * Checks if a given {@link Source} contains valid RDF-structured data 71 | * AND DataCube data. 72 | * @param src The source to check. 73 | * @return True if src is {@link TransformedRdfSource} and contains QB. 74 | */ 75 | protected boolean isValidSource(Source src) { 76 | return src.getType() == SourceType.TransformedRdfSource 77 | && containsDataCube(src); 78 | } 79 | 80 | /** 81 | * Checks if a given {@link Source} contains DataCube data. 82 | * @param src The source to check. 83 | * @return True if src contains at least one qb:Dataset. 84 | */ 85 | protected boolean containsDataCube(Source src) { 86 | String query = writeQuery(src.getUri(), "{?d a }", "d"); 87 | LinkedList ret = selectQuery(query, "d"); 88 | 89 | return !ret.isEmpty(); 90 | } 91 | 92 | /** 93 | * Retrieves all of the DataCube DataSets from a given project's sources. 94 | */ 95 | public LinkedList getProjectDataSets(Project proj) { 96 | LinkedList ret = new LinkedList(); 97 | DataSetORM datasetMapper; 98 | for (Source src : proj.getSources()) { 99 | if (isValidSource(src)) { 100 | datasetMapper = new DataSetORM(src.getUri(), getTranslatedResource("page.lang")); 101 | ret.add(datasetMapper.getDataSet()); 102 | } 103 | } 104 | 105 | return ret; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/java/org/datalift/d4/ModuleController.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright / LIRMM 2011-2012 3 | * Contributor(s) : T. Colas, F. Scharffe 4 | * 5 | * Contact: thibaud.colas@etud.univ-montp2.fr 6 | */ 7 | 8 | package org.datalift.d4; 9 | 10 | import static javax.ws.rs.core.HttpHeaders.ACCEPT; 11 | 12 | import java.io.ObjectStreamException; 13 | import java.net.URI; 14 | 15 | import javax.ws.rs.GET; 16 | import javax.ws.rs.HeaderParam; 17 | import javax.ws.rs.Path; 18 | import javax.ws.rs.PathParam; 19 | import javax.ws.rs.QueryParam; 20 | import javax.ws.rs.core.Context; 21 | import javax.ws.rs.core.Request; 22 | import javax.ws.rs.core.Response; 23 | import javax.ws.rs.core.UriInfo; 24 | 25 | import org.datalift.fwk.BaseModule; 26 | import org.datalift.fwk.Configuration; 27 | import org.datalift.fwk.ResourceResolver; 28 | import org.datalift.fwk.i18n.PreferredLocales; 29 | import org.datalift.fwk.log.Logger; 30 | import org.datalift.fwk.project.Project; 31 | import org.datalift.fwk.project.ProjectManager; 32 | import org.datalift.fwk.project.ProjectModule; 33 | import org.datalift.fwk.view.TemplateModel; 34 | import org.datalift.fwk.view.ViewFactory; 35 | 36 | /** 37 | * An abstract class for all of the interlinking modules, combining default 38 | * operations and values. 39 | * 40 | * @author tcolas 41 | * @version 071112 42 | */ 43 | public abstract class ModuleController extends BaseModule implements ProjectModule { 44 | 45 | //------------------------------------------------------------------------- 46 | // Constants 47 | //------------------------------------------------------------------------- 48 | 49 | /** Base name of the resource bundle for converter GUI. */ 50 | protected static final String GUI_RESOURCES_BUNDLE = "resources"; 51 | 52 | //------------------------------------------------------------------------- 53 | // Class members 54 | //------------------------------------------------------------------------- 55 | 56 | /** Datalift's logger. */ 57 | protected static final Logger LOG = Logger.getLogger(); 58 | 59 | //------------------------------------------------------------------------- 60 | // Instance members 61 | //------------------------------------------------------------------------- 62 | 63 | /** The requested module position in menu. */ 64 | protected int position; 65 | /** The requested module label in menu. */ 66 | protected String label; 67 | 68 | //------------------------------------------------------------------------- 69 | // Constructors 70 | //------------------------------------------------------------------------- 71 | 72 | /** 73 | * InterlinkingController with default behavior. 74 | * @param name Name of the module. 75 | * @param pos Position of the module's button. 76 | */ 77 | public ModuleController(String name, int pos) { 78 | super(name); 79 | 80 | position = pos; 81 | } 82 | 83 | //------------------------------------------------------------------------- 84 | // Project management 85 | //------------------------------------------------------------------------- 86 | 87 | /** 88 | * Resource getter. 89 | * @param key The key to retrieve. 90 | * @return The value of key. 91 | */ 92 | protected String getTranslatedResource(String key) { 93 | return PreferredLocales.get().getBundle(GUI_RESOURCES_BUNDLE, ModuleController.class).getString(key); 94 | } 95 | 96 | /** 97 | * Retrieves a {@link Project} using its URI. 98 | * @param projuri the project URI. 99 | * 100 | * @return the project. 101 | * @throws ObjectStreamException if the project does not exist. 102 | */ 103 | protected final Project getProject(URI projuri) throws ObjectStreamException { 104 | ProjectManager pm = Configuration.getDefault().getBean(ProjectManager.class); 105 | Project p = pm.findProject(projuri); 106 | 107 | return p; 108 | } 109 | 110 | /** 111 | * Handles our Velocity templates. 112 | * @param templateName Name of the template to parse. 113 | * @param it Parameters for the template. 114 | * @return A new viewable in Velocity Template Language 115 | */ 116 | protected final TemplateModel newViewable(String templateName, Object it) { 117 | return ViewFactory.newView("/" + this.getName() + templateName, it); 118 | } 119 | 120 | /** 121 | * Tells the project manager to add a new button to projects with at least 122 | * two sources. 123 | * @param p Our current project. 124 | * @return The URI to our project's main page. 125 | */ 126 | public abstract UriDesc canHandle(Project p); 127 | 128 | //------------------------------------------------------------------------- 129 | // Web services 130 | //------------------------------------------------------------------------- 131 | 132 | /** 133 | * Index page handler for interlinking modules. 134 | * @param projectId the project using our module. 135 | * @return Our module's interface. 136 | * @throws ObjectStreamException A Obscene Reject Mixt Opt. 137 | */ 138 | public abstract Response getIndexPage(@QueryParam("project") URI projectId) throws ObjectStreamException; 139 | 140 | /** 141 | * Traps accesses to module static resources and redirect them 142 | * toward the default {@link ResourceResolver} for resolution. 143 | * @param path the relative path of the module static 144 | * resource being accessed. 145 | * @param uriInfo the request URI data (injected). 146 | * @param request the JAX-RS request object (injected). 147 | * @param acceptHdr the HTTP "Accept" header value. 148 | * 149 | * @return a {@link Response JAX-RS response} to download the 150 | * content of the specified public resource. 151 | */ 152 | @GET 153 | @Path("static/{path: .*$}") 154 | public Object getStaticResource(@PathParam("path") String path, 155 | @Context UriInfo uriInfo, 156 | @Context Request request, 157 | @HeaderParam(ACCEPT) String acceptHdr) { 158 | return Configuration.getDefault() 159 | .getBean(ResourceResolver.class) 160 | .resolveModuleResource(this.getName(), 161 | uriInfo, request, acceptHdr); 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /src/java/org/datalift/d4/ModuleModel.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright / LIRMM 2011-2012 3 | * Contributor(s) : T. Colas, F. Scharffe 4 | * 5 | * Contact: thibaud.colas@etud.univ-montp2.fr 6 | */ 7 | 8 | package org.datalift.d4; 9 | 10 | import java.util.Iterator; 11 | import java.util.LinkedList; 12 | 13 | import org.datalift.fwk.Configuration; 14 | import org.datalift.fwk.i18n.PreferredLocales; 15 | import org.datalift.fwk.log.Logger; 16 | import org.datalift.fwk.project.Project; 17 | import org.datalift.fwk.project.Source; 18 | import org.datalift.fwk.project.SparqlSource; 19 | import org.datalift.fwk.project.TransformedRdfSource; 20 | import org.datalift.fwk.rdf.Repository; 21 | import org.openrdf.query.MalformedQueryException; 22 | import org.openrdf.query.QueryEvaluationException; 23 | import org.openrdf.query.QueryLanguage; 24 | import org.openrdf.query.TupleQuery; 25 | import org.openrdf.query.TupleQueryResult; 26 | import org.openrdf.repository.RepositoryConnection; 27 | import org.openrdf.repository.RepositoryException; 28 | 29 | /** 30 | * An abstract class for some modules, combining default 31 | * operations and values. 32 | * 33 | * @author tcolas 34 | * @version 071112 35 | */ 36 | public abstract class ModuleModel { 37 | 38 | //------------------------------------------------------------------------- 39 | // Constants 40 | //------------------------------------------------------------------------- 41 | 42 | /** Base name of the resource bundle for converter GUI. */ 43 | protected static String GUI_RESOURCES_BUNDLE = ModuleController.GUI_RESOURCES_BUNDLE; 44 | 45 | //------------------------------------------------------------------------- 46 | // Class members 47 | //------------------------------------------------------------------------- 48 | 49 | /** Datalift's internal Sesame {@link Repository repository}. **/ 50 | protected static final Repository INTERNAL_REPO = Configuration.getDefault().getInternalRepository(); 51 | /** Datalift's internal Sesame {@link Repository repository} URL. */ 52 | protected static final String INTERNAL_URL = INTERNAL_REPO.getEndpointUrl(); 53 | /** Datalift's logging system. */ 54 | protected static final Logger LOG = Logger.getLogger(); 55 | 56 | //------------------------------------------------------------------------- 57 | // Instance members 58 | //------------------------------------------------------------------------- 59 | 60 | /** The module name. */ 61 | protected final String moduleName; 62 | 63 | //------------------------------------------------------------------------- 64 | // Constructors 65 | //------------------------------------------------------------------------- 66 | 67 | /** 68 | * Creates a new InterconnectionModel instance. 69 | * @param module Name of the module. 70 | */ 71 | public ModuleModel(String module) { 72 | this.moduleName = module; 73 | } 74 | 75 | /** 76 | * Resource getter. 77 | * @param key The key to retrieve. 78 | * @return The value of key. 79 | */ 80 | protected String getTranslatedResource(String key) { 81 | return PreferredLocales.get().getBundle(GUI_RESOURCES_BUNDLE, ModuleModel.class).getString(key); 82 | } 83 | 84 | //------------------------------------------------------------------------- 85 | // Sources management. 86 | //------------------------------------------------------------------------- 87 | 88 | /** 89 | * Checks if a given {@link Source} is valid for our uses. 90 | * @param src The source to check. 91 | * @return True if src is {@link TransformedRdfSource} or {@link SparqlSource}. 92 | */ 93 | protected abstract boolean isValidSource(Source src); 94 | 95 | /** 96 | * Checks if a {@link Project proj} contains valid RDF sources. 97 | * @param proj The project to check. 98 | * @param minvalid The number of RDF sources we want to have. 99 | * @return True if there are more than number valid sources. 100 | */ 101 | protected final boolean hasMultipleRDFSources(Project proj, int minvalid) { 102 | int cpt = 0; 103 | Iterator sources = proj.getSources().iterator(); 104 | 105 | while (sources.hasNext() && cpt < minvalid) { 106 | if (isValidSource(sources.next())) { 107 | cpt++; 108 | } 109 | } 110 | return cpt >= minvalid; 111 | } 112 | 113 | /** 114 | * Returns all of the URIs (as strings) from the {@link Project project}. 115 | * @param proj The project to use. 116 | * @return A LinkedList containing source file's URIs as strings. 117 | */ 118 | protected final LinkedList getSourcesURIs(Project proj) { 119 | LinkedList ret = new LinkedList(); 120 | 121 | for (Source src : proj.getSources()) { 122 | if (isValidSource(src)) { 123 | ret.add(src.getUri()); 124 | } 125 | } 126 | return ret; 127 | } 128 | 129 | //------------------------------------------------------------------------- 130 | // Queries management. 131 | //------------------------------------------------------------------------- 132 | 133 | /** 134 | * Tels if the bindings of the results are well-formed. 135 | * @param tqr The result of a SPARQL query. 136 | * @param bind The result one and only binding. 137 | * @return True if the results contains only bind. 138 | * @throws QueryEvaluationException Error while closing the result. 139 | */ 140 | protected boolean hasCorrectBindingNames(TupleQueryResult tqr, String bind) throws QueryEvaluationException { 141 | return tqr.getBindingNames().size() == 1 && tqr.getBindingNames().contains(bind); 142 | } 143 | 144 | /** 145 | * Sends and evaluates a SPARQL select query on the data set, then returns 146 | * the results (which must be one-column only) as a list of Strings. 147 | * @param query The SPARQL query without its prefixes. 148 | * @param bind The result one and only binding. 149 | * @return The query's result as a list of Strings. 150 | */ 151 | protected LinkedList selectQuery(String query, String bind) { 152 | TupleQuery tq; 153 | TupleQueryResult tqr; 154 | LinkedList ret = new LinkedList(); 155 | 156 | LOG.debug("Processing query: \"{}\"", query); 157 | RepositoryConnection cnx = INTERNAL_REPO.newConnection(); 158 | try { 159 | tq = cnx.prepareTupleQuery(QueryLanguage.SPARQL, query); 160 | tqr = tq.evaluate(); 161 | 162 | if (!hasCorrectBindingNames(tqr, bind)) { 163 | throw new MalformedQueryException("Wrong query result bindings - " + query); 164 | } 165 | 166 | while (tqr.hasNext()) { 167 | ret.add(tqr.next().getValue(bind).stringValue()); 168 | } 169 | } 170 | catch (MalformedQueryException e) { 171 | LOG.fatal("Failed to process query \"{}\":", e, query); 172 | } catch (QueryEvaluationException e) { 173 | LOG.fatal("Failed to process query \"{}\":", e, query); 174 | } catch (RepositoryException e) { 175 | LOG.fatal("Failed to process query \"{}\":", e, query); 176 | } 177 | finally { 178 | try { cnx.close(); } catch (Exception e) { /* Ignore... */ } 179 | } 180 | return ret; 181 | } 182 | 183 | /** 184 | * Writes a query given a bind to retrieve, a context and a WHERE clause. 185 | * @param context Context on which the query will be executed. 186 | * @param where Constraints given by the query. 187 | * @param bind Binding to use to retrieve data. 188 | * @return A full query. 189 | */ 190 | protected String writeQuery(String context, String where, String bind) { 191 | String ret = "SELECT DISTINCT ?" + bind 192 | + (context.isEmpty() ? "" : " FROM <" + context + ">") 193 | + " WHERE " + where; 194 | return ret; 195 | } 196 | -------------------------------------------------------------------------------- /src/java/org/datalift/d4/datacube/DataSet.java: -------------------------------------------------------------------------------- 1 | package org.datalift.d4.datacube; 2 | 3 | import java.util.LinkedList; 4 | 5 | import org.datalift.fwk.log.Logger; 6 | 7 | /** 8 | * Objects of this class represent a DataCube DataSet with its metadata and observations. 9 | */ 10 | public class DataSet { 11 | 12 | private String graph; 13 | private String uri; 14 | private String title; 15 | 16 | private String nbTriples; 17 | 18 | private String description; 19 | private String comment; 20 | private String publisherName; 21 | private String publisherURL; 22 | private String depictionURL; 23 | private String coverageName; 24 | private String coverageURL; 25 | private String licenseName; 26 | private String licenseURL; 27 | private String formatName; 28 | private String formatURL; 29 | private String sourceName; 30 | private String sourceURL; 31 | private String modificationDate; 32 | private String releaseDate; 33 | private String periodicity; 34 | private String unitMeasureName; 35 | private String unitMeasure; 36 | 37 | private String dimensionName; 38 | private String dimensionURI; 39 | private String measureName; 40 | private String measureURI; 41 | 42 | private String extractDim; 43 | private String extractMeas; 44 | private String extractDimBis; 45 | private String extractMeasBis; 46 | 47 | private String structureName; 48 | 49 | public LinkedList observations; 50 | 51 | /** Datalift's logging system. */ 52 | protected static final Logger LOG = Logger.getLogger(); 53 | 54 | public DataSet() { 55 | observations = new LinkedList(); 56 | } 57 | 58 | public String getGraph() { 59 | return graph; 60 | } 61 | 62 | public void setGraph(String graph) { 63 | this.graph = (graph == null || graph.equals("") ? "N/A" : graph); 64 | } 65 | 66 | public String getUri() { 67 | return uri; 68 | } 69 | 70 | public void setUri(String uri) { 71 | this.uri = (uri == null || uri.equals("") ? "N/A" : uri); 72 | } 73 | 74 | public String getTitle() { 75 | return title; 76 | } 77 | 78 | public void setTitle(String title) { 79 | this.title = (title == null || title.equals("") ? "N/A" : title); 80 | } 81 | 82 | public String getNbTriples() { 83 | return nbTriples; 84 | } 85 | 86 | public String getDescription() { 87 | return description; 88 | } 89 | 90 | public void setDescription(String description) { 91 | this.description = (description == null || description.equals("") ? "N/A" : description); 92 | } 93 | 94 | public String getComment() { 95 | return comment; 96 | } 97 | 98 | public void setComment(String comment) { 99 | this.comment = (comment == null || comment.equals("") ? "" : comment); 100 | } 101 | 102 | public String getPublisherName() { 103 | return publisherName; 104 | } 105 | 106 | public void setPublisherName(String publisherName) { 107 | this.publisherName = (publisherName == null || publisherName.equals("") ? "N/A" : publisherName); 108 | } 109 | 110 | public String getPublisherURL() { 111 | return publisherURL; 112 | } 113 | 114 | public void setPublisherURL(String publisherURL) { 115 | this.publisherURL = (publisherURL == null || publisherURL.equals("") ? "N/A" : publisherURL); 116 | } 117 | 118 | public String getDepictionURL() { 119 | return depictionURL; 120 | } 121 | 122 | public void setDepictionURL(String depictionURL) { 123 | this.depictionURL = (depictionURL == null || depictionURL.equals("") ? "http://placekitten.com/g/300/200" : depictionURL); 124 | } 125 | 126 | public String getCoverageName() { 127 | return coverageName; 128 | } 129 | 130 | public void setCoverageName(String coverageName) { 131 | this.coverageName = (coverageName == null || coverageName.equals("") ? "N/A" : coverageName); 132 | } 133 | 134 | public String getCoverageURL() { 135 | return coverageURL; 136 | } 137 | 138 | public void setCoverageURL(String coverageURL) { 139 | this.coverageURL = (coverageURL == null || coverageURL.equals("") ? "N/A" : coverageURL); 140 | } 141 | 142 | public String getLicenseName() { 143 | return licenseName; 144 | } 145 | 146 | public void setLicenseName(String licenseName) { 147 | this.licenseName = (licenseName == null || licenseName.equals("") ? "N/A" : licenseName); 148 | } 149 | 150 | public String getLicenseURL() { 151 | return licenseURL; 152 | } 153 | 154 | public void setLicenseURL(String licenseURL) { 155 | this.licenseURL = (licenseURL == null || licenseURL.equals("") ? "N/A" : licenseURL); 156 | } 157 | 158 | public String getFormatName() { 159 | return formatName; 160 | } 161 | 162 | public void setFormatName(String formatName) { 163 | this.formatName = (formatName == null || formatName.equals("") ? "N/A" : formatName); 164 | } 165 | 166 | public String getFormatURL() { 167 | return formatURL; 168 | } 169 | 170 | public void setFormatURL(String formatURL) { 171 | this.formatURL = (formatURL == null || formatURL.equals("") ? "N/A" : formatURL); 172 | } 173 | 174 | public String getSourceName() { 175 | return sourceName; 176 | } 177 | 178 | public void setSourceName(String sourceName) { 179 | this.sourceName = (sourceName == null || sourceName.equals("") ? "N/A" : sourceName); 180 | } 181 | 182 | public String getSourceURL() { 183 | return sourceURL; 184 | } 185 | 186 | public void setSourceURL(String sourceURL) { 187 | this.sourceURL = (sourceURL == null || sourceURL.equals("") ? "N/A" : sourceURL); 188 | } 189 | 190 | public String getModificationDate() { 191 | return modificationDate; 192 | } 193 | 194 | public void setModificationDate(String modificationDate) { 195 | this.modificationDate = (modificationDate == null || modificationDate.equals("") ? "N/A" : modificationDate); 196 | } 197 | 198 | public String getReleaseDate() { 199 | return releaseDate; 200 | } 201 | 202 | public void setReleaseDate(String releaseDate) { 203 | this.releaseDate = (releaseDate == null || releaseDate.equals("") ? "N/A" : releaseDate); 204 | } 205 | 206 | public String getPeriodicity() { 207 | return periodicity; 208 | } 209 | 210 | public void setPeriodicity(String periodicity) { 211 | this.periodicity = (periodicity == null || periodicity.equals("") ? "N/A" : periodicity); 212 | } 213 | 214 | public String getUnitMeasureName() { 215 | return unitMeasureName; 216 | } 217 | 218 | public void setUnitMeasureName(String unitMeasureName) { 219 | this.unitMeasureName = (unitMeasureName == null || unitMeasureName.equals("") ? "N/A" : unitMeasureName); 220 | } 221 | 222 | public String getUnitMeasure() { 223 | return unitMeasure; 224 | } 225 | 226 | public void setUnitMeasure(String unitMeasure) { 227 | this.unitMeasure = (unitMeasure == null || unitMeasure.equals("") ? "N/A" : unitMeasure); 228 | } 229 | 230 | public String getDimensionName() { 231 | return dimensionName; 232 | } 233 | 234 | public void setDimensionName(String dimensionName) { 235 | this.dimensionName = (dimensionName == null || dimensionName.equals("") ? "N/A" : dimensionName); 236 | } 237 | 238 | public String getDimensionURI() { 239 | return dimensionURI; 240 | } 241 | 242 | public void setDimensionURI(String dimensionURI) { 243 | this.dimensionURI = (dimensionURI == null || dimensionURI.equals("") ? "N/A" : dimensionURI); 244 | } 245 | 246 | public String getMeasureName() { 247 | return measureName; 248 | } 249 | 250 | public void setMeasureName(String measureName) { 251 | this.measureName = (measureName == null || measureName.equals("") ? "N/A" : measureName); 252 | } 253 | 254 | public String getMeasureURI() { 255 | return measureURI; 256 | } 257 | 258 | public void setMeasureURI(String measureURI) { 259 | this.measureURI = (measureURI == null || measureURI.equals("") ? "N/A" : measureURI); 260 | } 261 | 262 | public String getStructureName() { 263 | return structureName; 264 | } 265 | 266 | public void setStructureName(String structureName) { 267 | this.structureName = (structureName == null || structureName.equals("") ? "N/A" : structureName); 268 | } 269 | 270 | public LinkedList getObservations() { 271 | return observations; 272 | } 273 | 274 | public void setObservations(LinkedList observations) { 275 | this.observations = observations; 276 | LOG.info(observations); 277 | extractDim = observations.getFirst().getDimension(); 278 | extractDimBis = observations.getLast().getDimension(); 279 | extractMeas = observations.getFirst().getMeasure(); 280 | extractMeasBis = observations.getLast().getMeasure(); 281 | nbTriples = String.valueOf(observations.size()); 282 | } 283 | 284 | public String getExtractDim() { 285 | return extractDim; 286 | } 287 | 288 | public String getExtractMeas() { 289 | return extractMeas; 290 | } 291 | 292 | public String getExtractDimBis() { 293 | return extractDimBis; 294 | } 295 | 296 | public String getExtractMeasBis() { 297 | return extractMeasBis; 298 | } 299 | } 300 | -------------------------------------------------------------------------------- /src/java/org/datalift/d4/datacube/DataSetORM.java: -------------------------------------------------------------------------------- 1 | package org.datalift.d4.datacube; 2 | 3 | import java.util.LinkedList; 4 | 5 | import org.datalift.fwk.Configuration; 6 | import org.datalift.fwk.log.Logger; 7 | import org.datalift.fwk.rdf.Repository; 8 | import org.openrdf.query.BindingSet; 9 | import org.openrdf.query.MalformedQueryException; 10 | import org.openrdf.query.QueryEvaluationException; 11 | import org.openrdf.query.QueryLanguage; 12 | import org.openrdf.query.TupleQuery; 13 | import org.openrdf.query.TupleQueryResult; 14 | import org.openrdf.repository.RepositoryConnection; 15 | import org.openrdf.repository.RepositoryException; 16 | 17 | 18 | /** 19 | * Used to convert a DataCube DataSet's RDF representation to a Java object. 20 | * @author tcolas 21 | */ 22 | public class DataSetORM { 23 | 24 | private final String sourceURI; 25 | private final String lang; 26 | 27 | private DataSet dataset; 28 | 29 | private static final String uri = "uri"; 30 | private static final String title = "title"; 31 | 32 | private static final String description = "description"; 33 | private static final String comment = "comment"; 34 | private static final String publisherName = "publisherName"; 35 | private static final String publisherURL = "publisherURL"; 36 | private static final String depictionURL = "depictionURL"; 37 | private static final String coverageName = "coverageName"; 38 | private static final String coverageURL = "coverageURL"; 39 | private static final String licenseName = "licenseName"; 40 | private static final String formatName = "formatName"; 41 | private static final String sourceName = "sourceName"; 42 | private static final String modificationDate = "modificationDate"; 43 | private static final String releaseDate = "releaseDate"; 44 | private static final String periodicity = "periodicity"; 45 | private static final String unitMeasureName = "unitMeasureName"; 46 | private static final String unitMeasure = "unitMeasure"; 47 | 48 | private static final String dimensionName = "dimensionName"; 49 | private static final String dimensionURI = "dimensionURI"; 50 | private static final String measureName = "measureName"; 51 | private static final String measureURI = "measureURI"; 52 | 53 | private static final String structureName = "structureName"; 54 | 55 | /** Datalift's internal Sesame {@link Repository repository}. **/ 56 | protected static final Repository INTERNAL_REPO = Configuration.getDefault().getInternalRepository(); 57 | /** Datalift's internal Sesame {@link Repository repository} URL. */ 58 | protected static final String INTERNAL_URL = INTERNAL_REPO.getEndpointUrl(); 59 | /** Datalift's logging system. */ 60 | protected static final Logger LOG = Logger.getLogger(); 61 | 62 | public DataSetORM(String sourceURI, String lang) { 63 | this.sourceURI = sourceURI; 64 | this.lang = lang; 65 | 66 | dataset = new DataSet(); 67 | dataset.setGraph(sourceURI); 68 | } 69 | 70 | // We might want to only retrieve values for a given language. 71 | public String filterLang(String binding) { 72 | return "FILTER ( lang(?" + binding + ") = '" + lang + "' ) "; 73 | } 74 | 75 | public DataSet getDataSet() { 76 | getMetaData(); 77 | getObservations(); 78 | 79 | return dataset; 80 | } 81 | 82 | /** 83 | * Sends a massive query to retrieve all of the dataset's metadata. 84 | * TODO : This query needs to be written with UNIONs. 85 | */ 86 | private void getMetaData() { 87 | TupleQuery tq; 88 | TupleQueryResult tqr; 89 | 90 | String prefix = "PREFIX rdfs: " 91 | + "PREFIX sdmx-measure: " 92 | + "PREFIX foaf: " 93 | + "PREFIX dct: " 94 | + "PREFIX sdmx-attribute: " 95 | + "PREFIX owl: " 96 | + "PREFIX xsd: " 97 | + "PREFIX rdf: " 98 | + "PREFIX qb: " 99 | + "PREFIX sdmx-dimension: "; 100 | String bindings = "?" + uri 101 | + " ?" + title 102 | + " ?" + description 103 | + " ?" + comment 104 | + " ?" + publisherName 105 | + " ?" + publisherURL 106 | + " ?" + depictionURL 107 | + " ?" + coverageName 108 | + " ?" + coverageURL 109 | + " ?" + licenseName 110 | + " ?" + formatName 111 | + " ?" + sourceName 112 | + " ?" + modificationDate 113 | + " ?" + releaseDate 114 | + " ?" + periodicity 115 | + " ?" + unitMeasureName 116 | + " ?" + unitMeasure 117 | + " ?" + dimensionName 118 | + " ?" + dimensionURI 119 | + " ?" + measureName 120 | + " ?" + measureURI 121 | + " ?" + structureName; 122 | String from = "FROM <" + sourceURI + ">"; 123 | 124 | String where = "?" + uri + " a qb:DataSet ." 125 | + " ?" + uri + " dct:title ?" + title + " ." 126 | + " ?" + uri + " dct:description ?" + description + " ." 127 | + " ?" + uri + " rdfs:comment ?" + comment + " ." 128 | + " ?" + uri + " dct:publisher ?pubtmp ." 129 | + " ?pubtmp foaf:name ?" + publisherName + " ." 130 | + " ?pubtmp foaf:homepage ?" + publisherURL + " ." 131 | + " ?" + uri + " foaf:depiction ?" + depictionURL + " ." 132 | + " ?" + uri + " dct:coverage ?" + coverageURL + " ." 133 | + " ?" + coverageURL + " rdfs:label ?" + coverageName + " ." 134 | + " ?" + uri + " dct:license ?" + licenseName + " ." 135 | + " ?" + uri + " dct:format ?" + formatName + " ." 136 | + " ?" + uri + " dct:source ?" + sourceName + " ." 137 | + " ?" + uri + " dct:modified ?" + modificationDate + " ." 138 | + " ?" + uri + " dct:issued ?" + releaseDate + " ." 139 | + " ?" + uri + " dct:accrualPeriodicity ?pertmp ." 140 | + " ?pertmp rdfs:label ?" + periodicity + " ." 141 | + " ?" + uri + " sdmx-attribute:unitMeasure ?" + unitMeasure + " ." 142 | + " ?" + unitMeasure + " rdfs:label ?" + unitMeasureName + " ." 143 | + " ?" + uri + " qb:structure ?dsdtmp ." 144 | + " ?dsdtmp rdfs:label ?" + structureName + " ." 145 | + " ?dsdtmp qb:component ?dimtmp ." 146 | + " ?dimtmp qb:dimension ?" + dimensionURI + " ." 147 | + " ?dimtmp rdfs:label ?" + dimensionName + " ." 148 | + " ?dsdtmp qb:component ?msrtmp ." 149 | + " ?msrtmp qb:measure ?" + measureURI + " ." 150 | + " ?msrtmp rdfs:label ?" + measureName + " ."; 151 | String filterLang = filterLang(title) + filterLang(description) 152 | + filterLang(comment) + filterLang(periodicity) 153 | + filterLang(structureName) + filterLang(dimensionName) 154 | + filterLang(measureName); 155 | String query = prefix + " SELECT " + bindings + " " + from + " WHERE { " + where + " " + filterLang + " }"; 156 | 157 | LOG.debug("Processing query: \"{}\"", query); 158 | RepositoryConnection cnx = INTERNAL_REPO.newConnection(); 159 | try { 160 | tq = cnx.prepareTupleQuery(QueryLanguage.SPARQL, query); 161 | tqr = tq.evaluate(); 162 | 163 | BindingSet bs = tqr.next(); 164 | dataset.setUri(bs.getValue(uri).stringValue()); 165 | dataset.setTitle(bs.getValue(title).stringValue()); 166 | dataset.setDescription(bs.getValue(description).stringValue()); 167 | dataset.setComment(bs.getValue(comment).stringValue()); 168 | dataset.setPublisherName(bs.getValue(publisherName).stringValue()); 169 | dataset.setPublisherURL(bs.getValue(publisherURL).stringValue()); 170 | dataset.setDepictionURL(bs.getValue(depictionURL).stringValue()); 171 | dataset.setCoverageName(bs.getValue(coverageName).stringValue()); 172 | dataset.setCoverageURL(bs.getValue(coverageURL).stringValue()); 173 | 174 | dataset.setLicenseName(bs.getValue(licenseName).stringValue()); 175 | dataset.setFormatName(bs.getValue(formatName).stringValue()); 176 | dataset.setSourceName(bs.getValue(sourceName).stringValue()); 177 | 178 | dataset.setModificationDate(bs.getValue(modificationDate).stringValue()); 179 | dataset.setReleaseDate(bs.getValue(releaseDate).stringValue()); 180 | dataset.setPeriodicity(bs.getValue(periodicity).stringValue()); 181 | dataset.setUnitMeasureName(bs.getValue(unitMeasureName).stringValue()); 182 | dataset.setUnitMeasure(bs.getValue(unitMeasure).stringValue()); 183 | 184 | dataset.setDimensionName(bs.getValue(dimensionName).stringValue()); 185 | dataset.setDimensionURI(bs.getValue(dimensionURI).stringValue()); 186 | dataset.setMeasureName(bs.getValue(measureName).stringValue()); 187 | dataset.setMeasureURI(bs.getValue(measureURI).stringValue()); 188 | dataset.setStructureName(bs.getValue(structureName).stringValue()); 189 | 190 | while (tqr.hasNext()) { 191 | bs = tqr.next(); 192 | } 193 | 194 | dataset.setLicenseURL(bs.getValue(licenseName).stringValue()); 195 | dataset.setFormatURL(bs.getValue(formatName).stringValue()); 196 | dataset.setSourceURL(bs.getValue(sourceName).stringValue()); 197 | 198 | } 199 | catch (MalformedQueryException e) { 200 | LOG.fatal("Failed to process query \"{}\":", e, query); 201 | } catch (QueryEvaluationException e) { 202 | LOG.fatal("Failed to process query \"{}\":", e, query); 203 | } catch (RepositoryException e) { 204 | LOG.fatal("Failed to process query \"{}\":", e, query); 205 | } 206 | finally { 207 | try { cnx.close(); } catch (Exception e) { /* Ignore... */ } 208 | } 209 | } 210 | 211 | /** 212 | * Sends a SPARQL query to retrieve a dataset's observation. 213 | */ 214 | private void getObservations() { 215 | TupleQuery tq; 216 | TupleQueryResult tqr; 217 | 218 | String prefix = "PREFIX sdmx-measure: " 219 | + "PREFIX rdf: " 220 | + "PREFIX rdfs: " 221 | + "PREFIX qb: " 222 | + "PREFIX sdmx-dimension: "; 223 | String where = "?s a qb:Observation . ?s sdmx-dimension:refArea ?dim . ?s sdmx-measure:obsValue ?obs ."; 224 | String query = prefix + " SELECT ?dim ?obs FROM <" + sourceURI + "> WHERE { " + where + " }"; 225 | 226 | LOG.debug("Processing query: \"{}\"", query); 227 | RepositoryConnection cnx = INTERNAL_REPO.newConnection(); 228 | try { 229 | tq = cnx.prepareTupleQuery(QueryLanguage.SPARQL, query); 230 | tqr = tq.evaluate(); 231 | 232 | BindingSet bs; 233 | LinkedList tmp = new LinkedList(); 234 | 235 | while (tqr.hasNext()) { 236 | bs = tqr.next(); 237 | tmp.add(new Observation(bs.getValue("dim").stringValue(), bs.getValue("obs").stringValue())); 238 | 239 | } 240 | 241 | dataset.setObservations(tmp); 242 | 243 | } 244 | catch (MalformedQueryException e) { 245 | LOG.fatal("Failed to process query \"{}\":", e, query); 246 | } catch (QueryEvaluationException e) { 247 | LOG.fatal("Failed to process query \"{}\":", e, query); 248 | } catch (RepositoryException e) { 249 | LOG.fatal("Failed to process query \"{}\":", e, query); 250 | } 251 | finally { 252 | try { cnx.close(); } catch (Exception e) { /* Ignore... */ } 253 | } 254 | } 255 | 256 | } 257 | -------------------------------------------------------------------------------- /src/java/org/datalift/d4/datacube/Observation.java: -------------------------------------------------------------------------------- 1 | package org.datalift.d4.datacube; 2 | 3 | /** 4 | * Represents a statistical observation as a key / value pair. 5 | * There are other much more complex dataset structures out there but this one is the simplest. 6 | * @author tcolas 7 | * 8 | */ 9 | public class Observation { 10 | 11 | /** Dimension of an observation, i.e what's being observed. **/ 12 | private String dimension; 13 | /** Measure of an observation, i.e the observed value for the given dimension. **/ 14 | private String measure; 15 | 16 | public Observation(String dimension, String measure) { 17 | this.dimension = dimension; 18 | this.measure = measure; 19 | } 20 | 21 | public String getDimension() { 22 | return dimension; 23 | } 24 | 25 | public String getMeasure() { 26 | return measure; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/java/org/datalift/d4/package.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | D4 module 4 | 5 | 6 |

7 | D4 : DataLift, DataCube, D3. 8 |
9 | D4 is a data visualisation tool for DataCube datasets built on top of DataLift widh D3 and Twitter Bootstrap. 10 |

11 | 12 | 13 | -------------------------------------------------------------------------------- /src/web/d4-form.vm: -------------------------------------------------------------------------------- 1 | #load_core_resources 2 | #load("d4/resources") 3 | ## 4 | ## Hello Velocity ! 5 | #set( $moduleUri = "$baseUri/d4") 6 | ## 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | #i18n('d4.title') - Datalift 16 | 17 | 18 | 19 | #css_links 20 | 21 | 22 | 23 | 24 | ## 25 | 26 | 27 | 28 | #banner 29 |
30 |

31 | #i18n('project.label' ${it.title}) — #i18n('d4.title') 32 |

33 | 34 | 40 | 41 | 42 |
43 | 44 | #foreach( $dataset in $datasets ) 45 | 46 | 52 | 53 |
54 |
55 |
56 | 59 |
60 |
61 |
62 |
63 |
64 | 65 | #i18n('dataset.illustration') : $dataset.getTitle() 66 |
67 |
68 |

$dataset.getDescription()

69 |

$dataset.getComment()

70 |
71 |
72 |
73 |
74 | 75 |

76 |

#i18n('dataset.by') 77 | | #i18n('dataset.source') $dataset.getSourceName() 78 | | #i18n('dataset.license') $dataset.getLicenseName() 79 |
80 | #i18n('dataset.published') 81 | | #i18n('dataset.modified') 82 | | #i18n('dataset.periodicity') $dataset.getPeriodicity() 83 |
84 | #i18n('dataset.format') $dataset.getFormatName() 85 | | #i18n('dataset.uri') <$dataset.getUri()> 86 | 87 | 88 | | $dataset.getNbTriples() #i18n('dataset.triples') 89 |

90 |
91 |
92 |
93 | 94 |
95 |
#i18n('dataset.observations')
96 | $dataset.getStructureName() 97 |

98 | #i18n('dataset.coverage') 99 |
$dataset.getCoverageName() 100 |
101 | #i18n('dataset.dimension') 102 |
$dataset.getDimensionName() 103 |
104 | #i18n('dataset.measure') 105 |
$dataset.getUnitMeasureName() 106 | — $dataset.getMeasureName() 107 |

108 |
109 |
110 |
111 | 112 |
113 | 117 |
118 |
119 | 120 | 121 | 122 | 123 | #foreach( $observation in $dataset.getObservations()) 124 | 125 | #end 126 | 127 |
$dataset.getDimensionName()$dataset.getMeasureName()
$observation.getDimension()$observation.getMeasure()
128 |
129 |
130 |
131 | 132 |
133 | 134 |
135 |
136 | 137 |
138 |
139 |
140 | 141 | 142 |

#i18n('stats.position')

143 |
144 |
#i18n('stats.mean')
145 |
NA
146 |
147 |
#i18n('stats.mode')
148 |
NA
149 |
150 |
#i18n('stats.median')
151 |
NA
152 |
153 |
154 |
155 |

#i18n('stats.dispersion')

156 |
157 |
#i18n('stats.min')
158 |
NA
159 |
160 |
#i18n('stats.max')
161 |
NA
162 |
163 |
#i18n('stats.range')
164 |
NA
165 |
166 |
#i18n('stats.var')
167 |
NA
168 |
169 |
#i18n('stats.sdev')
170 |
NA
171 |
172 |
173 |
174 |
175 |
176 | 177 |
178 | #end 179 | 180 |
181 | 182 |
183 |
184 | #footer 185 | #script_links 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 234 | 235 | 236 | ## Lien temporaire pour le développement. 237 | ## 238 | ## 239 | ## 240 | ## 241 | 242 | 243 | 244 | 245 | 246 | 247 | -------------------------------------------------------------------------------- /src/web/public/static/css/DT_bootstrap.css: -------------------------------------------------------------------------------- 1 | 2 | div.dataTables_length label { 3 | float: left; 4 | text-align: left; 5 | } 6 | 7 | div.dataTables_length select { 8 | width: 75px; 9 | } 10 | 11 | div.dataTables_filter label { 12 | float: right; 13 | } 14 | 15 | div.dataTables_info { 16 | padding-top: 8px; 17 | } 18 | 19 | div.dataTables_paginate { 20 | float: right; 21 | margin: 0; 22 | } 23 | 24 | table.table { 25 | clear: both; 26 | margin-bottom: 6px !important; 27 | max-width: none !important; 28 | } 29 | 30 | table.table thead .sorting, 31 | table.table thead .sorting_asc, 32 | table.table thead .sorting_desc, 33 | table.table thead .sorting_asc_disabled, 34 | table.table thead .sorting_desc_disabled { 35 | cursor: pointer; 36 | *cursor: hand; 37 | } 38 | 39 | table.table thead .sorting { background: url('sort_both.png') no-repeat center right; } 40 | table.table thead .sorting_asc { background: url('sort_asc.png') no-repeat center right; } 41 | table.table thead .sorting_desc { background: url('sort_desc.png') no-repeat center right; } 42 | 43 | table.table thead .sorting_asc_disabled { background: url('sort_asc_disabled.png') no-repeat center right; } 44 | table.table thead .sorting_desc_disabled { background: url('sort_desc_disabled.png') no-repeat center right; } 45 | 46 | table.dataTable th:active { 47 | outline: none; 48 | } 49 | 50 | /* Scrolling */ 51 | div.dataTables_scrollHead table { 52 | margin-bottom: 0 !important; 53 | border-bottom-left-radius: 0; 54 | border-bottom-right-radius: 0; 55 | } 56 | 57 | div.dataTables_scrollHead table thead tr:last-child th:first-child, 58 | div.dataTables_scrollHead table thead tr:last-child td:first-child { 59 | border-bottom-left-radius: 0 !important; 60 | border-bottom-right-radius: 0 !important; 61 | } 62 | 63 | div.dataTables_scrollBody table { 64 | border-top: none; 65 | margin-bottom: 0 !important; 66 | } 67 | 68 | div.dataTables_scrollBody tbody tr:first-child th, 69 | div.dataTables_scrollBody tbody tr:first-child td { 70 | border-top: none; 71 | } 72 | 73 | div.dataTables_scrollFoot table { 74 | border-top: none; 75 | } 76 | 77 | 78 | 79 | 80 | /* 81 | * TableTools styles 82 | */ 83 | .table tbody tr.active td, 84 | .table tbody tr.active th { 85 | background-color: #08C; 86 | color: white; 87 | } 88 | 89 | .table tbody tr.active:hover td, 90 | .table tbody tr.active:hover th { 91 | background-color: #0075b0 !important; 92 | } 93 | 94 | .table-striped tbody tr.active:nth-child(odd) td, 95 | .table-striped tbody tr.active:nth-child(odd) th { 96 | background-color: #017ebc; 97 | } 98 | 99 | table.DTTT_selectable tbody tr { 100 | cursor: pointer; 101 | *cursor: hand; 102 | } 103 | 104 | div.DTTT .btn { 105 | color: #333 !important; 106 | font-size: 12px; 107 | } 108 | 109 | div.DTTT .btn:hover { 110 | text-decoration: none !important; 111 | } 112 | 113 | 114 | ul.DTTT_dropdown.dropdown-menu a { 115 | color: #333 !important; /* needed only when demo_page.css is included */ 116 | } 117 | 118 | ul.DTTT_dropdown.dropdown-menu li:hover a { 119 | background-color: #0088cc; 120 | color: white !important; 121 | } 122 | 123 | /* TableTools information display */ 124 | div.DTTT_print_info.modal { 125 | height: 150px; 126 | margin-top: -75px; 127 | text-align: center; 128 | } 129 | 130 | div.DTTT_print_info h6 { 131 | font-weight: normal; 132 | font-size: 28px; 133 | line-height: 28px; 134 | margin: 1em; 135 | } 136 | 137 | div.DTTT_print_info p { 138 | font-size: 14px; 139 | line-height: 20px; 140 | } 141 | 142 | 143 | 144 | /* 145 | * FixedColumns styles 146 | */ 147 | div.DTFC_LeftHeadWrapper table, 148 | div.DTFC_LeftFootWrapper table, 149 | table.DTFC_Cloned tr.even { 150 | background-color: white; 151 | } 152 | 153 | div.DTFC_LeftHeadWrapper table { 154 | margin-bottom: 0 !important; 155 | border-top-right-radius: 0 !important; 156 | border-bottom-left-radius: 0 !important; 157 | border-bottom-right-radius: 0 !important; 158 | } 159 | 160 | div.DTFC_LeftHeadWrapper table thead tr:last-child th:first-child, 161 | div.DTFC_LeftHeadWrapper table thead tr:last-child td:first-child { 162 | border-bottom-left-radius: 0 !important; 163 | border-bottom-right-radius: 0 !important; 164 | } 165 | 166 | div.DTFC_LeftBodyWrapper table { 167 | border-top: none; 168 | margin-bottom: 0 !important; 169 | } 170 | 171 | div.DTFC_LeftBodyWrapper tbody tr:first-child th, 172 | div.DTFC_LeftBodyWrapper tbody tr:first-child td { 173 | border-top: none; 174 | } 175 | 176 | div.DTFC_LeftFootWrapper table { 177 | border-top: none; 178 | } 179 | 180 | -------------------------------------------------------------------------------- /src/web/public/static/css/back_disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thibaudcolas/d4/992726e87a6449cc0be91c78325c79994bdc1407/src/web/public/static/css/back_disabled.png -------------------------------------------------------------------------------- /src/web/public/static/css/back_enabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thibaudcolas/d4/992726e87a6449cc0be91c78325c79994bdc1407/src/web/public/static/css/back_enabled.png -------------------------------------------------------------------------------- /src/web/public/static/css/back_enabled_hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thibaudcolas/d4/992726e87a6449cc0be91c78325c79994bdc1407/src/web/public/static/css/back_enabled_hover.png -------------------------------------------------------------------------------- /src/web/public/static/css/bootstrap-responsive.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Responsive v2.1.1 3 | * 4 | * Copyright 2012 Twitter, Inc 5 | * Licensed under the Apache License v2.0 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Designed and built with all the love in the world @twitter by @mdo and @fat. 9 | */ 10 | 11 | .clearfix { 12 | *zoom: 1; 13 | } 14 | 15 | .clearfix:before, 16 | .clearfix:after { 17 | display: table; 18 | line-height: 0; 19 | content: ""; 20 | } 21 | 22 | .clearfix:after { 23 | clear: both; 24 | } 25 | 26 | .hide-text { 27 | font: 0/0 a; 28 | color: transparent; 29 | text-shadow: none; 30 | background-color: transparent; 31 | border: 0; 32 | } 33 | 34 | .input-block-level { 35 | display: block; 36 | width: 100%; 37 | min-height: 30px; 38 | -webkit-box-sizing: border-box; 39 | -moz-box-sizing: border-box; 40 | box-sizing: border-box; 41 | } 42 | 43 | .hidden { 44 | display: none; 45 | visibility: hidden; 46 | } 47 | 48 | .visible-phone { 49 | display: none !important; 50 | } 51 | 52 | .visible-tablet { 53 | display: none !important; 54 | } 55 | 56 | .hidden-desktop { 57 | display: none !important; 58 | } 59 | 60 | .visible-desktop { 61 | display: inherit !important; 62 | } 63 | 64 | @media (min-width: 768px) and (max-width: 979px) { 65 | .hidden-desktop { 66 | display: inherit !important; 67 | } 68 | .visible-desktop { 69 | display: none !important ; 70 | } 71 | .visible-tablet { 72 | display: inherit !important; 73 | } 74 | .hidden-tablet { 75 | display: none !important; 76 | } 77 | } 78 | 79 | @media (max-width: 767px) { 80 | .hidden-desktop { 81 | display: inherit !important; 82 | } 83 | .visible-desktop { 84 | display: none !important; 85 | } 86 | .visible-phone { 87 | display: inherit !important; 88 | } 89 | .hidden-phone { 90 | display: none !important; 91 | } 92 | } 93 | 94 | @media (min-width: 1200px) { 95 | .row { 96 | margin-left: -30px; 97 | *zoom: 1; 98 | } 99 | .row:before, 100 | .row:after { 101 | display: table; 102 | line-height: 0; 103 | content: ""; 104 | } 105 | .row:after { 106 | clear: both; 107 | } 108 | [class*="span"] { 109 | float: left; 110 | min-height: 1px; 111 | margin-left: 30px; 112 | } 113 | .container, 114 | .navbar-static-top .container, 115 | .navbar-fixed-top .container, 116 | .navbar-fixed-bottom .container { 117 | width: 1170px; 118 | } 119 | .span12 { 120 | width: 1170px; 121 | } 122 | .span11 { 123 | width: 1070px; 124 | } 125 | .span10 { 126 | width: 970px; 127 | } 128 | .span9 { 129 | width: 870px; 130 | } 131 | .span8 { 132 | width: 770px; 133 | } 134 | .span7 { 135 | width: 670px; 136 | } 137 | .span6 { 138 | width: 570px; 139 | } 140 | .span5 { 141 | width: 470px; 142 | } 143 | .span4 { 144 | width: 370px; 145 | } 146 | .span3 { 147 | width: 270px; 148 | } 149 | .span2 { 150 | width: 170px; 151 | } 152 | .span1 { 153 | width: 70px; 154 | } 155 | .offset12 { 156 | margin-left: 1230px; 157 | } 158 | .offset11 { 159 | margin-left: 1130px; 160 | } 161 | .offset10 { 162 | margin-left: 1030px; 163 | } 164 | .offset9 { 165 | margin-left: 930px; 166 | } 167 | .offset8 { 168 | margin-left: 830px; 169 | } 170 | .offset7 { 171 | margin-left: 730px; 172 | } 173 | .offset6 { 174 | margin-left: 630px; 175 | } 176 | .offset5 { 177 | margin-left: 530px; 178 | } 179 | .offset4 { 180 | margin-left: 430px; 181 | } 182 | .offset3 { 183 | margin-left: 330px; 184 | } 185 | .offset2 { 186 | margin-left: 230px; 187 | } 188 | .offset1 { 189 | margin-left: 130px; 190 | } 191 | .row-fluid { 192 | width: 100%; 193 | *zoom: 1; 194 | } 195 | .row-fluid:before, 196 | .row-fluid:after { 197 | display: table; 198 | line-height: 0; 199 | content: ""; 200 | } 201 | .row-fluid:after { 202 | clear: both; 203 | } 204 | .row-fluid [class*="span"] { 205 | display: block; 206 | float: left; 207 | width: 100%; 208 | min-height: 30px; 209 | margin-left: 2.564102564102564%; 210 | *margin-left: 2.5109110747408616%; 211 | -webkit-box-sizing: border-box; 212 | -moz-box-sizing: border-box; 213 | box-sizing: border-box; 214 | } 215 | .row-fluid [class*="span"]:first-child { 216 | margin-left: 0; 217 | } 218 | .row-fluid .span12 { 219 | width: 100%; 220 | *width: 99.94680851063829%; 221 | } 222 | .row-fluid .span11 { 223 | width: 91.45299145299145%; 224 | *width: 91.39979996362975%; 225 | } 226 | .row-fluid .span10 { 227 | width: 82.90598290598291%; 228 | *width: 82.8527914166212%; 229 | } 230 | .row-fluid .span9 { 231 | width: 74.35897435897436%; 232 | *width: 74.30578286961266%; 233 | } 234 | .row-fluid .span8 { 235 | width: 65.81196581196582%; 236 | *width: 65.75877432260411%; 237 | } 238 | .row-fluid .span7 { 239 | width: 57.26495726495726%; 240 | *width: 57.21176577559556%; 241 | } 242 | .row-fluid .span6 { 243 | width: 48.717948717948715%; 244 | *width: 48.664757228587014%; 245 | } 246 | .row-fluid .span5 { 247 | width: 40.17094017094017%; 248 | *width: 40.11774868157847%; 249 | } 250 | .row-fluid .span4 { 251 | width: 31.623931623931625%; 252 | *width: 31.570740134569924%; 253 | } 254 | .row-fluid .span3 { 255 | width: 23.076923076923077%; 256 | *width: 23.023731587561375%; 257 | } 258 | .row-fluid .span2 { 259 | width: 14.52991452991453%; 260 | *width: 14.476723040552828%; 261 | } 262 | .row-fluid .span1 { 263 | width: 5.982905982905983%; 264 | *width: 5.929714493544281%; 265 | } 266 | .row-fluid .offset12 { 267 | margin-left: 105.12820512820512%; 268 | *margin-left: 105.02182214948171%; 269 | } 270 | .row-fluid .offset12:first-child { 271 | margin-left: 102.56410256410257%; 272 | *margin-left: 102.45771958537915%; 273 | } 274 | .row-fluid .offset11 { 275 | margin-left: 96.58119658119658%; 276 | *margin-left: 96.47481360247316%; 277 | } 278 | .row-fluid .offset11:first-child { 279 | margin-left: 94.01709401709402%; 280 | *margin-left: 93.91071103837061%; 281 | } 282 | .row-fluid .offset10 { 283 | margin-left: 88.03418803418803%; 284 | *margin-left: 87.92780505546462%; 285 | } 286 | .row-fluid .offset10:first-child { 287 | margin-left: 85.47008547008548%; 288 | *margin-left: 85.36370249136206%; 289 | } 290 | .row-fluid .offset9 { 291 | margin-left: 79.48717948717949%; 292 | *margin-left: 79.38079650845607%; 293 | } 294 | .row-fluid .offset9:first-child { 295 | margin-left: 76.92307692307693%; 296 | *margin-left: 76.81669394435352%; 297 | } 298 | .row-fluid .offset8 { 299 | margin-left: 70.94017094017094%; 300 | *margin-left: 70.83378796144753%; 301 | } 302 | .row-fluid .offset8:first-child { 303 | margin-left: 68.37606837606839%; 304 | *margin-left: 68.26968539734497%; 305 | } 306 | .row-fluid .offset7 { 307 | margin-left: 62.393162393162385%; 308 | *margin-left: 62.28677941443899%; 309 | } 310 | .row-fluid .offset7:first-child { 311 | margin-left: 59.82905982905982%; 312 | *margin-left: 59.72267685033642%; 313 | } 314 | .row-fluid .offset6 { 315 | margin-left: 53.84615384615384%; 316 | *margin-left: 53.739770867430444%; 317 | } 318 | .row-fluid .offset6:first-child { 319 | margin-left: 51.28205128205128%; 320 | *margin-left: 51.175668303327875%; 321 | } 322 | .row-fluid .offset5 { 323 | margin-left: 45.299145299145295%; 324 | *margin-left: 45.1927623204219%; 325 | } 326 | .row-fluid .offset5:first-child { 327 | margin-left: 42.73504273504273%; 328 | *margin-left: 42.62865975631933%; 329 | } 330 | .row-fluid .offset4 { 331 | margin-left: 36.75213675213675%; 332 | *margin-left: 36.645753773413354%; 333 | } 334 | .row-fluid .offset4:first-child { 335 | margin-left: 34.18803418803419%; 336 | *margin-left: 34.081651209310785%; 337 | } 338 | .row-fluid .offset3 { 339 | margin-left: 28.205128205128204%; 340 | *margin-left: 28.0987452264048%; 341 | } 342 | .row-fluid .offset3:first-child { 343 | margin-left: 25.641025641025642%; 344 | *margin-left: 25.53464266230224%; 345 | } 346 | .row-fluid .offset2 { 347 | margin-left: 19.65811965811966%; 348 | *margin-left: 19.551736679396257%; 349 | } 350 | .row-fluid .offset2:first-child { 351 | margin-left: 17.094017094017094%; 352 | *margin-left: 16.98763411529369%; 353 | } 354 | .row-fluid .offset1 { 355 | margin-left: 11.11111111111111%; 356 | *margin-left: 11.004728132387708%; 357 | } 358 | .row-fluid .offset1:first-child { 359 | margin-left: 8.547008547008547%; 360 | *margin-left: 8.440625568285142%; 361 | } 362 | input, 363 | textarea, 364 | .uneditable-input { 365 | margin-left: 0; 366 | } 367 | .controls-row [class*="span"] + [class*="span"] { 368 | margin-left: 30px; 369 | } 370 | input.span12, 371 | textarea.span12, 372 | .uneditable-input.span12 { 373 | width: 1156px; 374 | } 375 | input.span11, 376 | textarea.span11, 377 | .uneditable-input.span11 { 378 | width: 1056px; 379 | } 380 | input.span10, 381 | textarea.span10, 382 | .uneditable-input.span10 { 383 | width: 956px; 384 | } 385 | input.span9, 386 | textarea.span9, 387 | .uneditable-input.span9 { 388 | width: 856px; 389 | } 390 | input.span8, 391 | textarea.span8, 392 | .uneditable-input.span8 { 393 | width: 756px; 394 | } 395 | input.span7, 396 | textarea.span7, 397 | .uneditable-input.span7 { 398 | width: 656px; 399 | } 400 | input.span6, 401 | textarea.span6, 402 | .uneditable-input.span6 { 403 | width: 556px; 404 | } 405 | input.span5, 406 | textarea.span5, 407 | .uneditable-input.span5 { 408 | width: 456px; 409 | } 410 | input.span4, 411 | textarea.span4, 412 | .uneditable-input.span4 { 413 | width: 356px; 414 | } 415 | input.span3, 416 | textarea.span3, 417 | .uneditable-input.span3 { 418 | width: 256px; 419 | } 420 | input.span2, 421 | textarea.span2, 422 | .uneditable-input.span2 { 423 | width: 156px; 424 | } 425 | input.span1, 426 | textarea.span1, 427 | .uneditable-input.span1 { 428 | width: 56px; 429 | } 430 | .thumbnails { 431 | margin-left: -30px; 432 | } 433 | .thumbnails > li { 434 | margin-left: 30px; 435 | } 436 | .row-fluid .thumbnails { 437 | margin-left: 0; 438 | } 439 | } 440 | 441 | @media (min-width: 768px) and (max-width: 979px) { 442 | .row { 443 | margin-left: -20px; 444 | *zoom: 1; 445 | } 446 | .row:before, 447 | .row:after { 448 | display: table; 449 | line-height: 0; 450 | content: ""; 451 | } 452 | .row:after { 453 | clear: both; 454 | } 455 | [class*="span"] { 456 | float: left; 457 | min-height: 1px; 458 | margin-left: 20px; 459 | } 460 | .container, 461 | .navbar-static-top .container, 462 | .navbar-fixed-top .container, 463 | .navbar-fixed-bottom .container { 464 | width: 724px; 465 | } 466 | .span12 { 467 | width: 724px; 468 | } 469 | .span11 { 470 | width: 662px; 471 | } 472 | .span10 { 473 | width: 600px; 474 | } 475 | .span9 { 476 | width: 538px; 477 | } 478 | .span8 { 479 | width: 476px; 480 | } 481 | .span7 { 482 | width: 414px; 483 | } 484 | .span6 { 485 | width: 352px; 486 | } 487 | .span5 { 488 | width: 290px; 489 | } 490 | .span4 { 491 | width: 228px; 492 | } 493 | .span3 { 494 | width: 166px; 495 | } 496 | .span2 { 497 | width: 104px; 498 | } 499 | .span1 { 500 | width: 42px; 501 | } 502 | .offset12 { 503 | margin-left: 764px; 504 | } 505 | .offset11 { 506 | margin-left: 702px; 507 | } 508 | .offset10 { 509 | margin-left: 640px; 510 | } 511 | .offset9 { 512 | margin-left: 578px; 513 | } 514 | .offset8 { 515 | margin-left: 516px; 516 | } 517 | .offset7 { 518 | margin-left: 454px; 519 | } 520 | .offset6 { 521 | margin-left: 392px; 522 | } 523 | .offset5 { 524 | margin-left: 330px; 525 | } 526 | .offset4 { 527 | margin-left: 268px; 528 | } 529 | .offset3 { 530 | margin-left: 206px; 531 | } 532 | .offset2 { 533 | margin-left: 144px; 534 | } 535 | .offset1 { 536 | margin-left: 82px; 537 | } 538 | .row-fluid { 539 | width: 100%; 540 | *zoom: 1; 541 | } 542 | .row-fluid:before, 543 | .row-fluid:after { 544 | display: table; 545 | line-height: 0; 546 | content: ""; 547 | } 548 | .row-fluid:after { 549 | clear: both; 550 | } 551 | .row-fluid [class*="span"] { 552 | display: block; 553 | float: left; 554 | width: 100%; 555 | min-height: 30px; 556 | margin-left: 2.7624309392265194%; 557 | *margin-left: 2.709239449864817%; 558 | -webkit-box-sizing: border-box; 559 | -moz-box-sizing: border-box; 560 | box-sizing: border-box; 561 | } 562 | .row-fluid [class*="span"]:first-child { 563 | margin-left: 0; 564 | } 565 | .row-fluid .span12 { 566 | width: 100%; 567 | *width: 99.94680851063829%; 568 | } 569 | .row-fluid .span11 { 570 | width: 91.43646408839778%; 571 | *width: 91.38327259903608%; 572 | } 573 | .row-fluid .span10 { 574 | width: 82.87292817679558%; 575 | *width: 82.81973668743387%; 576 | } 577 | .row-fluid .span9 { 578 | width: 74.30939226519337%; 579 | *width: 74.25620077583166%; 580 | } 581 | .row-fluid .span8 { 582 | width: 65.74585635359117%; 583 | *width: 65.69266486422946%; 584 | } 585 | .row-fluid .span7 { 586 | width: 57.18232044198895%; 587 | *width: 57.12912895262725%; 588 | } 589 | .row-fluid .span6 { 590 | width: 48.61878453038674%; 591 | *width: 48.56559304102504%; 592 | } 593 | .row-fluid .span5 { 594 | width: 40.05524861878453%; 595 | *width: 40.00205712942283%; 596 | } 597 | .row-fluid .span4 { 598 | width: 31.491712707182323%; 599 | *width: 31.43852121782062%; 600 | } 601 | .row-fluid .span3 { 602 | width: 22.92817679558011%; 603 | *width: 22.87498530621841%; 604 | } 605 | .row-fluid .span2 { 606 | width: 14.3646408839779%; 607 | *width: 14.311449394616199%; 608 | } 609 | .row-fluid .span1 { 610 | width: 5.801104972375691%; 611 | *width: 5.747913483013988%; 612 | } 613 | .row-fluid .offset12 { 614 | margin-left: 105.52486187845304%; 615 | *margin-left: 105.41847889972962%; 616 | } 617 | .row-fluid .offset12:first-child { 618 | margin-left: 102.76243093922652%; 619 | *margin-left: 102.6560479605031%; 620 | } 621 | .row-fluid .offset11 { 622 | margin-left: 96.96132596685082%; 623 | *margin-left: 96.8549429881274%; 624 | } 625 | .row-fluid .offset11:first-child { 626 | margin-left: 94.1988950276243%; 627 | *margin-left: 94.09251204890089%; 628 | } 629 | .row-fluid .offset10 { 630 | margin-left: 88.39779005524862%; 631 | *margin-left: 88.2914070765252%; 632 | } 633 | .row-fluid .offset10:first-child { 634 | margin-left: 85.6353591160221%; 635 | *margin-left: 85.52897613729868%; 636 | } 637 | .row-fluid .offset9 { 638 | margin-left: 79.8342541436464%; 639 | *margin-left: 79.72787116492299%; 640 | } 641 | .row-fluid .offset9:first-child { 642 | margin-left: 77.07182320441989%; 643 | *margin-left: 76.96544022569647%; 644 | } 645 | .row-fluid .offset8 { 646 | margin-left: 71.2707182320442%; 647 | *margin-left: 71.16433525332079%; 648 | } 649 | .row-fluid .offset8:first-child { 650 | margin-left: 68.50828729281768%; 651 | *margin-left: 68.40190431409427%; 652 | } 653 | .row-fluid .offset7 { 654 | margin-left: 62.70718232044199%; 655 | *margin-left: 62.600799341718584%; 656 | } 657 | .row-fluid .offset7:first-child { 658 | margin-left: 59.94475138121547%; 659 | *margin-left: 59.838368402492065%; 660 | } 661 | .row-fluid .offset6 { 662 | margin-left: 54.14364640883978%; 663 | *margin-left: 54.037263430116376%; 664 | } 665 | .row-fluid .offset6:first-child { 666 | margin-left: 51.38121546961326%; 667 | *margin-left: 51.27483249088986%; 668 | } 669 | .row-fluid .offset5 { 670 | margin-left: 45.58011049723757%; 671 | *margin-left: 45.47372751851417%; 672 | } 673 | .row-fluid .offset5:first-child { 674 | margin-left: 42.81767955801105%; 675 | *margin-left: 42.71129657928765%; 676 | } 677 | .row-fluid .offset4 { 678 | margin-left: 37.01657458563536%; 679 | *margin-left: 36.91019160691196%; 680 | } 681 | .row-fluid .offset4:first-child { 682 | margin-left: 34.25414364640884%; 683 | *margin-left: 34.14776066768544%; 684 | } 685 | .row-fluid .offset3 { 686 | margin-left: 28.45303867403315%; 687 | *margin-left: 28.346655695309746%; 688 | } 689 | .row-fluid .offset3:first-child { 690 | margin-left: 25.69060773480663%; 691 | *margin-left: 25.584224756083227%; 692 | } 693 | .row-fluid .offset2 { 694 | margin-left: 19.88950276243094%; 695 | *margin-left: 19.783119783707537%; 696 | } 697 | .row-fluid .offset2:first-child { 698 | margin-left: 17.12707182320442%; 699 | *margin-left: 17.02068884448102%; 700 | } 701 | .row-fluid .offset1 { 702 | margin-left: 11.32596685082873%; 703 | *margin-left: 11.219583872105325%; 704 | } 705 | .row-fluid .offset1:first-child { 706 | margin-left: 8.56353591160221%; 707 | *margin-left: 8.457152932878806%; 708 | } 709 | input, 710 | textarea, 711 | .uneditable-input { 712 | margin-left: 0; 713 | } 714 | .controls-row [class*="span"] + [class*="span"] { 715 | margin-left: 20px; 716 | } 717 | input.span12, 718 | textarea.span12, 719 | .uneditable-input.span12 { 720 | width: 710px; 721 | } 722 | input.span11, 723 | textarea.span11, 724 | .uneditable-input.span11 { 725 | width: 648px; 726 | } 727 | input.span10, 728 | textarea.span10, 729 | .uneditable-input.span10 { 730 | width: 586px; 731 | } 732 | input.span9, 733 | textarea.span9, 734 | .uneditable-input.span9 { 735 | width: 524px; 736 | } 737 | input.span8, 738 | textarea.span8, 739 | .uneditable-input.span8 { 740 | width: 462px; 741 | } 742 | input.span7, 743 | textarea.span7, 744 | .uneditable-input.span7 { 745 | width: 400px; 746 | } 747 | input.span6, 748 | textarea.span6, 749 | .uneditable-input.span6 { 750 | width: 338px; 751 | } 752 | input.span5, 753 | textarea.span5, 754 | .uneditable-input.span5 { 755 | width: 276px; 756 | } 757 | input.span4, 758 | textarea.span4, 759 | .uneditable-input.span4 { 760 | width: 214px; 761 | } 762 | input.span3, 763 | textarea.span3, 764 | .uneditable-input.span3 { 765 | width: 152px; 766 | } 767 | input.span2, 768 | textarea.span2, 769 | .uneditable-input.span2 { 770 | width: 90px; 771 | } 772 | input.span1, 773 | textarea.span1, 774 | .uneditable-input.span1 { 775 | width: 28px; 776 | } 777 | } 778 | 779 | @media (max-width: 767px) { 780 | body { 781 | padding-right: 20px; 782 | padding-left: 20px; 783 | } 784 | .navbar-fixed-top, 785 | .navbar-fixed-bottom, 786 | .navbar-static-top { 787 | margin-right: -20px; 788 | margin-left: -20px; 789 | } 790 | .container-fluid { 791 | padding: 0; 792 | } 793 | .dl-horizontal dt { 794 | float: none; 795 | width: auto; 796 | clear: none; 797 | text-align: left; 798 | } 799 | .dl-horizontal dd { 800 | margin-left: 0; 801 | } 802 | .container { 803 | width: auto; 804 | } 805 | .row-fluid { 806 | width: 100%; 807 | } 808 | .row, 809 | .thumbnails { 810 | margin-left: 0; 811 | } 812 | .thumbnails > li { 813 | float: none; 814 | margin-left: 0; 815 | } 816 | [class*="span"], 817 | .row-fluid [class*="span"] { 818 | display: block; 819 | float: none; 820 | width: 100%; 821 | margin-left: 0; 822 | -webkit-box-sizing: border-box; 823 | -moz-box-sizing: border-box; 824 | box-sizing: border-box; 825 | } 826 | .span12, 827 | .row-fluid .span12 { 828 | width: 100%; 829 | -webkit-box-sizing: border-box; 830 | -moz-box-sizing: border-box; 831 | box-sizing: border-box; 832 | } 833 | .input-large, 834 | .input-xlarge, 835 | .input-xxlarge, 836 | input[class*="span"], 837 | select[class*="span"], 838 | textarea[class*="span"], 839 | .uneditable-input { 840 | display: block; 841 | width: 100%; 842 | min-height: 30px; 843 | -webkit-box-sizing: border-box; 844 | -moz-box-sizing: border-box; 845 | box-sizing: border-box; 846 | } 847 | .input-prepend input, 848 | .input-append input, 849 | .input-prepend input[class*="span"], 850 | .input-append input[class*="span"] { 851 | display: inline-block; 852 | width: auto; 853 | } 854 | .controls-row [class*="span"] + [class*="span"] { 855 | margin-left: 0; 856 | } 857 | .modal { 858 | position: fixed; 859 | top: 20px; 860 | right: 20px; 861 | left: 20px; 862 | width: auto; 863 | margin: 0; 864 | } 865 | .modal.fade.in { 866 | top: auto; 867 | } 868 | } 869 | 870 | @media (max-width: 480px) { 871 | .nav-collapse { 872 | -webkit-transform: translate3d(0, 0, 0); 873 | } 874 | .page-header h1 small { 875 | display: block; 876 | line-height: 20px; 877 | } 878 | input[type="checkbox"], 879 | input[type="radio"] { 880 | border: 1px solid #ccc; 881 | } 882 | .form-horizontal .control-label { 883 | float: none; 884 | width: auto; 885 | padding-top: 0; 886 | text-align: left; 887 | } 888 | .form-horizontal .controls { 889 | margin-left: 0; 890 | } 891 | .form-horizontal .control-list { 892 | padding-top: 0; 893 | } 894 | .form-horizontal .form-actions { 895 | padding-right: 10px; 896 | padding-left: 10px; 897 | } 898 | .modal { 899 | top: 10px; 900 | right: 10px; 901 | left: 10px; 902 | } 903 | .modal-header .close { 904 | padding: 10px; 905 | margin: -10px; 906 | } 907 | .carousel-caption { 908 | position: static; 909 | } 910 | } 911 | 912 | @media (max-width: 979px) { 913 | body { 914 | padding-top: 0; 915 | } 916 | .navbar-fixed-top, 917 | .navbar-fixed-bottom { 918 | position: static; 919 | } 920 | .navbar-fixed-top { 921 | margin-bottom: 20px; 922 | } 923 | .navbar-fixed-bottom { 924 | margin-top: 20px; 925 | } 926 | .navbar-fixed-top .navbar-inner, 927 | .navbar-fixed-bottom .navbar-inner { 928 | padding: 5px; 929 | } 930 | .navbar .container { 931 | width: auto; 932 | padding: 0; 933 | } 934 | .navbar .brand { 935 | padding-right: 10px; 936 | padding-left: 10px; 937 | margin: 0 0 0 -5px; 938 | } 939 | .nav-collapse { 940 | clear: both; 941 | } 942 | .nav-collapse .nav { 943 | float: none; 944 | margin: 0 0 10px; 945 | } 946 | .nav-collapse .nav > li { 947 | float: none; 948 | } 949 | .nav-collapse .nav > li > a { 950 | margin-bottom: 2px; 951 | } 952 | .nav-collapse .nav > .divider-vertical { 953 | display: none; 954 | } 955 | .nav-collapse .nav .nav-header { 956 | color: #777777; 957 | text-shadow: none; 958 | } 959 | .nav-collapse .nav > li > a, 960 | .nav-collapse .dropdown-menu a { 961 | padding: 9px 15px; 962 | font-weight: bold; 963 | color: #777777; 964 | -webkit-border-radius: 3px; 965 | -moz-border-radius: 3px; 966 | border-radius: 3px; 967 | } 968 | .nav-collapse .btn { 969 | padding: 4px 10px 4px; 970 | font-weight: normal; 971 | -webkit-border-radius: 4px; 972 | -moz-border-radius: 4px; 973 | border-radius: 4px; 974 | } 975 | .nav-collapse .dropdown-menu li + li a { 976 | margin-bottom: 2px; 977 | } 978 | .nav-collapse .nav > li > a:hover, 979 | .nav-collapse .dropdown-menu a:hover { 980 | background-color: #f2f2f2; 981 | } 982 | .navbar-inverse .nav-collapse .nav > li > a:hover, 983 | .navbar-inverse .nav-collapse .dropdown-menu a:hover { 984 | background-color: #111111; 985 | } 986 | .nav-collapse.in .btn-group { 987 | padding: 0; 988 | margin-top: 5px; 989 | } 990 | .nav-collapse .dropdown-menu { 991 | position: static; 992 | top: auto; 993 | left: auto; 994 | display: block; 995 | float: none; 996 | max-width: none; 997 | padding: 0; 998 | margin: 0 15px; 999 | background-color: transparent; 1000 | border: none; 1001 | -webkit-border-radius: 0; 1002 | -moz-border-radius: 0; 1003 | border-radius: 0; 1004 | -webkit-box-shadow: none; 1005 | -moz-box-shadow: none; 1006 | box-shadow: none; 1007 | } 1008 | .nav-collapse .dropdown-menu:before, 1009 | .nav-collapse .dropdown-menu:after { 1010 | display: none; 1011 | } 1012 | .nav-collapse .dropdown-menu .divider { 1013 | display: none; 1014 | } 1015 | .nav-collapse .nav > li > .dropdown-menu:before, 1016 | .nav-collapse .nav > li > .dropdown-menu:after { 1017 | display: none; 1018 | } 1019 | .nav-collapse .navbar-form, 1020 | .nav-collapse .navbar-search { 1021 | float: none; 1022 | padding: 10px 15px; 1023 | margin: 10px 0; 1024 | border-top: 1px solid #f2f2f2; 1025 | border-bottom: 1px solid #f2f2f2; 1026 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); 1027 | -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); 1028 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); 1029 | } 1030 | .navbar-inverse .nav-collapse .navbar-form, 1031 | .navbar-inverse .nav-collapse .navbar-search { 1032 | border-top-color: #111111; 1033 | border-bottom-color: #111111; 1034 | } 1035 | .navbar .nav-collapse .nav.pull-right { 1036 | float: none; 1037 | margin-left: 0; 1038 | } 1039 | .nav-collapse, 1040 | .nav-collapse.collapse { 1041 | height: 0; 1042 | overflow: hidden; 1043 | } 1044 | .navbar .btn-navbar { 1045 | display: block; 1046 | } 1047 | .navbar-static .navbar-inner { 1048 | padding-right: 10px; 1049 | padding-left: 10px; 1050 | } 1051 | } 1052 | 1053 | @media (min-width: 980px) { 1054 | .nav-collapse.collapse { 1055 | height: auto !important; 1056 | overflow: visible !important; 1057 | } 1058 | } 1059 | -------------------------------------------------------------------------------- /src/web/public/static/css/forward_disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thibaudcolas/d4/992726e87a6449cc0be91c78325c79994bdc1407/src/web/public/static/css/forward_disabled.png -------------------------------------------------------------------------------- /src/web/public/static/css/forward_enabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thibaudcolas/d4/992726e87a6449cc0be91c78325c79994bdc1407/src/web/public/static/css/forward_enabled.png -------------------------------------------------------------------------------- /src/web/public/static/css/forward_enabled_hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thibaudcolas/d4/992726e87a6449cc0be91c78325c79994bdc1407/src/web/public/static/css/forward_enabled_hover.png -------------------------------------------------------------------------------- /src/web/public/static/css/glyphicons-halflings-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thibaudcolas/d4/992726e87a6449cc0be91c78325c79994bdc1407/src/web/public/static/css/glyphicons-halflings-white.png -------------------------------------------------------------------------------- /src/web/public/static/css/glyphicons-halflings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thibaudcolas/d4/992726e87a6449cc0be91c78325c79994bdc1407/src/web/public/static/css/glyphicons-halflings.png -------------------------------------------------------------------------------- /src/web/public/static/css/main.css: -------------------------------------------------------------------------------- 1 | body { 2 | overflow-x:hidden; 3 | } 4 | 5 | #workspace { 6 | border:none; 7 | } 8 | 9 | h3 { 10 | line-height:20px; 11 | font-size:18px; 12 | } 13 | 14 | /** 15 | * 16 | * Data table alternate rows (should use nth-child). 17 | * 18 | */ 19 | 20 | .observation-table tbody tr.even:hover, .observation-table tbody tr.even td.highlighted { 21 | background-color: #3a87ad; 22 | color: #eee; 23 | } 24 | 25 | .observation-table tbody tr.odd:hover, .observation-table tbody tr.odd td.highlighted { 26 | background-color: #3a87ad; 27 | color: #eee; 28 | } 29 | 30 | 31 | .observation-table tbody tr.even:hover td, .observation-table tbody tr.even td.highlighted { 32 | background-color: #3a87ad !important; 33 | color: #eee; 34 | } 35 | 36 | .observation-table tbody tr.odd:hover td, .observation-table tbody tr.odd td.highlighted { 37 | background-color: #3a87ad !important; 38 | } 39 | 40 | .tab-pane .dataTables_wrapper { 41 | overflow:hidden; 42 | } 43 | 44 | dt, dd { 45 | display:inline-block; 46 | } 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /src/web/public/static/css/sort_asc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thibaudcolas/d4/992726e87a6449cc0be91c78325c79994bdc1407/src/web/public/static/css/sort_asc.png -------------------------------------------------------------------------------- /src/web/public/static/css/sort_asc_disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thibaudcolas/d4/992726e87a6449cc0be91c78325c79994bdc1407/src/web/public/static/css/sort_asc_disabled.png -------------------------------------------------------------------------------- /src/web/public/static/css/sort_both.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thibaudcolas/d4/992726e87a6449cc0be91c78325c79994bdc1407/src/web/public/static/css/sort_both.png -------------------------------------------------------------------------------- /src/web/public/static/css/sort_desc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thibaudcolas/d4/992726e87a6449cc0be91c78325c79994bdc1407/src/web/public/static/css/sort_desc.png -------------------------------------------------------------------------------- /src/web/public/static/css/sort_desc_disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thibaudcolas/d4/992726e87a6449cc0be91c78325c79994bdc1407/src/web/public/static/css/sort_desc_disabled.png -------------------------------------------------------------------------------- /src/web/public/static/js/charts.js: -------------------------------------------------------------------------------- 1 | $('.observation-barchart').each(function (){ 2 | 3 | var index = $(this).attr('data-index'); 4 | 5 | function rand(min, max) { 6 | return Math.floor(Math.random() * (max - min + 1)) + min; 7 | } 8 | 9 | var w = "100%", //width 10 | h = 600, //height 11 | r = 300, //radius 12 | color = d3.scale.category20c(); //builtin range of colors 13 | 14 | var testData = [{"label":'', "value":data[index][1][0]}]; //data[index][0][0] 15 | 16 | for(var i = 2; i < data[index][0].length; i++) { 17 | testData.push({"label":'', "value":data[index][1][i]}); //data[index][0][i] 18 | } 19 | 20 | var vis = d3.select("#innerchart"+index) 21 | .append("svg:svg") //create the SVG element inside the 22 | .data([testData]) //associate our data with the document 23 | .attr("width", w) //set the width and height of our visualization (these will be attributes of the tag 24 | .attr("height", h) 25 | .append("svg:g") //make a group to hold our pie chart 26 | .attr("transform", "translate(" + r + "," + r + ")") //move the center of the pie chart from 0, 0 to radius, radius 27 | 28 | var arc = d3.svg.arc() //this will create elements for us using arc data 29 | .outerRadius(r); 30 | 31 | var pie = d3.layout.pie() //this will create arc data for us given a list of values 32 | .value(function(d) { return d.value; }); //we must tell it out to access the value of each element in our data array 33 | 34 | var arcs = vis.selectAll("g.slice") //this selects all elements with class slice (there aren't any yet) 35 | .data(pie) //associate the generated pie data (an array of arcs, each having startAngle, endAngle and value properties) 36 | .enter() //this will create elements for every "extra" data element that should be associated with a selection. The result is creating a for every object in the data array 37 | .append("svg:g") //create a group to hold each slice (we will have a and a element associated with each slice) 38 | .attr("class", "slice"); //allow us to style things in the slices (like text) 39 | 40 | arcs.append("svg:path") 41 | .attr("fill", function(d, i) { return color(i); } ) //set the color for each slice to be chosen from the color function defined above 42 | .attr("d", arc); //this creates the actual SVG path using the associated data (pie) with the arc drawing function 43 | 44 | arcs.append("svg:text") //add a label to each slice 45 | .attr("transform", function(d) { //set the label's origin to the center of the arc 46 | //we have to make sure to set these before calling arc.centroid 47 | d.innerRadius = 0; 48 | d.outerRadius = r; 49 | return "translate(" + arc.centroid(d) + ")"; //this gives us a pair of coordinates like [50, 50] 50 | }) 51 | .attr("text-anchor", "middle") //center the text on it's origin 52 | .text(function(d, i) { return testData[i].label; }); //get the label from our original data array 53 | 54 | }); 55 | 56 | -------------------------------------------------------------------------------- /src/web/public/static/js/jqgrid.common.js: -------------------------------------------------------------------------------- 1 | var gridData = new Array(); 2 | var searchData = new Array(); 3 | var gridHeader = new Array(); 4 | 5 | function getHeaderColumns() { 6 | var cols = $(".dl-grid-header-column"); 7 | for ( var i = 0; cols[i]; i++) { 8 | gridHeader[i] = normalizeHeaderName(cols[i].innerHTML); 9 | } 10 | } 11 | 12 | function normalizeHeaderName(colTitle) { 13 | return colTitle.replace(/ /g, "_").replace(/&/g, "&").replace(//g, ">"); 15 | } 16 | 17 | function gridSearch(searchString) { 18 | searchData = []; 19 | if ($("#searchColumn").val() == "all") { 20 | searchAllColumns(searchString); 21 | } else { 22 | searchOneColumn(searchString, $("#searchColumn").val()); 23 | } 24 | $("#grid").jqGrid('clearGridData', true); 25 | $("#grid").jqGrid('setGridParam', { 26 | data : searchData 27 | }).trigger("reloadGrid"); 28 | 29 | } 30 | 31 | 32 | function searchAllColumns(searchString) { 33 | if (searchString != "") { 34 | var c = 0; 35 | var obj = gridHeader; 36 | for ( var i = 0; i < gridData.length; i++) { 37 | for ( var k = 0; k < obj.length; k++) { 38 | var toCmp = gridData[i][obj[k]]; 39 | if ((toCmp != undefined) 40 | && (toCmp.toLowerCase().indexOf(searchString) != -1)) { 41 | searchData[c] = gridData[i]; 42 | c++; 43 | k = obj.length; 44 | } 45 | } 46 | } 47 | } else 48 | searchData = gridData; 49 | } 50 | 51 | function searchOneColumn(searchString, searchColumn) { 52 | if (searchString != "") { 53 | var p = normalizeHeaderName(searchColumn); 54 | var c = 0; 55 | for ( var i = 0; i < gridData.length; i++) { 56 | var toCmp = gridData[i][p]; 57 | if ((toCmp != undefined) 58 | && (toCmp.toLowerCase().indexOf(searchString) != -1)) { 59 | searchData[c] = gridData[i]; 60 | c++; 61 | } 62 | } 63 | } else 64 | searchData = gridData; 65 | } 66 | 67 | function htmlTableTojQGrid(params) { 68 | getHeaderColumns(); 69 | jQuery.extend(jQuery.jgrid.defaults, { 70 | caption: $('#searchnav').html(), 71 | rowNum: 10000, 72 | height: 450, 73 | search:{ 74 | caption: '#i18n("grid.search.value.label")' 75 | }, 76 | }); 77 | 78 | var gridParams = { 79 | datatype: "local", 80 | pager: $('#pagernav'), 81 | rowNum: 500, 82 | rowList: [50,100,200,500,1000,2000], 83 | }; 84 | if (params != null) { 85 | gridParams = jsonConcat(params, gridParams); 86 | } 87 | tableToGrid("#grid", gridParams); 88 | $("#grid").trigger("reloadGrid"); 89 | gridData = $("#grid").getRowData(); 90 | // Function called when modifying search text 91 | $("#gridsearch").keyup(function () { 92 | gridSearch($(this).val().toLowerCase()); 93 | }); 94 | // Function called when modifying search Column 95 | $("#searchColumn").change(function() { 96 | gridSearch($("#gridsearch").val().toLowerCase()); 97 | }); 98 | } 99 | 100 | function jsonConcat(o1, o2) { 101 | for ( var key in o2) { 102 | o1[key] = o2[key]; 103 | } 104 | return o1; 105 | } 106 | -------------------------------------------------------------------------------- /src/web/public/static/js/main.coffee: -------------------------------------------------------------------------------- 1 | console.log 'D4 main launched ! ' + projectURI 2 | 3 | # Not very precise day length. Close enough. 4 | oneDay = 24*60*60*1000 5 | 6 | # Might be cooler to do it with Velocity's i18n 7 | timeDisplay = (days) -> 8 | if pageLang is 'fr' then "Il y a #{days} jours" 9 | else "#{days} days ago" 10 | 11 | diffDays = (date) -> 12 | now = new Date().getTime() 13 | thenn = new Date(date).getTime() 14 | Math.round Math.abs (now - thenn) / oneDay 15 | 16 | $ -> 17 | 18 | ### 19 | # Here we setup all of the tooltips / String transformations / popovers. 20 | ### 21 | 22 | $('time').tooltip {title: -> timeDisplay diffDays $(@).html()} 23 | 24 | $('.tooltip-js').tooltip() 25 | 26 | $('dd').popover {trigger: 'hover', placement: 'right'} 27 | 28 | # Displays a glimpse of the dataset's observations. 29 | $('.triples').popover {html: on, trigger: 'hover', placement: 'bottom', title: "#{extractLabel}", content: -> extract[$(@).attr 'data-index']} 30 | 31 | # Transforms long strings to shorter ones. 32 | $('.long-string').each -> 33 | $this = $(@) 34 | if $this.html().length > 35 35 | $this.tooltip {title: $this.html()} 36 | $this.html _.prune $this.html(), 35, '…' 37 | 38 | # Makes displaying URIs easier. 39 | $('.dimension-string').each -> 40 | $this = $(@) 41 | $this.tooltip() 42 | if _.startsWith $this.html(), 'http://' 43 | $this.html _.humanize _.strRightBack $this.html(), '/' 44 | else 45 | $this.html _.titleize _.swapCase $this.html() 46 | return 47 | 48 | # Format numbers according to the current number convention. The int in "measure-int" isn't well choosed. 49 | $('.measure-int').each -> 50 | $this = $(@) 51 | $this.html _.numberFormat (_.toNumber $this.html()), 0, decimalSeparator, thousandsSeparator 52 | 53 | ### 54 | # Datatable translation. 55 | # The big one-liner is ugly but better for CoffeeScript's "magic". 56 | ### 57 | 58 | if pageLang is 'en' 59 | $('.observation-table').dataTable { 60 | 'aoColumns': [{ 'sType': 'html' }, { 'sType': 'data-numeric' }] 61 | } 62 | else 63 | $('.observation-table').dataTable {'aoColumns': [{ 'sType': 'html' }, { 'sType': 'data-numeric' }], 'oLanguage': {'sProcessing': 'Traitement en cours...','sSearch': 'Rechercher :','sLengthMenu': 'Afficher _MENU_ éléments','sInfo': 'Affichage de l\'élement _START_ à _END_ sur _TOTAL_ éléments','sInfoEmpty': 'Affichage de l\'élement 0 à 0 sur 0 éléments','sInfoFiltered': '(filtré de _MAX_ éléments au total)','sInfoPostFix': '','sLoadingRecords': 'Chargement en cours...','sZeroRecords': 'Aucun élément à afficher','sEmptyTable': 'Aucune donnée disponible dans le tableau','oPaginate': {'sFirst': 'Premier', 'sPrevious': 'Précédent', 'sNext': 'Suivant', 'sLast': 'Dernier'},'oAria': {'sSortAscending': ': activer pour trier la colonne par ordre croissant', 'sSortDescending': ': activer pour trier la colonne par ordre décroissant'}}} 64 | console.log '?' 65 | 66 | console.log 'This is awesome !' 67 | -------------------------------------------------------------------------------- /src/web/public/static/js/main.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.4.0 2 | (function() { 3 | var diffDays, oneDay, timeDisplay; 4 | 5 | console.log('D4 launched ! ' + projectURI); 6 | 7 | oneDay = 24 * 60 * 60 * 1000; 8 | 9 | timeDisplay = function(days) { 10 | if (pageLang === 'fr') { 11 | return "Il y a " + days + " jours"; 12 | } else { 13 | return "" + days + " days ago"; 14 | } 15 | }; 16 | 17 | diffDays = function(date) { 18 | var now, thenn; 19 | now = new Date().getTime(); 20 | thenn = new Date(date).getTime(); 21 | return Math.round(Math.abs((now - thenn) / oneDay)); 22 | }; 23 | 24 | $(function() { 25 | $('time').tooltip({ 26 | title: function() { 27 | return timeDisplay(diffDays($(this).html())); 28 | } 29 | }); 30 | $('.tooltip-js').tooltip(); 31 | $('dd').popover({ 32 | trigger: 'hover', 33 | placement: 'right' 34 | }); 35 | $('.triples').popover({ 36 | html: true, 37 | trigger: 'hover', 38 | placement: 'bottom', 39 | title: "" + extractLabel, 40 | content: function() { 41 | return extract[$(this).attr('data-index')]; 42 | } 43 | }); 44 | $('.long-string').each(function() { 45 | var $this; 46 | $this = $(this); 47 | if ($this.html().length > 35) { 48 | $this.tooltip({ 49 | title: $this.html() 50 | }); 51 | return $this.html(_.prune($this.html(), 35, '…')); 52 | } 53 | }); 54 | $('.dimension-string').each(function() { 55 | var $this; 56 | $this = $(this); 57 | $this.tooltip(); 58 | if (_.startsWith($this.html(), 'http://')) { 59 | $this.html(_.humanize(_.strRightBack($this.html(), '/'))); 60 | } else { 61 | $this.html(_.titleize(_.swapCase($this.html()))); 62 | } 63 | }); 64 | $('.measure-int').each(function() { 65 | var $this; 66 | $this = $(this); 67 | return $this.html(_.numberFormat(_.toNumber($this.html()), 0, decimalSeparator, thousandsSeparator)); 68 | }); 69 | if (pageLang === 'en') { 70 | $('.observation-table').dataTable({ 71 | 'aoColumns': [ 72 | { 73 | 'sType': 'html' 74 | }, { 75 | 'sType': 'data-numeric' 76 | } 77 | ] 78 | }); 79 | } else { 80 | $('.observation-table').dataTable({ 81 | 'aoColumns': [ 82 | { 83 | 'sType': 'html' 84 | }, { 85 | 'sType': 'data-numeric' 86 | } 87 | ], 88 | 'oLanguage': { 89 | 'sProcessing': 'Traitement en cours...', 90 | 'sSearch': 'Rechercher :', 91 | 'sLengthMenu': 'Afficher _MENU_ éléments', 92 | 'sInfo': 'Affichage de l\'élement _START_ à _END_ sur _TOTAL_ éléments', 93 | 'sInfoEmpty': 'Affichage de l\'élement 0 à 0 sur 0 éléments', 94 | 'sInfoFiltered': '(filtré de _MAX_ éléments au total)', 95 | 'sInfoPostFix': '', 96 | 'sLoadingRecords': 'Chargement en cours...', 97 | 'sZeroRecords': 'Aucun élément à afficher', 98 | 'sEmptyTable': 'Aucune donnée disponible dans le tableau', 99 | 'oPaginate': { 100 | 'sFirst': 'Premier', 101 | 'sPrevious': 'Précédent', 102 | 'sNext': 'Suivant', 103 | 'sLast': 'Dernier' 104 | }, 105 | 'oAria': { 106 | 'sSortAscending': ': activer pour trier la colonne par ordre croissant', 107 | 'sSortDescending': ': activer pour trier la colonne par ordre décroissant' 108 | } 109 | } 110 | }); 111 | console.log('?'); 112 | } 113 | return console.log('This is awesome !'); 114 | }); 115 | 116 | }).call(this); 117 | -------------------------------------------------------------------------------- /src/web/public/static/js/plugins.coffee: -------------------------------------------------------------------------------- 1 | # Mix in non-conflict functions to Underscore namespace if you want 2 | _.mixin _.str.exports() 3 | 4 | # All functions, include conflict, will be available through _.str object 5 | _.str.include 'Underscore.string', 'string' 6 | 7 | console.log 'Plugins loaded' 8 | 9 | -------------------------------------------------------------------------------- /src/web/public/static/js/plugins.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.4.0 2 | 3 | /* 4 | # Console-lacking browsers. 5 | */ 6 | 7 | 8 | (function() { 9 | 10 | if (!(window.console && console.log)) { 11 | (function() { 12 | var console, method, methods, noop, _i, _len; 13 | noop = function() {}; 14 | methods = ['assert', 'clear', 'count', 'debug', 'dir', 'dirxml', 'error', 'exception', 'group', 'groupCollapsed', 'groupEnd', 'info', 'log', 'markTimeline', 'profile', 'profileEnd', 'markTimeline', 'table', 'time', 'timeEnd', 'timeStamp', 'trace', 'warn']; 15 | console = window.console = {}; 16 | for (_i = 0, _len = methods.length; _i < _len; _i++) { 17 | method = methods[_i]; 18 | console[method] = noop; 19 | } 20 | })(); 21 | } 22 | 23 | _.mixin(_.str.exports()); 24 | 25 | _.str.include('Underscore.string', 'string'); 26 | 27 | console.log('Plugins loaded'); 28 | 29 | }).call(this); 30 | -------------------------------------------------------------------------------- /src/web/public/static/js/stats.coffee: -------------------------------------------------------------------------------- 1 | console.log 'Stats-side' 2 | 3 | # 4 | # This file generates all of the statistical measures. 5 | # 6 | 7 | $ -> 8 | # The mean is quite simple indeed. 9 | mean = (d) -> 10 | sum = _.reduce d, ((acc, v) -> acc + v), 0 11 | sum / d.length 12 | 13 | # Simplest (to code) median : sort, then pick the middle one. 14 | # Might not be the proper way to deal with even datasets. 15 | median = (d) -> 16 | sorted = _.sortBy d, (num) -> num; 17 | med = sorted[Math.floor sorted.length / 2] 18 | d.indexOf med 19 | 20 | # TODO : Bug. Scope bug ? 21 | variance = (tmpMean, d) -> 22 | squareSum = _.reduce d, ((acc, v) -> acc + v * v), 0 23 | (squareSum / d.length) - tmpMean * tmpMean 24 | 25 | # Pretty print of Strings 26 | # Uses Underscore. 27 | prettifyDim = (dim) -> 28 | if _.startsWith dim, 'http://' then _.humanize _.strRightBack dim, '/' else _.titleize _.swapCase dim 29 | 30 | # Pretty print of ints 31 | # Underscore again here. 32 | prettifyObs = (obs) -> 33 | _.numberFormat (_.toNumber Math.round obs), 0, decimalSeparator, thousandsSeparator 34 | 35 | # For each dataset. 36 | # Beware here, this is ugly. We might appreciate Knockout here. 37 | $('.observation-stats').each -> 38 | $this = $(@) 39 | index = $this.attr 'data-index' 40 | 41 | tmpMean = mean data[index][1] 42 | $('.stats' + index + '.stats-mean').html prettifyObs tmpMean 43 | 44 | medianIndex = median data[index][1] 45 | tmpMedian = data[index][1][medianIndex] 46 | tmpMedianDim = prettifyDim data[index][0][medianIndex] 47 | $('.stats' + index + '.stats-median').html (prettifyObs tmpMedian) + ' — ' + tmpMedianDim 48 | 49 | min = _.min data[index][1]; 50 | minDim = prettifyDim data[index][0][data[index][1].indexOf min] 51 | $('.stats' + index + '.stats-min').html (prettifyObs min) + ' — ' + minDim 52 | 53 | max = _.max data[index][1]; 54 | maxDim = prettifyDim data[index][0][data[index][1].indexOf max] 55 | $('.stats' + index + '.stats-max').html (prettifyObs max) + ' — ' + maxDim 56 | 57 | range = max - min 58 | $('.stats' + index + '.stats-range').html (prettifyObs range) 59 | 60 | tmpVar = variance tmpMean, data[index][1] 61 | $('.stats' + index + '.stats-var').html (prettifyObs tmpVar) 62 | 63 | tmpSd = Math.sqrt tmpVar 64 | $('.stats' + index + '.stats-sdev').html (prettifyObs tmpSd) 65 | 66 | # CoffeeScript "magic" 67 | return 68 | -------------------------------------------------------------------------------- /src/web/public/static/js/stats.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.4.0 2 | (function() { 3 | 4 | console.log('Stats-side'); 5 | 6 | $(function() { 7 | var mean, median, prettifyDim, prettifyObs, variance; 8 | mean = function(d) { 9 | var sum; 10 | sum = _.reduce(d, (function(acc, v) { 11 | return acc + v; 12 | }), 0); 13 | return sum / d.length; 14 | }; 15 | median = function(d) { 16 | var med, sorted; 17 | sorted = _.sortBy(d, function(num) { 18 | return num; 19 | }); 20 | med = sorted[Math.floor(sorted.length / 2)]; 21 | return d.indexOf(med); 22 | }; 23 | variance = function(tmpMean, d) { 24 | var squareSum; 25 | squareSum = _.reduce(d, (function(acc, v) { 26 | return acc + v * v; 27 | }), 0); 28 | return (squareSum / d.length) - tmpMean * tmpMean; 29 | }; 30 | prettifyDim = function(dim) { 31 | if (_.startsWith(dim, 'http://')) { 32 | return _.humanize(_.strRightBack(dim, '/')); 33 | } else { 34 | return _.titleize(_.swapCase(dim)); 35 | } 36 | }; 37 | prettifyObs = function(obs) { 38 | return _.numberFormat(_.toNumber(Math.round(obs)), 0, decimalSeparator, thousandsSeparator); 39 | }; 40 | return $('.observation-stats').each(function() { 41 | var $this, index, max, maxDim, medianIndex, min, minDim, range, tmpMean, tmpMedian, tmpMedianDim, tmpSd, tmpVar; 42 | $this = $(this); 43 | index = $this.attr('data-index'); 44 | tmpMean = mean(data[index][1]); 45 | $('.stats' + index + '.stats-mean').html(prettifyObs(tmpMean)); 46 | medianIndex = median(data[index][1]); 47 | tmpMedian = data[index][1][medianIndex]; 48 | tmpMedianDim = prettifyDim(data[index][0][medianIndex]); 49 | $('.stats' + index + '.stats-median').html((prettifyObs(tmpMedian)) + ' — ' + tmpMedianDim); 50 | min = _.min(data[index][1]); 51 | minDim = prettifyDim(data[index][0][data[index][1].indexOf(min)]); 52 | $('.stats' + index + '.stats-min').html((prettifyObs(min)) + ' — ' + minDim); 53 | max = _.max(data[index][1]); 54 | maxDim = prettifyDim(data[index][0][data[index][1].indexOf(max)]); 55 | $('.stats' + index + '.stats-max').html((prettifyObs(max)) + ' — ' + maxDim); 56 | range = max - min; 57 | $('.stats' + index + '.stats-range').html(prettifyObs(range)); 58 | tmpVar = variance(tmpMean, data[index][1]); 59 | $('.stats' + index + '.stats-var').html(prettifyObs(tmpVar)); 60 | tmpSd = Math.sqrt(tmpVar); 61 | $('.stats' + index + '.stats-sdev').html(prettifyObs(tmpSd)); 62 | }); 63 | }); 64 | 65 | }).call(this); 66 | -------------------------------------------------------------------------------- /src/web/public/static/js/vendor/DT_bootstrap.js: -------------------------------------------------------------------------------- 1 | /* Set the defaults for DataTables initialisation */ 2 | $.extend( true, $.fn.dataTable.defaults, { 3 | "sDom": "<'row-fluid'<'span6'l><'span6'f>r>t<'row-fluid'<'span6'i><'span6'p>>", 4 | "sPaginationType": "bootstrap", 5 | "oLanguage": { 6 | "sLengthMenu": "_MENU_ records per page" 7 | } 8 | } ); 9 | 10 | 11 | /* Default class modification */ 12 | $.extend( $.fn.dataTableExt.oStdClasses, { 13 | "sWrapper": "dataTables_wrapper form-inline" 14 | } ); 15 | 16 | 17 | /* API method to get paging information */ 18 | $.fn.dataTableExt.oApi.fnPagingInfo = function ( oSettings ) 19 | { 20 | return { 21 | "iStart": oSettings._iDisplayStart, 22 | "iEnd": oSettings.fnDisplayEnd(), 23 | "iLength": oSettings._iDisplayLength, 24 | "iTotal": oSettings.fnRecordsTotal(), 25 | "iFilteredTotal": oSettings.fnRecordsDisplay(), 26 | "iPage": Math.ceil( oSettings._iDisplayStart / oSettings._iDisplayLength ), 27 | "iTotalPages": Math.ceil( oSettings.fnRecordsDisplay() / oSettings._iDisplayLength ) 28 | }; 29 | }; 30 | 31 | 32 | /* Bootstrap style pagination control */ 33 | $.extend( $.fn.dataTableExt.oPagination, { 34 | "bootstrap": { 35 | "fnInit": function( oSettings, nPaging, fnDraw ) { 36 | var oLang = oSettings.oLanguage.oPaginate; 37 | var fnClickHandler = function ( e ) { 38 | e.preventDefault(); 39 | if ( oSettings.oApi._fnPageChange(oSettings, e.data.action) ) { 40 | fnDraw( oSettings ); 41 | } 42 | }; 43 | 44 | $(nPaging).addClass('pagination').append( 45 | '' 49 | ); 50 | var els = $('a', nPaging); 51 | $(els[0]).bind( 'click.DT', { action: "previous" }, fnClickHandler ); 52 | $(els[1]).bind( 'click.DT', { action: "next" }, fnClickHandler ); 53 | }, 54 | 55 | "fnUpdate": function ( oSettings, fnDraw ) { 56 | var iListLength = 5; 57 | var oPaging = oSettings.oInstance.fnPagingInfo(); 58 | var an = oSettings.aanFeatures.p; 59 | var i, j, sClass, iStart, iEnd, iHalf=Math.floor(iListLength/2); 60 | 61 | if ( oPaging.iTotalPages < iListLength) { 62 | iStart = 1; 63 | iEnd = oPaging.iTotalPages; 64 | } 65 | else if ( oPaging.iPage <= iHalf ) { 66 | iStart = 1; 67 | iEnd = iListLength; 68 | } else if ( oPaging.iPage >= (oPaging.iTotalPages-iHalf) ) { 69 | iStart = oPaging.iTotalPages - iListLength + 1; 70 | iEnd = oPaging.iTotalPages; 71 | } else { 72 | iStart = oPaging.iPage - iHalf + 1; 73 | iEnd = iStart + iListLength - 1; 74 | } 75 | 76 | for ( i=0, iLen=an.length ; i'+j+'') 84 | .insertBefore( $('li:last', an[i])[0] ) 85 | .bind('click', function (e) { 86 | e.preventDefault(); 87 | oSettings._iDisplayStart = (parseInt($('a', this).text(),10)-1) * oPaging.iLength; 88 | fnDraw( oSettings ); 89 | } ); 90 | } 91 | 92 | // Add / remove disabled classes from the static elements 93 | if ( oPaging.iPage === 0 ) { 94 | $('li:first', an[i]).addClass('disabled'); 95 | } else { 96 | $('li:first', an[i]).removeClass('disabled'); 97 | } 98 | 99 | if ( oPaging.iPage === oPaging.iTotalPages-1 || oPaging.iTotalPages === 0 ) { 100 | $('li:last', an[i]).addClass('disabled'); 101 | } else { 102 | $('li:last', an[i]).removeClass('disabled'); 103 | } 104 | } 105 | } 106 | } 107 | } ); 108 | 109 | 110 | /* 111 | * TableTools Bootstrap compatibility 112 | * Required TableTools 2.1+ 113 | */ 114 | if ( $.fn.DataTable.TableTools ) { 115 | // Set the classes that TableTools uses to something suitable for Bootstrap 116 | $.extend( true, $.fn.DataTable.TableTools.classes, { 117 | "container": "DTTT btn-group", 118 | "buttons": { 119 | "normal": "btn", 120 | "disabled": "disabled" 121 | }, 122 | "collection": { 123 | "container": "DTTT_dropdown dropdown-menu", 124 | "buttons": { 125 | "normal": "", 126 | "disabled": "disabled" 127 | } 128 | }, 129 | "print": { 130 | "info": "DTTT_print_info modal" 131 | }, 132 | "select": { 133 | "row": "active" 134 | } 135 | } ); 136 | 137 | // Have the collection use a bootstrap compatible dropdown 138 | $.extend( true, $.fn.DataTable.TableTools.DEFAULTS.oTags, { 139 | "collection": { 140 | "container": "ul", 141 | "button": "li", 142 | "liner": "a" 143 | } 144 | } ); 145 | } 146 | -------------------------------------------------------------------------------- /src/web/public/static/js/vendor/underscore.min.js: -------------------------------------------------------------------------------- 1 | // Underscore.js 1.3.1 2 | // (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc. 3 | // Underscore is freely distributable under the MIT license. 4 | // Portions of Underscore are inspired or borrowed from Prototype, 5 | // Oliver Steele's Functional, and John Resig's Micro-Templating. 6 | // For all details and documentation: 7 | // http://documentcloud.github.com/underscore 8 | (function(){function q(a,c,d){if(a===c)return a!==0||1/a==1/c;if(a==null||c==null)return a===c;if(a._chain)a=a._wrapped;if(c._chain)c=c._wrapped;if(a.isEqual&&b.isFunction(a.isEqual))return a.isEqual(c);if(c.isEqual&&b.isFunction(c.isEqual))return c.isEqual(a);var e=l.call(a);if(e!=l.call(c))return false;switch(e){case "[object String]":return a==String(c);case "[object Number]":return a!=+a?c!=+c:a==0?1/a==1/c:a==+c;case "[object Date]":case "[object Boolean]":return+a==+c;case "[object RegExp]":return a.source== 9 | c.source&&a.global==c.global&&a.multiline==c.multiline&&a.ignoreCase==c.ignoreCase}if(typeof a!="object"||typeof c!="object")return false;for(var f=d.length;f--;)if(d[f]==a)return true;d.push(a);var f=0,g=true;if(e=="[object Array]"){if(f=a.length,g=f==c.length)for(;f--;)if(!(g=f in a==f in c&&q(a[f],c[f],d)))break}else{if("constructor"in a!="constructor"in c||a.constructor!=c.constructor)return false;for(var h in a)if(b.has(a,h)&&(f++,!(g=b.has(c,h)&&q(a[h],c[h],d))))break;if(g){for(h in c)if(b.has(c, 10 | h)&&!f--)break;g=!f}}d.pop();return g}var r=this,G=r._,n={},k=Array.prototype,o=Object.prototype,i=k.slice,H=k.unshift,l=o.toString,I=o.hasOwnProperty,w=k.forEach,x=k.map,y=k.reduce,z=k.reduceRight,A=k.filter,B=k.every,C=k.some,p=k.indexOf,D=k.lastIndexOf,o=Array.isArray,J=Object.keys,s=Function.prototype.bind,b=function(a){return new m(a)};if(typeof exports!=="undefined"){if(typeof module!=="undefined"&&module.exports)exports=module.exports=b;exports._=b}else r._=b;b.VERSION="1.3.1";var j=b.each= 11 | b.forEach=function(a,c,d){if(a!=null)if(w&&a.forEach===w)a.forEach(c,d);else if(a.length===+a.length)for(var e=0,f=a.length;e2;a== 12 | null&&(a=[]);if(y&&a.reduce===y)return e&&(c=b.bind(c,e)),f?a.reduce(c,d):a.reduce(c);j(a,function(a,b,i){f?d=c.call(e,d,a,b,i):(d=a,f=true)});if(!f)throw new TypeError("Reduce of empty array with no initial value");return d};b.reduceRight=b.foldr=function(a,c,d,e){var f=arguments.length>2;a==null&&(a=[]);if(z&&a.reduceRight===z)return e&&(c=b.bind(c,e)),f?a.reduceRight(c,d):a.reduceRight(c);var g=b.toArray(a).reverse();e&&!f&&(c=b.bind(c,e));return f?b.reduce(g,c,d,e):b.reduce(g,c)};b.find=b.detect= 13 | function(a,c,b){var e;E(a,function(a,g,h){if(c.call(b,a,g,h))return e=a,true});return e};b.filter=b.select=function(a,c,b){var e=[];if(a==null)return e;if(A&&a.filter===A)return a.filter(c,b);j(a,function(a,g,h){c.call(b,a,g,h)&&(e[e.length]=a)});return e};b.reject=function(a,c,b){var e=[];if(a==null)return e;j(a,function(a,g,h){c.call(b,a,g,h)||(e[e.length]=a)});return e};b.every=b.all=function(a,c,b){var e=true;if(a==null)return e;if(B&&a.every===B)return a.every(c,b);j(a,function(a,g,h){if(!(e= 14 | e&&c.call(b,a,g,h)))return n});return e};var E=b.some=b.any=function(a,c,d){c||(c=b.identity);var e=false;if(a==null)return e;if(C&&a.some===C)return a.some(c,d);j(a,function(a,b,h){if(e||(e=c.call(d,a,b,h)))return n});return!!e};b.include=b.contains=function(a,c){var b=false;if(a==null)return b;return p&&a.indexOf===p?a.indexOf(c)!=-1:b=E(a,function(a){return a===c})};b.invoke=function(a,c){var d=i.call(arguments,2);return b.map(a,function(a){return(b.isFunction(c)?c||a:a[c]).apply(a,d)})};b.pluck= 15 | function(a,c){return b.map(a,function(a){return a[c]})};b.max=function(a,c,d){if(!c&&b.isArray(a))return Math.max.apply(Math,a);if(!c&&b.isEmpty(a))return-Infinity;var e={computed:-Infinity};j(a,function(a,b,h){b=c?c.call(d,a,b,h):a;b>=e.computed&&(e={value:a,computed:b})});return e.value};b.min=function(a,c,d){if(!c&&b.isArray(a))return Math.min.apply(Math,a);if(!c&&b.isEmpty(a))return Infinity;var e={computed:Infinity};j(a,function(a,b,h){b=c?c.call(d,a,b,h):a;bd?1:0}),"value")};b.groupBy=function(a,c){var d={},e=b.isFunction(c)?c:function(a){return a[c]};j(a,function(a,b){var c=e(a,b);(d[c]||(d[c]=[])).push(a)});return d};b.sortedIndex=function(a, 17 | c,d){d||(d=b.identity);for(var e=0,f=a.length;e>1;d(a[g])=0})})};b.difference=function(a){var c=b.flatten(i.call(arguments,1));return b.filter(a,function(a){return!b.include(c,a)})};b.zip=function(){for(var a=i.call(arguments),c=b.max(b.pluck(a,"length")),d=Array(c),e=0;e=0;d--)b=[a[d].apply(this,b)];return b[0]}}; 24 | b.after=function(a,b){return a<=0?b():function(){if(--a<1)return b.apply(this,arguments)}};b.keys=J||function(a){if(a!==Object(a))throw new TypeError("Invalid object");var c=[],d;for(d in a)b.has(a,d)&&(c[c.length]=d);return c};b.values=function(a){return b.map(a,b.identity)};b.functions=b.methods=function(a){var c=[],d;for(d in a)b.isFunction(a[d])&&c.push(d);return c.sort()};b.extend=function(a){j(i.call(arguments,1),function(b){for(var d in b)a[d]=b[d]});return a};b.defaults=function(a){j(i.call(arguments, 25 | 1),function(b){for(var d in b)a[d]==null&&(a[d]=b[d])});return a};b.clone=function(a){return!b.isObject(a)?a:b.isArray(a)?a.slice():b.extend({},a)};b.tap=function(a,b){b(a);return a};b.isEqual=function(a,b){return q(a,b,[])};b.isEmpty=function(a){if(b.isArray(a)||b.isString(a))return a.length===0;for(var c in a)if(b.has(a,c))return false;return true};b.isElement=function(a){return!!(a&&a.nodeType==1)};b.isArray=o||function(a){return l.call(a)=="[object Array]"};b.isObject=function(a){return a===Object(a)}; 26 | b.isArguments=function(a){return l.call(a)=="[object Arguments]"};if(!b.isArguments(arguments))b.isArguments=function(a){return!(!a||!b.has(a,"callee"))};b.isFunction=function(a){return l.call(a)=="[object Function]"};b.isString=function(a){return l.call(a)=="[object String]"};b.isNumber=function(a){return l.call(a)=="[object Number]"};b.isNaN=function(a){return a!==a};b.isBoolean=function(a){return a===true||a===false||l.call(a)=="[object Boolean]"};b.isDate=function(a){return l.call(a)=="[object Date]"}; 27 | b.isRegExp=function(a){return l.call(a)=="[object RegExp]"};b.isNull=function(a){return a===null};b.isUndefined=function(a){return a===void 0};b.has=function(a,b){return I.call(a,b)};b.noConflict=function(){r._=G;return this};b.identity=function(a){return a};b.times=function(a,b,d){for(var e=0;e/g,">").replace(/"/g,""").replace(/'/g,"'").replace(/\//g,"/")};b.mixin=function(a){j(b.functions(a), 28 | function(c){K(c,b[c]=a[c])})};var L=0;b.uniqueId=function(a){var b=L++;return a?a+b:b};b.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var t=/.^/,u=function(a){return a.replace(/\\\\/g,"\\").replace(/\\'/g,"'")};b.template=function(a,c){var d=b.templateSettings,d="var __p=[],print=function(){__p.push.apply(__p,arguments);};with(obj||{}){__p.push('"+a.replace(/\\/g,"\\\\").replace(/'/g,"\\'").replace(d.escape||t,function(a,b){return"',_.escape("+ 29 | u(b)+"),'"}).replace(d.interpolate||t,function(a,b){return"',"+u(b)+",'"}).replace(d.evaluate||t,function(a,b){return"');"+u(b).replace(/[\r\n\t]/g," ")+";__p.push('"}).replace(/\r/g,"\\r").replace(/\n/g,"\\n").replace(/\t/g,"\\t")+"');}return __p.join('');",e=new Function("obj","_",d);return c?e(c,b):function(a){return e.call(this,a,b)}};b.chain=function(a){return b(a).chain()};var m=function(a){this._wrapped=a};b.prototype=m.prototype;var v=function(a,c){return c?b(a).chain():a},K=function(a,c){m.prototype[a]= 30 | function(){var a=i.call(arguments);H.call(a,this._wrapped);return v(c.apply(b,a),this._chain)}};b.mixin(b);j("pop,push,reverse,shift,sort,splice,unshift".split(","),function(a){var b=k[a];m.prototype[a]=function(){var d=this._wrapped;b.apply(d,arguments);var e=d.length;(a=="shift"||a=="splice")&&e===0&&delete d[0];return v(d,this._chain)}});j(["concat","join","slice"],function(a){var b=k[a];m.prototype[a]=function(){return v(b.apply(this._wrapped,arguments),this._chain)}});m.prototype.chain=function(){this._chain= 31 | true;return this};m.prototype.value=function(){return this._wrapped}}).call(this); -------------------------------------------------------------------------------- /src/web/public/static/js/vendor/underscore.string.min.js: -------------------------------------------------------------------------------- 1 | !function(e,t){"use strict";var n=t.prototype.trim,r=t.prototype.trimRight,i=t.prototype.trimLeft,s=function(e){return e*1||0},o=function(e,t){if(t<1)return"";var n="";while(t>0)t&1&&(n+=e),t>>=1,e+=e;return n},u=[].slice,a=function(e){return e==null?"\\s":e.source?e.source:"["+p.escapeRegExp(e)+"]"},f={lt:"<",gt:">",quot:'"',apos:"'",amp:"&"},l={};for(var c in f)l[f[c]]=c;var h=function(){function e(e){return Object.prototype.toString.call(e).slice(8,-1).toLowerCase()}var n=o,r=function(){return r.cache.hasOwnProperty(arguments[0])||(r.cache[arguments[0]]=r.parse(arguments[0])),r.format.call(null,r.cache[arguments[0]],arguments)};return r.format=function(r,i){var s=1,o=r.length,u="",a,f=[],l,c,p,d,v,m;for(l=0;l=0?"+"+a:a,v=p[4]?p[4]=="0"?"0":p[4].charAt(1):" ",m=p[6]-t(a).length,d=p[6]?n(v,m):"",f.push(p[5]?a+d:d+a)}}return f.join("")},r.cache={},r.parse=function(e){var t=e,n=[],r=[],i=0;while(t){if((n=/^[^\x25]+/.exec(t))!==null)r.push(n[0]);else if((n=/^\x25{2}/.exec(t))!==null)r.push("%");else{if((n=/^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(t))===null)throw new Error("[_.sprintf] huh?");if(n[2]){i|=1;var s=[],o=n[2],u=[];if((u=/^([a-z_][a-z_\d]*)/i.exec(o))===null)throw new Error("[_.sprintf] huh?");s.push(u[1]);while((o=o.substring(u[0].length))!=="")if((u=/^\.([a-z_][a-z_\d]*)/i.exec(o))!==null)s.push(u[1]);else{if((u=/^\[(\d+)\]/.exec(o))===null)throw new Error("[_.sprintf] huh?");s.push(u[1])}n[2]=s}else i|=2;if(i===3)throw new Error("[_.sprintf] mixing positional and named placeholders is not (yet) supported");r.push(n)}t=t.substring(n[0].length)}return r},r}(),p={VERSION:"2.3.0",isBlank:function(e){return e==null&&(e=""),/^\s*$/.test(e)},stripTags:function(e){return e==null?"":t(e).replace(/<\/?[^>]+>/g,"")},capitalize:function(e){return e=e==null?"":t(e),e.charAt(0).toUpperCase()+e.slice(1)},chop:function(e,n){return e==null?[]:(e=t(e),n=~~n,n>0?e.match(new RegExp(".{1,"+n+"}","g")):[e])},clean:function(e){return p.strip(e).replace(/\s+/g," ")},count:function(e,n){return e==null||n==null?0:t(e).split(n).length-1},chars:function(e){return e==null?[]:t(e).split("")},swapCase:function(e){return e==null?"":t(e).replace(/\S/g,function(e){return e===e.toUpperCase()?e.toLowerCase():e.toUpperCase()})},escapeHTML:function(e){return e==null?"":t(e).replace(/[&<>"']/g,function(e){return"&"+l[e]+";"})},unescapeHTML:function(e){return e==null?"":t(e).replace(/\&([^;]+);/g,function(e,n){var r;return n in f?f[n]:(r=n.match(/^#x([\da-fA-F]+)$/))?t.fromCharCode(parseInt(r[1],16)):(r=n.match(/^#(\d+)$/))?t.fromCharCode(~~r[1]):e})},escapeRegExp:function(e){return e==null?"":t(e).replace(/([.*+?^=!:${}()|[\]\/\\])/g,"\\$1")},splice:function(e,t,n,r){var i=p.chars(e);return i.splice(~~t,~~n,r),i.join("")},insert:function(e,t,n){return p.splice(e,t,0,n)},include:function(e,n){return n===""?!0:e==null?!1:t(e).indexOf(n)!==-1},join:function(){var e=u.call(arguments),t=e.shift();return t==null&&(t=""),e.join(t)},lines:function(e){return e==null?[]:t(e).split("\n")},reverse:function(e){return p.chars(e).reverse().join("")},startsWith:function(e,n){return n===""?!0:e==null||n==null?!1:(e=t(e),n=t(n),e.length>=n.length&&e.slice(0,n.length)===n)},endsWith:function(e,n){return n===""?!0:e==null||n==null?!1:(e=t(e),n=t(n),e.length>=n.length&&e.slice(e.length-n.length)===n)},succ:function(e){return e==null?"":(e=t(e),e.slice(0,-1)+t.fromCharCode(e.charCodeAt(e.length-1)+1))},titleize:function(e){return e==null?"":t(e).replace(/(?:^|\s)\S/g,function(e){return e.toUpperCase()})},camelize:function(e){return p.trim(e).replace(/[-_\s]+(.)?/g,function(e,t){return t.toUpperCase()})},underscored:function(e){return p.trim(e).replace(/([a-z\d])([A-Z]+)/g,"$1_$2").replace(/[-\s]+/g,"_").toLowerCase()},dasherize:function(e){return p.trim(e).replace(/([A-Z])/g,"-$1").replace(/[-_\s]+/g,"-").toLowerCase()},classify:function(e){return p.titleize(t(e).replace(/_/g," ")).replace(/\s/g,"")},humanize:function(e){return p.capitalize(p.underscored(e).replace(/_id$/,"").replace(/_/g," "))},trim:function(e,r){return e==null?"":!r&&n?n.call(e):(r=a(r),t(e).replace(new RegExp("^"+r+"+|"+r+"+$","g"),""))},ltrim:function(e,n){return e==null?"":!n&&i?i.call(e):(n=a(n),t(e).replace(new RegExp("^"+n+"+"),""))},rtrim:function(e,n){return e==null?"":!n&&r?r.call(e):(n=a(n),t(e).replace(new RegExp(n+"+$"),""))},truncate:function(e,n,r){return e==null?"":(e=t(e),r=r||"...",n=~~n,e.length>n?e.slice(0,n)+r:e)},prune:function(e,n,r){if(e==null)return"";e=t(e),n=~~n,r=r!=null?t(r):"...";if(e.length<=n)return e;var i=function(e){return e.toUpperCase()!==e.toLowerCase()?"A":" "},s=e.slice(0,n+1).replace(/.(?=\W*\w*$)/g,i);return s.slice(s.length-2).match(/\w\w/)?s=s.replace(/\s*\S+$/,""):s=p.rtrim(s.slice(0,s.length-1)),(s+r).length>e.length?e:e.slice(0,s.length)+r},words:function(e,t){return p.isBlank(e)?[]:p.trim(e,t).split(t||/\s+/)},pad:function(e,n,r,i){e=e==null?"":t(e),n=~~n;var s=0;r?r.length>1&&(r=r.charAt(0)):r=" ";switch(i){case"right":return s=n-e.length,e+o(r,s);case"both":return s=n-e.length,o(r,Math.ceil(s/2))+e+o(r,Math.floor(s/2));default:return s=n-e.length,o(r,s)+e}},lpad:function(e,t,n){return p.pad(e,t,n)},rpad:function(e,t,n){return p.pad(e,t,n,"right")},lrpad:function(e,t,n){return p.pad(e,t,n,"both")},sprintf:h,vsprintf:function(e,t){return t.unshift(e),h.apply(null,t)},toNumber:function(e,n){if(e==null||e=="")return 0;e=t(e);var r=s(s(e).toFixed(~~n));return r===0&&!e.match(/^0+$/)?Number.NaN:r},numberFormat:function(e,t,n,r){if(isNaN(e)||e==null)return"";e=e.toFixed(~~t),r=r||",";var i=e.split("."),s=i[0],o=i[1]?(n||".")+i[1]:"";return s.replace(/(\d)(?=(?:\d{3})+$)/g,"$1"+r)+o},strRight:function(e,n){if(e==null)return"";e=t(e),n=n!=null?t(n):n;var r=n?e.indexOf(n):-1;return~r?e.slice(r+n.length,e.length):e},strRightBack:function(e,n){if(e==null)return"";e=t(e),n=n!=null?t(n):n;var r=n?e.lastIndexOf(n):-1;return~r?e.slice(r+n.length,e.length):e},strLeft:function(e,n){if(e==null)return"";e=t(e),n=n!=null?t(n):n;var r=n?e.indexOf(n):-1;return~r?e.slice(0,r):e},strLeftBack:function(e,t){if(e==null)return"";e+="",t=t!=null?""+t:t;var n=e.lastIndexOf(t);return~n?e.slice(0,n):e},toSentence:function(e,t,n,r){t=t||", ",n=n||" and ";var i=e.slice(),s=i.pop();return e.length>2&&r&&(n=p.rtrim(t)+n),i.length?i.join(t)+n+s:s},toSentenceSerial:function(){var e=u.call(arguments);return e[3]=!0,p.toSentence.apply(p,e)},slugify:function(e){if(e==null)return"";var n="ąàáäâãåæćęèéëêìíïîłńòóöôõøùúüûñçżź",r="aaaaaaaaceeeeeiiiilnoooooouuuunczz",i=new RegExp(a(n),"g");return e=t(e).toLowerCase().replace(i,function(e){var t=n.indexOf(e);return r.charAt(t)||"-"}),p.dasherize(e.replace(/[^\w\s-]/g,""))},surround:function(e,t){return[t,e,t].join("")},quote:function(e){return p.surround(e,'"')},exports:function(){var e={};for(var t in this){if(!this.hasOwnProperty(t)||t.match(/^(?:include|contains|reverse)$/))continue;e[t]=this[t]}return e},repeat:function(e,n,r){if(e==null)return"";n=~~n;if(r==null)return o(t(e),n);for(var i=[];n>0;i[--n]=e);return i.join(r)},levenshtein:function(e,n){if(e==null&&n==null)return 0;if(e==null)return t(n).length;if(n==null)return t(e).length;e=t(e),n=t(n);var r=[],i,s;for(var o=0;o<=n.length;o++)for(var u=0;u<=e.length;u++)o&&u?e.charAt(u-1)===n.charAt(o-1)?s=i:s=Math.min(r[u],r[u-1],i)+1:s=o+u,i=r[u],r[u]=s;return r.pop()}};p.strip=p.trim,p.lstrip=p.ltrim,p.rstrip=p.rtrim,p.center=p.lrpad,p.rjust=p.lpad,p.ljust=p.rpad,p.contains=p.include,p.q=p.quote,typeof exports!="undefined"?(typeof module!="undefined"&&module.exports&&(module.exports=p),exports._s=p):typeof define=="function"&&define.amd?define("underscore.string",[],function(){return p}):(e._=e._||{},e._.string=e._.str=p)}(this,String); -------------------------------------------------------------------------------- /src/web/resources_en.properties: -------------------------------------------------------------------------------- 1 | # English textual content for the D4 QB module GUI. 2 | # ============================================================================ 3 | # Principal textual content. 4 | d4.title = D4 5 | d4.button = D4 6 | d4.description = B 7 | # 8 | # ============================================================================ 9 | # Generic labels. 10 | page.lang = en 11 | project.label = {0} Project 12 | update.confirm = The data will be modified permanently. 13 | send.label = Go ! 14 | help.label = Help 15 | help.title = Show the help menu 16 | example.label = Example 17 | example.title = Display an example 18 | cancel.label = Cancel 19 | cancel.title = Go back to the 20 | back.label = Go back 21 | # 22 | tab.metadata = Metadata 23 | tab.table = Data Table 24 | tab.chart = Chart 25 | tab.stats = Statistics 26 | # 27 | # ============================================================================ 28 | # Metadata. 29 | dataset.illustration = Illustration 30 | dataset.by = by 31 | dataset.source = source 32 | dataset.license = license 33 | dataset.published = published 34 | dataset.modified = modified 35 | dataset.periodicity = frequency 36 | dataset.format = format 37 | dataset.uri = URI 38 | dataset.triples = observations 39 | dataset.observations = Observations 40 | dataset.coverage = coverage 41 | dataset.dimension = dimension 42 | dataset.measure = measure 43 | dataset.extract = Extract 44 | # 45 | # ============================================================================ 46 | # Vue statistiques. 47 | stats.position = Measures of position 48 | stats.dispersion = Measures of spread 49 | stats.mean = Mean 50 | stats.mean.desc = The mean is the value that each measure in the data should have in order to change them without changing their total. 51 | stats.mode = Mode 52 | stats.mode.desc = The mode is the value that appears most often in a set of data. 53 | stats.median = Median 54 | stats.median.desc = The median is the value separating the upper half and the lower half of the data. 55 | stats.min = Minimum 56 | stats.min.desc = Smallest value in the data. 57 | stats.max = Maximum 58 | stats.max.desc = Largest value in the data. 59 | stats.range = Range 60 | stats.range.desc = The range how much the min and max values are far apart. 61 | stats.var = Variance 62 | stats.var.desc = A measure of how far a set of numbers is spread out. 63 | stats.sdev = Standard Deviation 64 | stats.sdev.desc = A measure of how a set of numbers is spread out. 65 | # 66 | separator.decimal = . 67 | separator.thousands = , 68 | # 69 | warning.label = Warning 70 | warning.info = This module requires JavaScript to be enabled in order to work properly. 71 | -------------------------------------------------------------------------------- /src/web/resources_fr.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thibaudcolas/d4/992726e87a6449cc0be91c78325c79994bdc1407/src/web/resources_fr.properties --------------------------------------------------------------------------------