Chris@20
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
Chris@20
|
2
|
Chris@20
|
3 #include "Objects.h"
|
Chris@20
|
4 #include "TypeRegistrar.h"
|
Chris@20
|
5
|
Chris@20
|
6 #include <dataquay/BasicStore.h>
|
Chris@31
|
7 #include <dataquay/TransactionalStore.h>
|
Chris@20
|
8 #include <dataquay/RDFException.h>
|
Chris@28
|
9 #include <dataquay/objectmapper/ObjectLoader.h>
|
Chris@28
|
10 #include <dataquay/objectmapper/ObjectStorer.h>
|
Chris@31
|
11 #include <dataquay/objectmapper/ObjectMapper.h>
|
Chris@28
|
12 #include <dataquay/objectmapper/TypeMapping.h>
|
Chris@20
|
13
|
Chris@20
|
14 #include <QMultiMap>
|
Chris@20
|
15 #include <QFileInfo>
|
Chris@20
|
16
|
Chris@20
|
17 #include <iostream>
|
Chris@20
|
18
|
Chris@20
|
19 using namespace Dataquay;
|
Chris@20
|
20 using namespace ClassicalData;
|
Chris@20
|
21 using namespace std;
|
Chris@20
|
22
|
Chris@53
|
23 /*
|
Chris@20
|
24 ostream &operator<<(ostream &target, const QString &str)
|
Chris@20
|
25 {
|
Chris@20
|
26 return target << str.toLocal8Bit().data();
|
Chris@20
|
27 }
|
Chris@20
|
28
|
Chris@20
|
29 ostream &operator<<(ostream &target, const QUrl &u)
|
Chris@20
|
30 {
|
Chris@20
|
31 return target << "<" << u.toString() << ">";
|
Chris@20
|
32 }
|
Chris@53
|
33 */
|
Chris@20
|
34
|
Chris@20
|
35 bool
|
Chris@20
|
36 load(BasicStore *store, QString fileName)
|
Chris@20
|
37 {
|
Chris@20
|
38 QUrl url = QUrl::fromLocalFile(fileName);
|
Chris@20
|
39
|
Chris@20
|
40 cerr << "Importing from URL " << url << " ...";
|
Chris@20
|
41 try {
|
Chris@24
|
42 store->import(url, BasicStore::ImportPermitDuplicates);
|
Chris@20
|
43 } catch (RDFException e) {
|
Chris@24
|
44 cerr << " retrying with explicit ntriples type...";
|
Chris@24
|
45 try {
|
Chris@24
|
46 store->import(url, BasicStore::ImportPermitDuplicates, "ntriples");
|
Chris@24
|
47 } catch (RDFException e) {
|
Chris@24
|
48 cerr << "failed" << endl;
|
Chris@24
|
49 cerr << "Import failed: " << e.what() << endl;
|
Chris@24
|
50 return false;
|
Chris@24
|
51 }
|
Chris@20
|
52 }
|
Chris@20
|
53
|
Chris@20
|
54 cerr << " done" << endl;
|
Chris@20
|
55 return true;
|
Chris@20
|
56 }
|
Chris@20
|
57
|
Chris@20
|
58 void
|
Chris@20
|
59 usage(char *name)
|
Chris@20
|
60 {
|
Chris@20
|
61 int s = 0;
|
Chris@20
|
62 for (int i = 0; name[i]; ++i) if (name[i] == '/') s = i + 1;
|
Chris@20
|
63 name = name + s;
|
Chris@24
|
64 cerr << "Usage:" << endl;
|
Chris@24
|
65 cerr << " " << name << " <input-rdf-file> list" << endl;
|
Chris@24
|
66 cerr << " " << name << " <input-rdf-file> list-uris" << endl;
|
Chris@24
|
67 cerr << " " << name << " <input-rdf-file> show <uri> [<uri> ...]" << endl;
|
Chris@24
|
68 cerr << " " << name << " <input-rdf-file> search <text>" << endl;
|
Chris@24
|
69 cerr << " " << name << " <input-rdf-file> match <text>" << endl;
|
Chris@24
|
70 cerr << " " << name << " <input-rdf-file> merge <target-uri> <dup> [<dup> ...]" << endl;
|
Chris@42
|
71 cerr << " " << name << " <input-rdf-file> write" << endl;
|
Chris@20
|
72 exit(-1);
|
Chris@20
|
73 }
|
Chris@20
|
74
|
Chris@20
|
75 static QList<Composer *> allComposers;
|
Chris@20
|
76 static QMap<Composer *, QSet<Work *> > worksMap;
|
Chris@20
|
77
|
Chris@20
|
78 void
|
Chris@20
|
79 show(Composer *c)
|
Chris@20
|
80 {
|
Chris@20
|
81 cout << c->property("uri").value<Uri>() << endl;
|
Chris@20
|
82 cout << c->getSortName(true);
|
Chris@20
|
83 QString d = c->getDisplayDates();
|
Chris@20
|
84 if (d != "") cout << " (" << d << ")";
|
Chris@20
|
85 if (!c->nationality().empty() || c->period() != "") {
|
Chris@20
|
86 cout << " [";
|
Chris@20
|
87 bool first = true;
|
Chris@20
|
88 foreach (QString n, c->nationality()) {
|
Chris@20
|
89 if (!first) cout << "/";
|
Chris@20
|
90 cout << n;
|
Chris@20
|
91 first = false;
|
Chris@20
|
92 }
|
Chris@20
|
93 if (c->period() != "") {
|
Chris@20
|
94 if (!first) cout << ", ";
|
Chris@20
|
95 cout << c->period();
|
Chris@20
|
96 }
|
Chris@20
|
97 cout << "]";
|
Chris@20
|
98 }
|
Chris@20
|
99 if (c->gender() != "") {
|
Chris@20
|
100 cout << " *" << c->gender();
|
Chris@20
|
101 }
|
Chris@20
|
102 if (!worksMap[c].empty()) {
|
Chris@20
|
103 cout << " [" << worksMap[c].size() << " work(s)]";
|
Chris@20
|
104 }
|
Chris@20
|
105 cout << endl;
|
Chris@20
|
106 foreach (QString a, c->aliases()) {
|
Chris@20
|
107 cout << " - " << a << endl;
|
Chris@20
|
108 }
|
Chris@20
|
109 if (c->remarks() != "") {
|
Chris@20
|
110 cout << " " << c->remarks() << endl;
|
Chris@20
|
111 }
|
Chris@20
|
112 foreach (Document *d, c->pages()) {
|
Chris@24
|
113 cout << d->siteName() << " -> " << d->uri() << endl;
|
Chris@24
|
114 }
|
Chris@24
|
115 foreach (Uri u, c->otherURIs()) {
|
Chris@24
|
116 cout << "Same as " << u << endl;
|
Chris@20
|
117 }
|
Chris@20
|
118 }
|
Chris@20
|
119
|
Chris@20
|
120 void
|
Chris@20
|
121 showBrief(Composer *c)
|
Chris@20
|
122 {
|
Chris@20
|
123 cout << c->property("uri").value<Uri>() << endl;
|
Chris@20
|
124 cout << c->getSortName(false);
|
Chris@20
|
125 QString d = c->getDisplayDates();
|
Chris@20
|
126 if (d != "") cout << " (" << d << ")";
|
Chris@20
|
127 if (!c->nationality().empty() || c->period() != "") {
|
Chris@20
|
128 cout << " [";
|
Chris@20
|
129 bool first = true;
|
Chris@20
|
130 foreach (QString n, c->nationality()) {
|
Chris@20
|
131 if (!first) cout << "/";
|
Chris@20
|
132 cout << n;
|
Chris@20
|
133 first = false;
|
Chris@20
|
134 }
|
Chris@20
|
135 if (c->period() != "") {
|
Chris@20
|
136 if (!first) cout << " ";
|
Chris@20
|
137 cout << c->period();
|
Chris@20
|
138 }
|
Chris@20
|
139 cout << "]";
|
Chris@20
|
140 }
|
Chris@20
|
141 if (c->gender() != "") {
|
Chris@20
|
142 cout << " *" << c->gender();
|
Chris@20
|
143 }
|
Chris@20
|
144 if (!worksMap[c].empty()) {
|
Chris@20
|
145 cout << " [" << worksMap[c].size() << " work(s)]";
|
Chris@20
|
146 }
|
Chris@20
|
147 cout << endl;
|
Chris@20
|
148 }
|
Chris@20
|
149
|
Chris@20
|
150 void
|
Chris@20
|
151 listBrief(QList<Composer *> composers)
|
Chris@20
|
152 {
|
Chris@20
|
153 QMultiMap<QString, Composer *> sorted;
|
Chris@20
|
154 foreach (Composer *c, composers) {
|
Chris@20
|
155 sorted.insert(c->getSortName(false), c);
|
Chris@20
|
156 }
|
Chris@20
|
157 foreach (Composer *c, sorted) {
|
Chris@20
|
158 showBrief(c);
|
Chris@20
|
159 }
|
Chris@20
|
160 }
|
Chris@20
|
161
|
Chris@20
|
162 void
|
Chris@20
|
163 listUris(QList<Composer *> composers)
|
Chris@20
|
164 {
|
Chris@20
|
165 QMultiMap<Uri, Composer *> sorted;
|
Chris@20
|
166 foreach (Composer *c, composers) {
|
Chris@20
|
167 sorted.insert(c->property("uri").value<Uri>(), c);
|
Chris@20
|
168 }
|
Chris@20
|
169 foreach (Uri uri, sorted.keys()) {
|
Chris@20
|
170 cout << uri << endl;
|
Chris@20
|
171 }
|
Chris@20
|
172 }
|
Chris@20
|
173
|
Chris@20
|
174 void
|
Chris@20
|
175 showSearchResults(QMultiMap<float, Composer *> matches, int count)
|
Chris@20
|
176 {
|
Chris@20
|
177 int n = 0;
|
Chris@20
|
178 for (QMultiMap<float, Composer *>::const_iterator i = matches.end();
|
Chris@20
|
179 i != matches.begin(); ) {
|
Chris@20
|
180 --i;
|
Chris@20
|
181 if (i.key() <= 0) continue;
|
Chris@20
|
182 cout << endl;
|
Chris@20
|
183 if (n == 0) {
|
Chris@20
|
184 cout << "Best match:" << endl;
|
Chris@20
|
185 } else if (n == 1) {
|
Chris@20
|
186 cout << "Other candidate(s):" << endl;
|
Chris@20
|
187 }
|
Chris@20
|
188 cout << "[" << i.key() << "] ";
|
Chris@20
|
189 if (n == 0) show(i.value());
|
Chris@20
|
190 else showBrief(i.value());
|
Chris@20
|
191 if (++n > count) break;
|
Chris@20
|
192 }
|
Chris@20
|
193 if (n == 0) cout << "No matches" << endl;
|
Chris@20
|
194 cout << endl;
|
Chris@20
|
195 }
|
Chris@20
|
196
|
Chris@20
|
197 void
|
Chris@20
|
198 search(QString typing)
|
Chris@20
|
199 {
|
Chris@28
|
200 cout << "Searching (quick) for: " << typing << endl;
|
Chris@20
|
201 QMultiMap<float, Composer *> matches;
|
Chris@20
|
202 foreach (Composer *c, allComposers) {
|
Chris@28
|
203 float value = c->matchTypingQuick(typing);
|
Chris@28
|
204 matches.insert(value, c);
|
Chris@28
|
205 }
|
Chris@28
|
206 showSearchResults(matches, 5);
|
Chris@28
|
207
|
Chris@28
|
208 cout << "Searching (slow) for: " << typing << endl;
|
Chris@28
|
209 matches.clear();
|
Chris@28
|
210 foreach (Composer *c, allComposers) {
|
Chris@20
|
211 float value = c->matchTyping(typing);
|
Chris@20
|
212 matches.insert(value, c);
|
Chris@20
|
213 }
|
Chris@20
|
214 showSearchResults(matches, 5);
|
Chris@20
|
215 }
|
Chris@20
|
216
|
Chris@20
|
217 void
|
Chris@20
|
218 match(QString text)
|
Chris@20
|
219 {
|
Chris@20
|
220 cout << "Matching: " << text << endl;
|
Chris@20
|
221 QMultiMap<float, Composer *> matches;
|
Chris@20
|
222 QRegExp sre("[\\., -]+");
|
Chris@20
|
223 QStringList elements = text.toLower().split(sre, QString::SkipEmptyParts);
|
Chris@20
|
224 foreach (Composer *c, allComposers) {
|
Chris@20
|
225 float value = c->matchFuzzyName(elements);
|
Chris@20
|
226 matches.insert(value, c);
|
Chris@20
|
227 }
|
Chris@20
|
228 showSearchResults(matches, 5);
|
Chris@20
|
229 }
|
Chris@20
|
230
|
Chris@24
|
231 QList<Composer *>
|
Chris@24
|
232 matchWildcard(QString text)
|
Chris@24
|
233 {
|
Chris@24
|
234 if (!text.contains('/') && !text.contains('*')) {
|
Chris@24
|
235 text = "*" + text + "*";
|
Chris@24
|
236 }
|
Chris@24
|
237 QRegExp re(text, Qt::CaseInsensitive, QRegExp::Wildcard);
|
Chris@24
|
238 QList<Composer *> results;
|
Chris@24
|
239 foreach (Composer *c, allComposers) {
|
Chris@24
|
240 if (re.exactMatch(c->property("uri").value<Uri>().toString())) {
|
Chris@24
|
241 results.push_back(c);
|
Chris@24
|
242 }
|
Chris@24
|
243 }
|
Chris@24
|
244 return results;
|
Chris@24
|
245 }
|
Chris@24
|
246
|
Chris@24
|
247 Composer *
|
Chris@24
|
248 matchSingle(QString text)
|
Chris@24
|
249 {
|
Chris@24
|
250 QList<Composer *> matches = matchWildcard(text);
|
Chris@24
|
251 if (matches.empty()) {
|
Chris@24
|
252 cerr << "matchSingle: No matches for " << text << endl;
|
Chris@24
|
253 return 0;
|
Chris@24
|
254 } else if (matches.size() > 1) {
|
Chris@24
|
255 cerr << "matchSingle: Multiple matches for " << text << endl;
|
Chris@24
|
256 return 0;
|
Chris@24
|
257 }
|
Chris@24
|
258 return matches[0];
|
Chris@24
|
259 }
|
Chris@24
|
260
|
Chris@20
|
261 void
|
Chris@20
|
262 showWildcard(QString text)
|
Chris@20
|
263 {
|
Chris@20
|
264 cout << "Showing URI or wildcard: " << text << endl;
|
Chris@24
|
265 cout << endl;
|
Chris@24
|
266 foreach (Composer *c, matchWildcard(text)) {
|
Chris@24
|
267 show(c);
|
Chris@24
|
268 cout << endl;
|
Chris@20
|
269 }
|
Chris@24
|
270 }
|
Chris@24
|
271
|
Chris@24
|
272 void
|
Chris@26
|
273 merge(Composer *target, QList<Composer *> sources, BasicStore *store)
|
Chris@24
|
274 {
|
Chris@24
|
275 cout << "Merging into this composer record:" << endl << endl;
|
Chris@24
|
276 show(target);
|
Chris@24
|
277 cout << endl << "... the following composer record(s):" << endl;
|
Chris@24
|
278 foreach (Composer *c, sources) {
|
Chris@24
|
279 cout << endl;
|
Chris@24
|
280 show(c);
|
Chris@24
|
281 target->mergeFrom(c);
|
Chris@26
|
282
|
Chris@26
|
283 QSet<Work *> works = worksMap[c];
|
Chris@26
|
284 foreach (Work *w, works) {
|
Chris@26
|
285 w->composition()->setComposer(target);
|
Chris@26
|
286 }
|
Chris@26
|
287 worksMap[target].unite(works);
|
Chris@26
|
288 worksMap.remove(c);
|
Chris@26
|
289
|
Chris@26
|
290 delete c->birth();
|
Chris@26
|
291 delete c->death();
|
Chris@26
|
292 delete c;
|
Chris@26
|
293
|
Chris@26
|
294 //!!! and composition events!
|
Chris@24
|
295 }
|
Chris@24
|
296 cout << endl << "Result after merging:" << endl << endl;;
|
Chris@24
|
297 show(target);
|
Chris@20
|
298 cout << endl;
|
Chris@20
|
299 }
|
Chris@20
|
300
|
Chris@20
|
301 int
|
Chris@20
|
302 main(int argc, char **argv)
|
Chris@20
|
303 {
|
Chris@20
|
304 if (argc < 3) usage(argv[0]);
|
Chris@20
|
305 QString inFileName = argv[1];
|
Chris@20
|
306 QString command = argv[2];
|
Chris@20
|
307 QStringList args;
|
Chris@20
|
308 for (int i = 3; i < argc; ++i) {
|
Chris@20
|
309 args.push_back(argv[i]);
|
Chris@20
|
310 }
|
Chris@20
|
311
|
Chris@20
|
312 BasicStore *store = new BasicStore();
|
Chris@20
|
313 store->setBaseUri(Uri("http://dbtune.org/classical/resource/"));
|
Chris@28
|
314 ObjectLoader *loader = new ObjectLoader(store);
|
Chris@31
|
315 // loader->setFollowPolicy(ObjectLoader::FollowObjectProperties);
|
Chris@28
|
316
|
Chris@28
|
317 TypeMapping tm;
|
Chris@20
|
318
|
Chris@22
|
319 TypeRegistrar::registerTypes();
|
Chris@28
|
320 TypeRegistrar::addMappings(store, &tm);
|
Chris@28
|
321
|
Chris@28
|
322 loader->setTypeMapping(tm);
|
Chris@20
|
323
|
Chris@20
|
324 if (!load(store, inFileName)) {
|
Chris@20
|
325 cerr << "Failed to load data source" << endl;
|
Chris@20
|
326 return 1;
|
Chris@20
|
327 }
|
Chris@20
|
328
|
Chris@20
|
329 cerr << "Imported RDF data, mapping to objects...";
|
Chris@31
|
330 QObjectList objects = loader->loadAll();
|
Chris@20
|
331 cerr << " done" << endl;
|
Chris@20
|
332
|
Chris@28
|
333 delete loader;
|
Chris@20
|
334
|
Chris@42
|
335 bool write = false, writeFull = false;
|
Chris@31
|
336 if (command == "merge") {
|
Chris@31
|
337 write = true;
|
Chris@42
|
338 } else if (command == "write") {
|
Chris@42
|
339 writeFull = true;
|
Chris@31
|
340 }
|
Chris@20
|
341
|
Chris@31
|
342 TransactionalStore *ts = 0;
|
Chris@31
|
343 ObjectMapper *mapper = 0;
|
Chris@31
|
344
|
Chris@31
|
345 if (write) {
|
Chris@31
|
346 cerr << "Managing objects...";
|
Chris@31
|
347 ts = new TransactionalStore(store);
|
Chris@31
|
348 mapper = new ObjectMapper(ts);
|
Chris@31
|
349 mapper->setTypeMapping(tm);
|
Chris@31
|
350 mapper->manage(objects);
|
Chris@31
|
351 cerr << " done" << endl;
|
Chris@31
|
352 }
|
Chris@31
|
353
|
Chris@31
|
354 foreach (QObject *o, objects) {
|
Chris@31
|
355 Composer *c = qobject_cast<Composer *>(o);
|
Chris@31
|
356 if (c) allComposers.push_back(c);
|
Chris@31
|
357 }
|
Chris@31
|
358
|
Chris@31
|
359 QList<Work *> works;
|
Chris@31
|
360 foreach (QObject *o, objects) {
|
Chris@31
|
361 Work *w = qobject_cast<Work *>(o);
|
Chris@31
|
362 if (w) works.push_back(w);
|
Chris@31
|
363 }
|
Chris@31
|
364
|
Chris@20
|
365 foreach (Work *w, works) {
|
Chris@20
|
366 Composition *c = w->composition();
|
Chris@20
|
367 if (c) {
|
Chris@20
|
368 Composer *cp = c->composer();
|
Chris@20
|
369 if (cp) worksMap[cp].insert(w);
|
Chris@20
|
370 }
|
Chris@20
|
371 }
|
Chris@20
|
372
|
Chris@42
|
373 if (command == "write") {
|
Chris@42
|
374 if (!args.empty()) usage(argv[0]);
|
Chris@42
|
375 } else if (command == "list") {
|
Chris@22
|
376 if (!args.empty()) usage(argv[0]);
|
Chris@20
|
377 listBrief(allComposers);
|
Chris@20
|
378 } else if (command == "list-uris") {
|
Chris@22
|
379 if (!args.empty()) usage(argv[0]);
|
Chris@20
|
380 listUris(allComposers);
|
Chris@20
|
381 } else {
|
Chris@20
|
382 if (args.empty()) usage(argv[0]);
|
Chris@20
|
383 if (command == "show") {
|
Chris@20
|
384 foreach (QString s, args) {
|
Chris@20
|
385 showWildcard(s);
|
Chris@20
|
386 }
|
Chris@20
|
387 } else if (command == "search") {
|
Chris@20
|
388 foreach (QString s, args) {
|
Chris@20
|
389 search(s);
|
Chris@20
|
390 }
|
Chris@20
|
391 } else if (command == "match") {
|
Chris@20
|
392 foreach (QString s, args) {
|
Chris@20
|
393 match(s);
|
Chris@20
|
394 }
|
Chris@24
|
395 } else if (command == "merge") {
|
Chris@24
|
396 if (args.size() < 2) usage(argv[0]);
|
Chris@24
|
397 Composer *target = matchSingle(args[0]);
|
Chris@24
|
398 if (!target) return 1;
|
Chris@24
|
399 QList<Composer *> sources;
|
Chris@24
|
400 for (int i = 1; i < args.size(); ++i) {
|
Chris@24
|
401 Composer *c = matchSingle(args[i]);
|
Chris@24
|
402 if (!c) return 1;
|
Chris@24
|
403 sources.push_back(c);
|
Chris@24
|
404 }
|
Chris@26
|
405 merge(target, sources, store);
|
Chris@20
|
406 }
|
Chris@20
|
407 }
|
Chris@20
|
408
|
Chris@24
|
409 if (write) {
|
Chris@31
|
410
|
Chris@31
|
411 cerr << "Committing changes...";
|
Chris@31
|
412 mapper->commit();
|
Chris@24
|
413 cerr << " done" << endl;
|
Chris@24
|
414
|
Chris@24
|
415 cerr << "Saving to file out.ttl...";
|
Chris@25
|
416 store->save("out.ttl");
|
Chris@24
|
417 cerr << " done" << endl;
|
Chris@42
|
418
|
Chris@42
|
419 } else if (writeFull) {
|
Chris@42
|
420
|
Chris@42
|
421 ObjectStorer *storer = new ObjectStorer(store);
|
Chris@42
|
422
|
Chris@42
|
423 storer->setTypeMapping(tm);
|
Chris@42
|
424
|
Chris@42
|
425 storer->setPropertyStorePolicy(ObjectStorer::StoreIfChanged);
|
Chris@53
|
426 storer->setBlankNodePolicy(Dataquay::NeverUseBlankNodes);
|
Chris@42
|
427
|
Chris@42
|
428 cerr << "Mapping results back to store...";
|
Chris@42
|
429 storer->setFollowPolicy(ObjectStorer::FollowObjectProperties);
|
Chris@42
|
430 storer->store(objects);
|
Chris@42
|
431 cerr << " done" << endl;
|
Chris@42
|
432
|
Chris@42
|
433 cerr << "Saving to file out.ttl...";
|
Chris@42
|
434 store->save("out.ttl");
|
Chris@42
|
435 cerr << " done" << endl;
|
Chris@42
|
436
|
Chris@42
|
437 delete storer;
|
Chris@24
|
438 }
|
Chris@24
|
439
|
Chris@31
|
440 delete mapper;
|
Chris@31
|
441 delete ts;
|
Chris@31
|
442
|
Chris@25
|
443 delete store;
|
Chris@20
|
444 }
|
Chris@20
|
445
|