2020-11-22 Fred Gleason <fredg@paravelsystems.com>

* Added '--send-mail' and '--mail-per-file' options to rdimport(1).

Signed-off-by: Fred Gleason <fredg@paravelsystems.com>
This commit is contained in:
Fred Gleason 2020-11-23 11:56:35 -05:00
parent deb363f6eb
commit e5a89ae833
7 changed files with 402 additions and 16 deletions

View File

@ -20618,3 +20618,5 @@
Settings' dialog in rdadmin(1).
* Added a 'Notification E-Mail Addresses' control to the 'Group'
dialog in rdadmin(1).
2020-11-22 Fred Gleason <fredg@paravelsystems.com>
* Added '--send-mail' and '--mail-per-file' options to rdimport(1).

View File

@ -256,6 +256,24 @@
</listitem>
</varlistentry>
<varlistentry>
<term>
<option>--mail-per-file</option>
</term>
<listitem>
<para>
Send an e-mail message for each file processed, rather than
a single message per run summarizing all actions taken. Implies
the <command>--send-mail</command> switch.
</para>
<para>
See the <command>--send-mail</command> switch (below) for more
details about generating e-mailed reports from
<command>rdimport</command><manvolnum>1</manvolnum>.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<option>--metadata-pattern=</option><replaceable>pattern</replaceable>
@ -547,6 +565,32 @@
</listitem>
</varlistentry>
<varlistentry>
<term>
<option>--send-mail</option>
</term>
<listitem>
<para>
Send e-mail to the address(es) specified in the destination group's
<computeroutput>Notification E-Mail Addresses</computeroutput>
setting in <command>rdadmin</command><manvolnum>1</manvolnum>
summarizing the action(s) performed during the run.
Each invocation of
<command>rdimport</command><manvolnum>1</manvolnum> will
potentially generate
one message for all successful imports and another for all failed
imports (but see the <option>--mail-per-file</option> switch
(above) for a way to modify this behavior).
</para>
<para>
NOTE: Rivendell uses the system's
<command>sendmail</command><manvolnum>1</manvolnum> subsystem
for originating e-mail. For many modern e-mail setups, further
configuration of that subsystem may be necessary.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<option>--set-datetimes=</option><replaceable>start-datetime</replaceable>,<replaceable>end-datetime</replaceable>
@ -989,6 +1033,10 @@
<refsect1 id='see_also'><title>See Also</title>
<para>
<citerefentry>
<refentrytitle>sendmail</refentrytitle><manvolnum>1</manvolnum>
</citerefentry>
<literal>,</literal>
<citerefentry>
<refentrytitle>rdexport</refentrytitle><manvolnum>1</manvolnum>
</citerefentry>

View File

@ -27,7 +27,8 @@ moc_%.cpp: %.h
bin_PROGRAMS = rdimport
dist_rdimport_SOURCES = markerset.cpp markerset.h\
dist_rdimport_SOURCES = journal.cpp journal.h\
markerset.cpp markerset.h\
rdimport.cpp rdimport.h
nodist_rdimport_SOURCES = moc_rdimport.cpp

235
utils/rdimport/journal.cpp Normal file
View File

