You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tellico/src/translators/alexandriaimporter.cpp

256 lines
8.5 KiB

/***************************************************************************
copyright : (C) 2003-2006 by Robby Stephenson
email : robby@periapsis.org
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of version 2 of the GNU General Public License as *
* published by the Free Software Foundation; *
* *
***************************************************************************/
#include "alexandriaimporter.h"
#include "../collections/bookcollection.h"
#include "../entry.h"
#include "../field.h"
#include "../latin1literal.h"
#include "../isbnvalidator.h"
#include "../imagefactory.h"
#include "../progressmanager.h"
#include "../tellico_debug.h"
#include <kcombobox.h>
#include <kapplication.h>
#include <kstringhandler.h>
#include <tqlayout.h>
#include <tqlabel.h>
#include <tqgroupbox.h>
using Tellico::Import::AlexandriaImporter;
bool AlexandriaImporter::canImport(int type) const {
return type == Data::Collection::Book;
}
Tellico::Data::CollPtr AlexandriaImporter::collection() {
if(!m_widget || m_library->count() == 0) {
return 0;
}
m_coll = new Data::BookCollection(true);
TQDir dataDir = m_libraryDir;
dataDir.cd(m_library->currentText());
dataDir.setFilter(TQDir::Files | TQDir::Readable | TQDir::NoSymLinks);
const TQString title = TQString::tqfromLatin1("title");
const TQString author = TQString::tqfromLatin1("author");
const TQString year = TQString::tqfromLatin1("pub_year");
const TQString binding = TQString::tqfromLatin1("binding");
const TQString isbn = TQString::tqfromLatin1("isbn");
const TQString pub = TQString::tqfromLatin1("publisher");
const TQString rating = TQString::tqfromLatin1("rating");
const TQString cover = TQString::tqfromLatin1("cover");
const TQString comments = TQString::tqfromLatin1("comments");
// start with yaml files
dataDir.setNameFilter(TQString::tqfromLatin1("*.yaml"));
const TQStringList files = dataDir.entryList();
const uint numFiles = files.count();
const uint stepSize = TQMAX(s_stepSize, numFiles/100);
const bool showProgress = options() & ImportProgress;
ProgressItem& item = ProgressManager::self()->newProgressItem(this, progressLabel(), true);
item.setTotalSteps(numFiles);
connect(&item, TQT_SIGNAL(signalCancelled(ProgressItem*)), TQT_SLOT(slotCancel()));
ProgressItem::Done done(this);
TQStringList covers;
covers << TQString::tqfromLatin1(".cover")
<< TQString::tqfromLatin1("_medium.jpg")
<< TQString::tqfromLatin1("_small.jpg");
TQTextStream ts;
ts.setEncoding(TQTextStream::UnicodeUTF8); // YAML is always utf8?
uint j = 0;
for(TQStringList::ConstIterator it = files.begin(); !m_cancelled && it != files.end(); ++it, ++j) {
TQFile file(dataDir.absFilePath(*it));
if(!file.open(IO_ReadOnly)) {
continue;
}
Data::EntryPtr entry = new Data::Entry(m_coll);
bool readNextLine = true;
ts.unsetDevice();
ts.setDevice(TQT_TQIODEVICE(&file));
TQString line;
while(!ts.atEnd()) {
if(readNextLine) {
line = ts.readLine();
} else {
readNextLine = true;
}
// skip the line that starts with ---
if(line.isEmpty() || line.startsWith(TQString::tqfromLatin1("---"))) {
continue;
}
if(line.endsWith(TQChar('\\'))) {
line.truncate(line.length()-1); // remove last character
line += ts.readLine();
}
cleanLine(line);
TQString alexField = line.section(':', 0, 0);
TQString alexValue = line.section(':', 1).stripWhiteSpace();
clean(alexValue);
// Alexandria uses "n/a for empty values, and it is translated
// only thing we can do is check for english value and continue
if(alexValue == Latin1Literal("n/a")) {
continue;
}
if(alexField == Latin1Literal("authors")) {
TQStringList authors;
line = ts.readLine();
TQRegExp begin(TQString::tqfromLatin1("^\\s*-\\s+"));
while(!line.isNull() && line.tqfind(begin) > -1) {
line.remove(begin);
authors += clean(line);
line = ts.readLine();
}
entry->setField(author, authors.join(TQString::tqfromLatin1("; ")));
// the next line has already been read
readNextLine = false;
// Alexandria calls the edition the binding
} else if(alexField == Latin1Literal("edition")) {
// special case if it's "Hardcover"
if(alexValue.lower() == Latin1Literal("hardcover")) {
alexValue = i18n("Hardback");
}
entry->setField(binding, alexValue);
} else if(alexField == Latin1Literal("publishing_year")) {
entry->setField(year, alexValue);
} else if(alexField == Latin1Literal("isbn")) {
const ISBNValidator val(0);
val.fixup(alexValue);
entry->setField(isbn, alexValue);
// now find cover image
KURL u;
alexValue.remove('-');
for(TQStringList::Iterator ext = covers.begin(); ext != covers.end(); ++ext) {
u.setPath(dataDir.absFilePath(alexValue + *ext));
if(!TQFile::exists(u.path())) {
continue;
}
TQString id = ImageFactory::addImage(u, true);
if(!id.isEmpty()) {
entry->setField(cover, id);
break;
}
}
} else if(alexField == Latin1Literal("notes")) {
entry->setField(comments, alexValue);
// now try by name then title
} else if(m_coll->fieldByName(alexField)) {
entry->setField(alexField, alexValue);
} else if(m_coll->fieldByTitle(alexField)) {
entry->setField(m_coll->fieldByTitle(alexField), alexValue);
}
}
m_coll->addEntries(entry);
if(showProgress && j%stepSize == 0) {
ProgressManager::self()->setProgress(this, j);
kapp->processEvents();
}
}
return m_coll;
}
TQWidget* AlexandriaImporter::widget(TQWidget* parent_, const char* name_/*=0*/) {
if(m_widget) {
return m_widget;
}
m_libraryDir = TQDir::home();
m_libraryDir.setFilter(TQDir::Dirs | TQDir::Readable | TQDir::NoSymLinks);
m_widget = new TQWidget(parent_, name_);
TQVBoxLayout* l = new TQVBoxLayout(m_widget);
TQGroupBox* box = new TQGroupBox(2, Qt::Horizontal, i18n("Alexandria Options"), m_widget);
TQLabel* label = new TQLabel(i18n("&Library:"), box);
m_library = new KComboBox(box);
label->setBuddy(m_library);
// .alexandria might not exist
if(m_libraryDir.cd(TQString::tqfromLatin1(".alexandria"))) {
TQStringList dirs = m_libraryDir.entryList();
dirs.remove(TQString::tqfromLatin1(".")); // why can't I tell TQDir not to include these? TQDir::Hidden doesn't work
dirs.remove(TQString::tqfromLatin1(".."));
m_library->insertStringList(dirs);
}
l->addWidget(box);
l->addStretch(1);
return m_widget;
}
TQString& AlexandriaImporter::cleanLine(TQString& str_) {
static TQRegExp escRx(TQString::tqfromLatin1("\\\\x(\\w\\w)"), false);
str_.remove(TQString::tqfromLatin1("\\r"));
str_.tqreplace(TQString::tqfromLatin1("\\n"), TQString::tqfromLatin1("\n"));
str_.tqreplace(TQString::tqfromLatin1("\\t"), TQString::tqfromLatin1("\t"));
// YAML uses escape sequences like \xC3
int pos = escRx.search(str_);
int origPos = pos;
TQCString bytes;
while(pos > -1) {
bool ok;
char c = escRx.cap(1).toInt(&ok, 16);
if(ok) {
bytes += c;
} else {
bytes = TQCString();
break;
}
pos = escRx.search(str_, pos+1);
}
if(!bytes.isEmpty()) {
str_.tqreplace(origPos, bytes.length()*4, TQString::fromUtf8(bytes.data()));
}
return str_;
}
TQString AlexandriaImporter::clean(TQString& str_) {
const TQRegExp quote(TQString::tqfromLatin1("\\\\\"")); // equals \"
if(str_.startsWith(TQChar('\'')) || str_.startsWith(TQChar('"'))) {
str_.remove(0, 1);
}
if(str_.endsWith(TQChar('\'')) || str_.endsWith(TQChar('"'))) {
str_.truncate(str_.length()-1);
}
// we ignore YAML tags, this is not actually a good parser, but will do for now
str_.remove(TQRegExp(TQString::tqfromLatin1("^![^\\s]*\\s+")));
return str_.tqreplace(quote, TQChar('"'));
}
void AlexandriaImporter::slotCancel() {
m_cancelled = true;
}
#include "alexandriaimporter.moc"