Made CSV expoer RFC 4180 compliant.
This commit is contained in:
parent
dfc382b92c
commit
bb9cff2992
|
@ -29,7 +29,7 @@ remwharead - Remember what you read, and when
|
|||
Add tags to _URL_, delimited by commas.
|
||||
|
||||
*-e* _format_, *--export* _format_::
|
||||
Export to _format_. Possible values are _csv_ and _asciidoc_.
|
||||
Export to _format_. Possible values are _csv_ and _asciidoc_. See _FORMATS_.
|
||||
|
||||
*-f* _file_, *--file* _file_::
|
||||
Save output to _file_. Default is stdout.
|
||||
|
@ -56,6 +56,15 @@ Print version, copyright and license.
|
|||
|
||||
`remwharead -e asciidoc | asciidoctor --backend=html5 --out-file=out.html -`
|
||||
|
||||
== FORMATS
|
||||
|
||||
=== CSV
|
||||
|
||||
CSV is short for comma separated values. All fields are quoted and delimited by
|
||||
commas. Line breaks in the full text are converted to "\n". Our CSV
|
||||
implementation follows RFC 4180 and the full MIME media type is
|
||||
`text/csv;charset=utf-8;header=present`.
|
||||
|
||||
== FILES
|
||||
|
||||
* *Database*: `${XDG_DATA_HOME}/remwharead/database.sqlite`
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
/* This file is part of remwharead.
|
||||
* Copyright © 2019 tastytea <tastytea@tastytea.de>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <regex>
|
||||
#include "time.hpp"
|
||||
#include "csv.hpp"
|
||||
|
||||
using std::cerr;
|
||||
using std::endl;
|
||||
using std::regex;
|
||||
using std::regex_replace;
|
||||
|
||||
void export_csv(const vector<Database::entry> &entries, ostream &out)
|
||||
{
|
||||
try
|
||||
{
|
||||
out << "\"URI\",\"Archived URI\",\"Date & time\",\"Tags\","
|
||||
<< "\"Title\",\"Description\",\"Full text\"\r\n";
|
||||
for (const Database::entry &entry : entries)
|
||||
{
|
||||
string strtags;
|
||||
for (const string &tag : entry.tags)
|
||||
{
|
||||
strtags += tag;
|
||||
if (tag != *(entry.tags.rbegin()))
|
||||
{
|
||||
strtags += ",";
|
||||
}
|
||||
}
|
||||
out << '"' << quote_csv(entry.uri) << "\",\""
|
||||
<< quote_csv(entry.archive_uri) << "\",\""
|
||||
<< timepoint_to_string(entry.datetime) << "\",\""
|
||||
<< quote_csv(strtags) << "\",\""
|
||||
<< quote_csv(entry.title) << "\",\""
|
||||
<< quote_csv(entry.description) << "\",\""
|
||||
<< quote_csv(entry.fulltext_oneline()) << '"'<< "\r\n";
|
||||
}
|
||||
}
|
||||
catch (std::exception &e)
|
||||
{
|
||||
cerr << "Error in " << __func__ << ": " << e.what() << endl;
|
||||
}
|
||||
}
|
||||
|
||||
const string quote_csv(const string &field)
|
||||
{
|
||||
return regex_replace(field, regex("\""), "\"\"");
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
/* This file is part of remwharead.
|
||||
* Copyright © 2019 tastytea <tastytea@tastytea.de>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef REMWHAREAD_CSV_HPP
|
||||
#define REMWHAREAD_CSV_HPP
|
||||
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include "sqlite.hpp"
|
||||
|
||||
using std::vector;
|
||||
using std::ostream;
|
||||
using std::cout;
|
||||
|
||||
void export_csv(const vector<Database::entry> &entries, ostream &out = cout);
|
||||
const string quote_csv(const string &field);
|
||||
|
||||
#endif // REMWHAREAD_CSV_HPP
|
20
src/main.cpp
20
src/main.cpp
|
@ -20,6 +20,7 @@
|
|||
#include "sqlite.hpp"
|
||||
#include "parse_options.hpp"
|
||||
#include "url.hpp"
|
||||
#include "csv.hpp"
|
||||
|
||||
using std::cout;
|
||||
using std::cerr;
|
||||
|
@ -55,24 +56,7 @@ int main(const int argc, const char *argv[])
|
|||
{
|
||||
case export_format::csv:
|
||||
{
|
||||
cout << "#URI;Archived URI;Date & time;Tags;Title;Description\n";
|
||||
for (const Database::entry &entry
|
||||
: db.retrieve(opts.span[0], opts.span[1]))
|
||||
{
|
||||
string strtags;
|
||||
for (const string &tag : entry.tags)
|
||||
{
|
||||
strtags += tag;
|
||||
if (tag != *(entry.tags.rbegin()))
|
||||
{
|
||||
strtags += ",";
|
||||
}
|
||||
}
|
||||
cout << '"' << entry.uri << "\";\"" << entry.archive_uri << "\";\""
|
||||
<< timepoint_to_string(entry.datetime) << "\";\""
|
||||
<< strtags << "\";\"" << entry.title << "\";\""
|
||||
<< entry.description << '"'<< endl;
|
||||
}
|
||||
export_csv(db.retrieve(opts.span[0], opts.span[1]));
|
||||
break;
|
||||
}
|
||||
case export_format::asciidoc:
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <exception>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <regex>
|
||||
#include <basedir.h>
|
||||
#include <sqlite/execute.hpp>
|
||||
#include <sqlite/query.hpp>
|
||||
|
@ -25,6 +26,8 @@
|
|||
|
||||
using std::cerr;
|
||||
using std::endl;
|
||||
using std::regex;
|
||||
using std::regex_replace;
|
||||
|
||||
Database::Database()
|
||||
: _connected(false)
|
||||
|
@ -60,6 +63,11 @@ Database::operator bool() const
|
|||
return _connected;
|
||||
}
|
||||
|
||||
const string Database::entry::fulltext_oneline() const
|
||||
{
|
||||
return regex_replace(fulltext, regex("\n"), "\\n");
|
||||
}
|
||||
|
||||
void Database::store(const Database::entry &data) const
|
||||
{
|
||||
try
|
||||
|
|
|
@ -43,6 +43,9 @@ public:
|
|||
string title;
|
||||
string description;
|
||||
string fulltext;
|
||||
|
||||
//! The full text in one line.
|
||||
const string fulltext_oneline() const;
|
||||
} entry;
|
||||
|
||||
Database();
|
||||
|
|
Loading…
Reference in New Issue