@ -0,0 +1,235 @@
// journal.cpp
//
// E-mail file importation actions
//
// (C) Copyright 2020 Fred Gleason <fredg@paravelsystems.com>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
// published by the Free Software Foundation.
//
// 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, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
#include <rdapplication.h>
#include <rdgroup.h>
#include <rdsendmail.h>
#include "journal.h"
Journal::Journal(bool send_immediately)
{
c_send_immediately=send_immediately;
}
void Journal::addSuccess(const QString &groupname,QString filename,
unsigned cartnum,const QString &title)
{
QString errors;
QString subject;
QString body;
RDGroup *group=new RDGroup(groupname);
QStringList addrs=
group->notifyEmailAddress().split(",",QString::SkipEmptyParts);
if(addrs.size()>0) {
filename=filename.split("/",QString::SkipEmptyParts).last();
if(c_send_immediately) {
subject=QObject::tr("Rivendell import for file")+": "+filename;
body+=QObject::tr("Rivendell File Import Report")+"\n";
body+="\n";
body+=QObject::tr("Import Success")+"\n";
body+="\n";
body+=QObject::tr("Filename")+": "+filename+"\n";
body+=QObject::tr("Submitted by")+": ";
if(rda->user()->emailAddress().isEmpty()) {
body+=rda->user()->name()+"\n";
}
else {
body+=rda->user()->emailAddress()+"\n";
}
body+=QObject::tr("Group")+": "+groupname+"\n";
body+=QObject::tr("Cart Number")+QString().sprintf(": %06u",cartnum)+"\n";
body+=QObject::tr("Cart Title")+": "+title+"\n";
if(!RDSendMail(&errors,subject,body,rda->system()->originEmailAddress(),
addrs)) {
rda->syslog(LOG_WARNING,"email send failed [%s]",
errors.toUtf8().constData());
}
}
else {
c_good_groups.push_back(groupname);
c_good_filenames.push_back(filename);
c_good_cart_numbers.push_back(cartnum);
c_good_titles.push_back(title);
}
}
delete group;
}
void Journal::addFailure(const QString &groupname,QString filename,
const QString &err_msg)
{
QString errors;
QString subject;
QString body;
RDGroup *group=new RDGroup(groupname);
QStringList addrs=
group->notifyEmailAddress().split(",",QString::SkipEmptyParts);
if(addrs.size()>0) {
filename=filename.split("/",QString::SkipEmptyParts).last();
if(c_send_immediately) {
subject=QObject::tr("Rivendell import FAILURE for file")+": "+filename;
body+=QObject::tr("Rivendell File Import Report")+"\n";
body+="\n";
body+=QObject::tr("IMPORT FAILED!")+"\n";
body+="\n";
body+=QObject::tr("Filename")+": "+filename+"\n";
body+=QObject::tr("Submitted by")+": ";
if(rda->user()->emailAddress().isEmpty()) {
body+=rda->user()->name()+"\n";
}
else {
body+=rda->user()->emailAddress()+"\n";
}
body+=QObject::tr("Group")+": "+groupname+"\n";
body+=QObject::tr("Reason")+": "+err_msg+"\n";
if(!RDSendMail(&errors,subject,body,rda->system()->originEmailAddress(),
addrs)) {
rda->syslog(LOG_WARNING,"email send failed [%s]",
errors.toUtf8().constData());
}
}
else {
c_bad_groups.push_back(groupname);
c_bad_filenames.push_back(filename);
c_bad_errors.push_back(err_msg);
}
}
delete group;
}
void Journal::sendAll()
{
QStringList used_addrs;
//
// Successful imports
//
used_addrs.clear();
if(c_good_groups.size()>0) {
QMultiMap<QString,QString> grp_map=GroupsByAddress(c_good_groups);
for(QMap<QString,QString>::const_iterator it=grp_map.begin();
it!=grp_map.end();it++) {
if(!used_addrs.contains(it.key())) {
QString errors;
QString subject;
QString body;
QString from_addr;
QStringList to_addrs;
from_addr=rda->system()->originEmailAddress();
to_addrs=it.key().split(",",QString::SkipEmptyParts);
subject=QObject::tr("Rivendell import report")+"\n";
body+=QObject::tr("Rivendell File Import Report")+"\n";
body+="\n";
body+=QObject::tr("-Group---- -Cart- -Title------------------------ -Filename-------------")+"\n";
QStringList grps=grp_map.values(it.key());
for(int i=0;i<grps.size();i++) {
for(int j=0;j<c_good_groups.size();j++) {
if(c_good_groups.at(j)==grps.at(i)) {
body+=QString().sprintf("%-10s %06u %-30s %-22s\n",
grps.at(i).left(10).toUtf8().constData(),
c_good_cart_numbers.at(j),
c_good_titles.at(j).left(30).toUtf8().constData(),
c_good_filenames.at(j).left(22).toUtf8().constData());
}
}
}
if(!RDSendMail(&errors,subject,body,rda->system()->originEmailAddress(),
to_addrs)) {
rda->syslog(LOG_WARNING,"email send failed [%s]",
errors.toUtf8().constData());
}
used_addrs.push_back(it.key());
}
}
}
//
// Failed imports
//
used_addrs.clear();
if(c_bad_groups.size()>0) {
QMultiMap<QString,QString> grp_map=GroupsByAddress(c_bad_groups);
for(QMap<QString,QString>::const_iterator it=grp_map.begin();
it!=grp_map.end();it++) {
if(!used_addrs.contains(it.key())) {
QString errors;
QString subject;
QString body;
QString from_addr;
QStringList to_addrs;
from_addr=rda->system()->originEmailAddress();
to_addrs=it.key().split(",",QString::SkipEmptyParts);
subject=QObject::tr("Rivendell import FAILURE report")+"\n";
body+=QObject::tr("Rivendell File Import FAILURE Report")+"\n";
body+="\n";
body+=QObject::tr("-Group---- -Reason------------------------------ -Filename-------------")+"\n";
QStringList grps=grp_map.values(it.key());
for(int i=0;i<grps.size();i++) {
for(int j=0;j<c_bad_groups.size();j++) {
if(c_bad_groups.at(j)==grps.at(i)) {
body+=QString().sprintf("%-10s %-37s %-22s\n",
grps.at(i).left(10).toUtf8().constData(),
c_bad_errors.at(j).left(37).toUtf8().constData(),
c_bad_filenames.at(j).left(22).toUtf8().constData());
}
}
}
if(!RDSendMail(&errors,subject,body,rda->system()->originEmailAddress(),
to_addrs)) {
rda->syslog(LOG_WARNING,"email send failed [%s]",
errors.toUtf8().constData());
}
used_addrs.push_back(it.key());
}
}
}
}
QMultiMap<QString,QString> Journal::GroupsByAddress(QStringList groups) const
{
RDGroup *grp;
QMultiMap<QString,QString> ret;
for(int i=0;i<groups.size();i++) {
grp=new RDGroup(groups.at(i));
if(!grp->notifyEmailAddress().isEmpty()) {
if(!ret.contains(grp->notifyEmailAddress(),grp->name())) {
ret.insert(grp->notifyEmailAddress(),grp->name());
}
}
delete grp;
}
return ret;
}

52
utils/rdimport/journal.h Normal file
View File

@ -0,0 +1,52 @@
// journal.h
//
// E-mail file importation actions
//
// (C) Copyright 2020 Fred Gleason <fredg@paravelsystems.com>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
// published by the Free Software Foundation.
//
// 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, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
#ifndef JOURNAL_H
#define JOURNAL_H
#include <QList>
#include <QMultiMap>
#include <QString>
#include <QStringList>
class Journal
{
public:
Journal(bool send_immediately);
void addSuccess(const QString &groupname,QString filename,
unsigned cartnum,const QString &title);
void addFailure(const QString &groupname,QString filename,
const QString &err_msg);
void sendAll();
private:
QMultiMap<QString,QString> GroupsByAddress(QStringList groups) const;
bool c_send_immediately;
QStringList c_good_groups;
QStringList c_good_filenames;
QList<unsigned> c_good_cart_numbers;
QStringList c_good_titles;
QStringList c_bad_groups;
QStringList c_bad_filenames;
QStringList c_bad_errors;
};
#endif // JOURNAL_H

View File

@ -99,6 +99,8 @@ MainObject::MainObject(QObject *parent)
import_xml=false;
import_to_mono=false;
import_failed_imports=0;
import_send_mail=false;
import_mail_per_file=false;
//
// Open the Database
@ -445,6 +447,15 @@ MainObject::MainObject(QObject *parent)
import_xml=true;
rda->cmdSwitch()->setProcessed(i,true);
}
if(rda->cmdSwitch()->key(i)=="--send-mail") {
import_send_mail=true;
rda->cmdSwitch()->setProcessed(i,true);
}
if(rda->cmdSwitch()->key(i)=="--mail-per-file") {
import_mail_per_file=true;
import_send_mail=true;
rda->cmdSwitch()->setProcessed(i,true);
}
}
//
@ -707,6 +718,17 @@ MainObject::MainObject(QObject *parent)
else {
Log(LOG_INFO,QString(" Broken format workarounds are DISABLED\n"));
}
if(import_send_mail) {
if(import_mail_per_file) {
Log(LOG_INFO,QString(" E-mail report per file is ENABLED\n"));
}
else {
Log(LOG_INFO,QString(" Summary e-mail report is ENABLED\n"));
}
}
else {
Log(LOG_INFO,QString(" E-mail reporting is DISABLED\n"));
}
if(import_create_dates) {
Log(LOG_INFO,QString(" Import Create Dates mode is ON\n"));
Log(LOG_INFO,QString().sprintf(" Import Create Start Date Offset = %d days\n",import_create_startdate_offset));
@ -800,6 +822,11 @@ MainObject::MainObject(QObject *parent)
}
}
//
// Start the email journal
//
import_journal=new Journal(import_mail_per_file);
//
// Setup Signal Handling
//
@ -968,18 +995,19 @@ MainObject::Result MainObject::ImportFile(const QString &filename,
Log(LOG_WARNING,QString().sprintf(
" File \"%s\" is not readable or not a recognized format, skipping...\n",
(const char *)RDGetBasePart(filename).toUtf8()));
delete wavefile;
delete wavedata;
delete effective_group;
if(!import_run) {
NormalExit();
}
if(!import_temp_fix_filename.isEmpty()) {
// printf("Fixed Name: %s\n",(const char *)import_temp_fix_filename);
QFile::remove(import_temp_fix_filename);
import_temp_fix_filename="";
}
import_failed_imports++;
import_journal->addFailure(effective_group->name(),filename,
tr("unknown/unrecognized file format"));
delete wavefile;
delete wavedata;
delete effective_group;
return MainObject::FileBad;
}
Log(LOG_WARNING,QString().sprintf("success.\n"));
@ -989,9 +1017,6 @@ MainObject::Result MainObject::ImportFile(const QString &filename,
Log(LOG_WARNING,QString().sprintf(
" File \"%s\" is not readable or not a recognized format, skipping...\n",
(const char *)RDGetBasePart(filename).toUtf8()));
delete wavefile;
delete wavedata;
delete effective_group;
if(!import_run) {
NormalExit();
}
@ -1000,6 +1025,11 @@ MainObject::Result MainObject::ImportFile(const QString &filename,
import_temp_fix_filename="";
}
import_failed_imports++;
import_journal->addFailure(effective_group->name(),filename,
tr("unknown/unrecognized file format"));
delete wavefile;
delete wavedata;
delete effective_group;
return MainObject::FileBad;
}
}
@ -1042,10 +1072,12 @@ MainObject::Result MainObject::ImportFile(const QString &filename,
" File \"%s\" has an invalid or out of range Cart Number, skipping...\n",
(const char *)RDGetBasePart(filename).toUtf8()));
wavefile->closeWave();
import_failed_imports++;
import_journal->addFailure(effective_group->name(),filename,
tr("invalid/out-of-range cart number"));
delete wavefile;
delete wavedata;
delete effective_group;
import_failed_imports++;
return MainObject::FileBad;
}
}
@ -1056,9 +1088,6 @@ MainObject::Result MainObject::ImportFile(const QString &filename,
Log(LOG_ERR,QString().sprintf("rdimport: no free carts available in specified group\n"));
wavefile->closeWave();
import_failed_imports++;
delete wavefile;
delete wavedata;
delete effective_group;
import_failed_imports++;
if(import_drop_box) {
if(!import_run) {
@ -1068,6 +1097,11 @@ MainObject::Result MainObject::ImportFile(const QString &filename,
QFile::remove(import_temp_fix_filename);
import_temp_fix_filename="";
}
import_journal->addFailure(effective_group->name(),filename,
tr("no free cart available in group"));
delete wavefile;
delete wavedata;
delete effective_group;
return MainObject::NoCart;
}
exit(RDApplication::ExitImportFailed);
@ -1087,8 +1121,10 @@ MainObject::Result MainObject::ImportFile(const QString &filename,
cart->addCut(import_format,import_bitrate,import_channels);
if(cutnum<0) {
Log(LOG_WARNING,QString().sprintf("rdimport: no free cuts available in cart %06u\n",*cartnum));
delete cart;
import_failed_imports++;
import_journal->addFailure(effective_group->name(),filename,
tr("no free cut available in cart"));
delete cart;
return MainObject::NoCut;
}
RDCut *cut=new RDCut(*cartnum,cutnum);
@ -1149,9 +1185,6 @@ MainObject::Result MainObject::ImportFile(const QString &filename,
delete cut;
delete cart;
wavefile->closeWave();
delete wavefile;
delete wavedata;
delete effective_group;
if(!import_run) {
NormalExit();
}
@ -1160,6 +1193,11 @@ MainObject::Result MainObject::ImportFile(const QString &filename,
import_temp_fix_filename="";
}
import_failed_imports++;
import_journal->addFailure(effective_group->name(),filename,
tr("corrupt audio file"));
delete wavefile;
delete wavedata;
delete effective_group;
return MainObject::FileBad;
break;
}
@ -1351,6 +1389,9 @@ MainObject::Result MainObject::ImportFile(const QString &filename,
delete ll;
}
import_journal->
addSuccess(effective_group->name(),filename,*cartnum,cart->title());
delete settings;
delete conv;
delete cut;
@ -2176,6 +2217,7 @@ void MainObject::Log(int prio,const QString &msg) const
void MainObject::NormalExit() const
{
import_journal->sendAll();
if(import_failed_imports>0) {
exit(RDApplication::ExitImportFailed);
}

View File

@ -28,6 +28,8 @@
#include <qsqldatabase.h>
#include <qfileinfo.h>
#include <qdatetime.h>
#include <QList>
#include <QStringList>
#include <rdcart.h>
#include <rdcut.h>
@ -36,6 +38,7 @@
#include <rdwavedata.h>
#include <rdwavefile.h>
#include "journal.h"
#include "markerset.h"
#define RDIMPORT_TEMP_BASENAME "rdimp"
@ -113,6 +116,8 @@ class MainObject : public QObject
int import_autotrim_level;
int import_segue_level;
int import_segue_length;
bool import_send_mail;
bool import_mail_per_file;
unsigned import_cart_number;
QString import_metadata_pattern;
QString import_output_pattern;
@ -151,6 +156,7 @@ class MainObject : public QObject
MarkerSet *import_segue_markers;
MarkerSet *import_fadedown_marker;
MarkerSet *import_fadeup_marker;
Journal *import_journal;
};