// rdreport.cpp // // Abstract a Rivendell Report Descriptor // // (C) Copyright 2002-2021 Fred Gleason // // 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 #include "rdapplication.h" #include "rdconf.h" #include "rddatedecode.h" #include "rdescape_string.h" #include "rdlog_line.h" #include "rdreport.h" RDReport::RDReport(const QString &rptname,RDStation *station,RDConfig *config, QObject *parent) { report_name=rptname; report_station=station; report_config=config; report_error_code=RDReport::ErrorOk; } QString RDReport::name() const { return report_name; } bool RDReport::exists() const { QString sql=QString("select `NAME` from `REPORTS` where ")+ "`NAME`='"+RDEscapeString(report_name)+"'"; RDSqlQuery *q=new RDSqlQuery(sql); if(!q->first()) { delete q; return false; } delete q; return true; } QString RDReport::description() const { return RDGetSqlValue("REPORTS","NAME",report_name,"DESCRIPTION").toString(); } void RDReport::setDescription(const QString &desc) const { SetRow("DESCRIPTION",desc); } RDReport::ExportFilter RDReport::filter() const { return (RDReport::ExportFilter)RDGetSqlValue("REPORTS","NAME",report_name, "EXPORT_FILTER").toInt(); } void RDReport::setFilter(ExportFilter filter) const { SetRow("EXPORT_FILTER",(int)filter); } QString RDReport::exportPath(ExportOs ostype) const { return RDGetSqlValue("REPORTS","NAME",report_name, OsFieldName(ostype)+"EXPORT_PATH").toString(); } void RDReport::setExportPath(ExportOs ostype,const QString &path) const { SetRow(OsFieldName(ostype)+"EXPORT_PATH",path); } QString RDReport::postExportCommand(ExportOs ostype) const { return RDGetSqlValue("REPORTS","NAME",report_name, OsFieldName(ostype)+"POST_EXPORT_CMD").toString(); } void RDReport::setPostExportCommand(ExportOs ostype,const QString &cmd) const { SetRow(OsFieldName(ostype)+"POST_EXPORT_CMD",cmd); } bool RDReport::exportTypeEnabled(ExportType type) const { return RDBool(RDGetSqlValue("REPORTS","NAME",report_name, TypeFieldName(type,false)).toString()); } void RDReport::setExportTypeEnabled(ExportType type,bool state) const { SetRow(TypeFieldName(type,false),RDYesNo(state)); } bool RDReport::exportTypeForced(ExportType type) const { return RDBool(RDGetSqlValue("REPORTS","NAME",report_name, TypeFieldName(type,true)).toString()); } void RDReport::setExportTypeForced(ExportType type,bool state) const { SetRow(TypeFieldName(type,true),RDYesNo(state)); } QString RDReport::stationId() const { return RDGetSqlValue("REPORTS","NAME",report_name,"STATION_ID").toString(); } void RDReport::setStationId(const QString &id) const { SetRow("STATION_ID",id); } unsigned RDReport::cartDigits() const { return RDGetSqlValue("REPORTS","NAME",report_name,"CART_DIGITS").toUInt(); } void RDReport::setCartDigits(unsigned num) const { SetRow("CART_DIGITS",num); } bool RDReport::useLeadingZeros() const { return RDBool(RDGetSqlValue("REPORTS","NAME",report_name,"USE_LEADING_ZEROS"). toString()); } void RDReport::setUseLeadingZeros(bool state) const { SetRow("USE_LEADING_ZEROS",state); } int RDReport::linesPerPage() const { return RDGetSqlValue("REPORTS","NAME",report_name,"LINES_PER_PAGE").toInt(); } void RDReport::setLinesPerPage(int lines) const { SetRow("LINES_PER_PAGE",lines); } QString RDReport::serviceName() const { return RDGetSqlValue("REPORTS","NAME",report_name,"SERVICE_NAME").toString(); } void RDReport::setServiceName(const QString &name) const { SetRow("SERVICE_NAME",name); } RDReport::StationType RDReport::stationType() const { return (RDReport::StationType) RDGetSqlValue("REPORTS","NAME",report_name,"STATION_TYPE").toInt(); } void RDReport::setStationType(RDReport::StationType type) const { SetRow("STATION_TYPE",(int)type); } QString RDReport::stationFormat() const { return RDGetSqlValue("REPORTS","NAME",report_name,"STATION_FORMAT"). toString(); } void RDReport::setStationFormat(const QString &fmt) const { SetRow("STATION_FORMAT",fmt); } bool RDReport::filterOnairFlag() const { return RDBool(RDGetSqlValue("REPORTS","NAME",report_name,"FILTER_ONAIR_FLAG"). toString()); } void RDReport::setFilterOnairFlag(bool state) const { SetRow("FILTER_ONAIR_FLAG",RDYesNo(state)); } bool RDReport::filterGroups() const { return RDBool(RDGetSqlValue("REPORTS","NAME",report_name,"FILTER_GROUPS"). toString()); } void RDReport::setFilterGroups(bool state) const { SetRow("FILTER_GROUPS",RDYesNo(state)); } QTime RDReport::startTime(bool *is_null) const { if(is_null!=NULL) { if(RDIsSqlNull("REPORTS","NAME",report_name,"START_TIME")) { *is_null=true; return QTime(); } *is_null=false; } return RDGetSqlValue("REPORTS","NAME",report_name,"START_TIME").toTime(); } void RDReport::setStartTime(const QTime &time) const { SetRow("START_TIME",time); } void RDReport::setStartTime() const { SetRowNull("START_TIME"); } QTime RDReport::endTime(bool *is_null) const { if(is_null!=NULL) { if(RDIsSqlNull("REPORTS","NAME",report_name,"END_TIME")) { *is_null=true; return QTime(); } *is_null=false; } return RDGetSqlValue("REPORTS","NAME",report_name,"END_TIME").toTime(); } void RDReport::setEndTime(const QTime &time) const { SetRow("END_TIME",time); } void RDReport::setEndTime() const { SetRowNull("END_TIME"); } bool RDReport::aggregateTuningHoursRequired() const { return RDReport::aggregateTuningHoursRequired(filter()); } RDReport::ErrorCode RDReport::errorCode() const { return report_error_code; } bool RDReport::outputExists(const QDate &startdate) { QString out_path; out_path=RDDateDecode(exportPath(RDReport::Linux),startdate,report_station, report_config,serviceName()); return QFile::exists(out_path); } bool RDReport::generateReport(const QDate &startdate,const QDate &enddate, RDStation *station,QString *out_path,double ath) { QString sql; RDSqlQuery *q; RDSqlQuery *q1; RDSvc *svc; QString daypart_sql; QString station_sql; QString group_sql; QString force_sql; bool is_null=false; if(!exists()) { return false; } // // Generate the daypart filter // startTime(&is_null); if(!is_null) { for(int i=0;i<(startdate.daysTo(enddate)+1);i++) { QDate date=startdate.addDays(i); if(startTime()='")+ date.toString("yyyy-MM-dd")+ " "+startTime().toString("hh:mm:ss")+"')&&"+ "(`EVENT_DATETIME`<'"+date.toString("yyyy-MM-dd")+ " "+endTime().toString("hh:mm:ss")+"'))||"; } else { daypart_sql+=QString("((`EVENT_DATETIME`<='")+ date.toString("yyyy-MM-dd")+ " "+endTime().toString("hh:mm:ss")+"')&&"+ "(`EVENT_DATETIME`>'"+date.toString("yyyy-MM-dd")+" 00:00:00))||"+ "((`EVENT_DATETIME`>='"+ date.toString("yyyy-MM-dd")+ " "+startTime().toString("hh:mm:ss")+"')&&"+ "(`EVENT_DATETIME`<'"+date.toString("yyyy-MM-dd")+" 23:59:59))||"; } } daypart_sql=daypart_sql.left(daypart_sql.length()-2); } // // Generate the Station List // sql=QString("select `STATION_NAME` from `REPORT_STATIONS` where ")+ "`REPORT_NAME`='"+RDEscapeString(name())+"'"; q=new RDSqlQuery(sql); while(q->next()) { station_sql+= QString("(`STATION_NAME`='")+ RDEscapeString(q->value(0).toString())+"')||"; } delete q; station_sql=station_sql.left(station_sql.length()-2); // // Next, the group list // bool where=false; if(exportTypeEnabled(RDReport::Generic)) { sql="select `NAME` from `GROUPS` "; } else { where=true; sql="select `NAME` from `GROUPS` where "; if(exportTypeEnabled(RDReport::Traffic)) { sql+="(`REPORT_TFC`='Y')||"; } if(exportTypeEnabled(RDReport::Music)) { sql+="(`REPORT_MUS`='Y')||"; } } if(filterGroups()) { QString sql2=QString("select `GROUP_NAME` from `REPORT_GROUPS` where ")+ "`REPORT_NAME`='"+RDEscapeString(name())+"'"; q=new RDSqlQuery(sql2); while(q->next()) { if(!where) { sql+="where "; where=true; } sql+=QString("(`NAME`='")+RDEscapeString(q->value(0).toString())+"')||"; } delete q; } sql=sql.left(sql.length()-2); q=new RDSqlQuery(sql); while(q->next()) { group_sql+=QString("(`CART`.`GROUP_NAME`='")+ RDEscapeString(q->value(0).toString())+"')||"; } delete q; group_sql=group_sql.left(group_sql.length()-2); if(group_sql.length()==2) { group_sql=""; } // // Generate Mixdown ID // QString mixname=QString::asprintf("MX%d",getpid()); // // Iterate Selected Services // sql=QString("select `SERVICE_NAME` from `REPORT_SERVICES` where ")+ "`REPORT_NAME`='"+RDEscapeString(name())+"'"; q=new RDSqlQuery(sql); while(q->next()) { svc=new RDSvc(q->value(0).toString(),report_station,report_config); if(svc->exists()) { // // Generate Type Filters // force_sql=""; if(!exportTypeEnabled(RDReport::Generic)) { if(exportTypeForced(RDReport::Traffic)|| exportTypeEnabled(RDReport::Traffic)) { force_sql+=QString::asprintf("(`ELR_LINES`.`EVENT_SOURCE`=%d)||", RDLogLine::Traffic); } if(exportTypeForced(RDReport::Music)|| exportTypeEnabled(RDReport::Music)) { force_sql+=QString::asprintf("(`ELR_LINES`.`EVENT_SOURCE`=%d)||", RDLogLine::Music); } force_sql=force_sql.left(force_sql.length()-2); } // // Selected Fields // sql=QString("select ")+ "`LENGTH`,"+ // 00 "`LOG_ID`,"+ // 01 "`CART_NUMBER`,"+ // 02 "`STATION_NAME`,"+ // 03 "`EVENT_DATETIME`,"+ // 04 "`EVENT_TYPE`,"+ // 05 "`EXT_START_TIME`,"+ // 06 "`EXT_LENGTH`,"+ // 07 "`EXT_DATA`,"+ // 08 "`EXT_EVENT_ID`,"+ // 09 "`EXT_ANNC_TYPE`,"+ // 10 "`PLAY_SOURCE`,"+ // 11 "`CUT_NUMBER`,"+ // 12 "`EVENT_SOURCE`,"+ // 13 "`EXT_CART_NAME`,"+ // 14 "`LOG_NAME`,"+ // 15 "`ELR_LINES`.`TITLE`,"+ // 16 "`ELR_LINES`.`ARTIST`,"+ // 17 "`SCHEDULED_TIME`,"+ // 18 "`START_SOURCE`,"+ // 19 "`ELR_LINES`.`PUBLISHER`,"+ // 20 "`ELR_LINES`.`COMPOSER`,"+ // 21 "`ELR_LINES`.`ALBUM`,"+ // 22 "`ELR_LINES`.`LABEL`,"+ // 23 "`ELR_LINES`.`ISRC`,"+ // 24 "`ELR_LINES`.`USAGE_CODE`,"+ // 25 "`ELR_LINES`.`ONAIR_FLAG`,"+ // 26 "`ELR_LINES`.`ISCI`,"+ // 27 "`ELR_LINES`.`CONDUCTOR`,"+ // 28 "`ELR_LINES`.`USER_DEFINED`,"+ // 29 "`ELR_LINES`.`SONG_ID`,"+ // 30 "`ELR_LINES`.`DESCRIPTION`,"+ // 31 "`ELR_LINES`.`OUTCUE` "+ // 32 "from `ELR_LINES` left join `CART` "+ "on `ELR_LINES`.`CART_NUMBER`=`CART`.`NUMBER` where "+ "`SERVICE_NAME`='"+RDEscapeString(q->value(0).toString())+"' && "; // // OnAir Flag Filter // if(filterOnairFlag()) { sql+="(`ONAIR_FLAG`='Y')&&"; } // // Group Filter // sql+="("; if(!group_sql.isEmpty()) { sql+=QString("(")+group_sql+")&&"; } if(!force_sql.isEmpty()) { sql+=QString("(")+force_sql+")&&"; } // // Daypart Filter // if(daypart_sql.isEmpty()) { //TODO Do we need to escape on Select statement? sql+=QString("(`EVENT_DATETIME`>='")+startdate.toString("yyyy-MM-dd")+ " 00:00:00')&&"+ "(`EVENT_DATETIME`<='"+enddate.toString("yyyy-MM-dd")+ " 23:59:59')&&"; } else { sql+=(QString("(")+daypart_sql+")&&"); } if(!station_sql.isEmpty()) { sql+=QString("(")+station_sql+")||"; } sql=sql.left(sql.length()-2); sql+=")"; q1=new RDSqlQuery(sql); while(q1->next()) { sql=QString("insert into `ELR_LINES` set ")+ "`SERVICE_NAME`='"+RDEscapeString(mixname)+"',"+ QString::asprintf("`LENGTH`=%d,`LOG_ID`=%u,`CART_NUMBER`=%u,", q1->value(0).toInt(), q1->value(1).toUInt(), q1->value(2).toInt())+ "`STATION_NAME`='"+RDEscapeString(q1->value(3).toString())+"',"+ "`EVENT_DATETIME`="+RDCheckDateTime(q1->value(4).toDateTime(), "yyyy-MM-dd hh:mm:ss")+","+ QString::asprintf("`EVENT_TYPE`=%d,",q1->value(5).toInt())+ "`EXT_START_TIME`="+ RDCheckDateTime(q1->value(6).toTime(),"hh:mm:ss")+","+ QString::asprintf("`EXT_LENGTH`=%d,",q1->value(7).toInt())+ "`EXT_DATA`='"+RDEscapeString(q1->value(8).toString())+"',"+ "`EXT_EVENT_ID`='"+RDEscapeString(q1->value(9).toString())+"',"+ "`EXT_ANNC_TYPE`='"+RDEscapeString(q1->value(10).toString())+"',"+ QString::asprintf("`PLAY_SOURCE`=%d,",q1->value(11).toInt())+ QString::asprintf("`CUT_NUMBER`=%d,",q1->value(12).toInt())+ QString::asprintf("`EVENT_SOURCE`=%d,",q1->value(13).toInt())+ "`EXT_CART_NAME`='"+RDEscapeString(q1->value(14).toString())+"',"+ "`LOG_NAME`='"+RDEscapeString(q1->value(15).toString())+"',"+ "`TITLE`='"+RDEscapeString(q1->value(16).toString())+"',"+ "`ARTIST`='"+RDEscapeString(q1->value(17).toString())+"',"+ "`SCHEDULED_TIME`="+ RDCheckDateTime(q1->value(18).toDate(),"yyyy-MM-dd hh:mm:ss")+","+ QString::asprintf("`START_SOURCE`=%d,",q1->value(19).toInt())+ "`PUBLISHER`='"+RDEscapeString(q1->value(20).toString())+"',"+ "`COMPOSER`='"+RDEscapeString(q1->value(21).toString())+"',"+ "`ALBUM`='"+RDEscapeString(q1->value(22).toString())+"',"+ "`LABEL`='"+RDEscapeString(q1->value(23).toString())+"',"+ "`ISRC`='"+RDEscapeString(q1->value(24).toString())+"',"+ QString::asprintf("`USAGE_CODE`=%d,",q1->value(25).toInt())+ "`ONAIR_FLAG`='"+RDEscapeString(q1->value(26).toString())+"',"+ "`ISCI`='"+RDEscapeString(q1->value(27).toString())+"',"+ "`CONDUCTOR`='"+RDEscapeString(q1->value(28).toString())+"',"+ "`USER_DEFINED`='"+RDEscapeString(q1->value(29).toString())+"',"+ "`SONG_ID`='"+RDEscapeString(q1->value(30).toString())+"',"+ "`DESCRIPTION`='"+RDEscapeString(q1->value(31).toString())+"',"+ "`OUTCUE`='"+RDEscapeString(q1->value(32).toString())+"'"; RDSqlQuery::apply(sql); } delete q1; } delete svc; } delete q; bool ret=false; QString filename=RDDateDecode(exportPath(RDReport::Linux),startdate, report_station,report_config,serviceName()); switch(filter()) { case RDReport::CbsiDeltaFlex: ret=ExportDeltaflex(filename,startdate,enddate,mixname); break; case RDReport::TextLog: ret=ExportTextLog(filename,startdate,enddate,mixname); break; case RDReport::BmiEmr: ret=ExportBmiEmr(filename,startdate,enddate,mixname); break; case RDReport::NaturalLog: case RDReport::Technical: ret=ExportTechnical(filename,startdate,enddate,true,false,mixname); break; case RDReport::SoundExchange: ret=ExportSoundEx(filename,startdate,enddate,ath,mixname); break; case RDReport::NprSoundExchange: ret=ExportNprSoundEx(filename,startdate,enddate,mixname); break; case RDReport::RadioTraffic: ret=ExportRadioTraffic(filename,startdate,enddate,mixname,0); break; case RDReport::RadioTraffic2: ret=ExportRadioTraffic(filename,startdate,enddate,mixname,1); break; case RDReport::VisualTraffic: ret=ExportDeltaflex(filename,startdate,enddate,mixname); break; case RDReport::CounterPoint: case RDReport::WideOrbit: ret=ExportRadioTraffic(filename,startdate,enddate,mixname,0); break; case RDReport::CounterPoint2: ret=ExportRadioTraffic(filename,startdate,enddate,mixname,1); break; case RDReport::Music1: ret=ExportRadioTraffic(filename,startdate,enddate,mixname,0); break; case RDReport::MusicClassical: ret=ExportMusicClassical(filename,startdate,enddate,mixname); break; case RDReport::MusicPlayout: ret=ExportMusicPlayout(filename,startdate,enddate,mixname); break; case RDReport::SpinCount: ret=ExportSpinCount(filename,startdate,enddate,mixname); break; case RDReport::MusicSummary: ret=ExportMusicSummary(filename,startdate,enddate,mixname); break; case RDReport::MrMaster: ret=ExportTechnical(filename,startdate,enddate,false,true,mixname); break; case RDReport::CutLog: ret=ExportCutLog(filename,startdate,enddate,mixname); break; case RDReport::ResultsReport: ret=ExportResultsReport(filename,startdate,enddate,mixname); break; default: return false; break; } *out_path=RDDateDecode(exportPath(RDReport::Linux),startdate,report_station, report_config,serviceName()); QString post_cmd=RDDateDecode(postExportCommand(RDReport::Linux),startdate, report_station,report_config,serviceName()); RDCheckExitCode("RDReport::generateReport system",system(post_cmd.toUtf8())); sql=QString("delete from `ELR_LINES` where ")+ "`SERVICE_NAME`='"+RDEscapeString(mixname)+"'"; RDSqlQuery::apply(sql); // printf("RDReport mixname: %s\n",mixname.toUtf8().constData()); return ret; } QString RDReport::filterText(RDReport::ExportFilter filter) { switch(filter) { case RDReport::CbsiDeltaFlex: return QObject::tr("CBSI DeltaFlex Traffic Reconciliation v2.01"); case RDReport::TextLog: return QObject::tr("Text Log"); case RDReport::BmiEmr: return QObject::tr("ASCAP/BMI Electronic Music Report"); case RDReport::Technical: return QObject::tr("Technical Playout Report"); case RDReport::SoundExchange: return QObject::tr("SoundExchange Statutory License Report"); case RDReport::NprSoundExchange: return QObject::tr("NPR/DS SoundExchange Report"); case RDReport::RadioTraffic: return QObject::tr("Original RadioTraffic.com Traffic Reconciliation (DEPRECATED)"); case RDReport::RadioTraffic2: return QObject::tr("RadioTraffic.com Traffic Reconciliation"); case RDReport::VisualTraffic: return QObject::tr("VisualTraffic Reconciliation"); case RDReport::CounterPoint: return QObject::tr("CounterPoint Traffic Reconciliation"); case RDReport::CounterPoint2: return QObject::tr("CounterPoint Traffic Reconciliation v2"); case RDReport::MrMaster: return QObject::tr("Mr. Master Reconciliation"); case RDReport::Music1: return QObject::tr("Music1 Reconciliation"); case RDReport::MusicClassical: return QObject::tr("Classical Music Playout"); case RDReport::MusicPlayout: return QObject::tr("Music Playout"); case RDReport::MusicSummary: return QObject::tr("Music Summary"); case RDReport::NaturalLog: return QObject::tr("NaturalLog Reconciliation"); case RDReport::SpinCount: return QObject::tr("Spin Count"); case RDReport::WideOrbit: return QObject::tr("WideOrbit Traffic Reconciliation"); case RDReport::CutLog: return QObject::tr("Cut Log"); case RDReport::ResultsReport: return QObject::tr("Results Report"); case RDReport::LastFilter: break; } return QObject::tr("Unknown"); } QString RDReport::stationTypeText(RDReport::StationType type) { switch(type) { case RDReport::TypeOther: return QObject::tr("Other"); case RDReport::TypeAm: return QObject::tr("AM"); case RDReport::TypeFm: return QObject::tr("FM"); default: break; } return QObject::tr("Unknown"); } bool RDReport::multipleDaysAllowed(RDReport::ExportFilter filter) { switch(filter) { case RDReport::CbsiDeltaFlex: case RDReport::TextLog: case RDReport::RadioTraffic: case RDReport::RadioTraffic2: case RDReport::VisualTraffic: case RDReport::CounterPoint: case RDReport::CounterPoint2: case RDReport::LastFilter: case RDReport::MrMaster: case RDReport::Music1: case RDReport::MusicClassical: case RDReport::MusicPlayout: case RDReport::NaturalLog: case RDReport::SpinCount: case RDReport::WideOrbit: case RDReport::CutLog: case RDReport::ResultsReport: return false; case RDReport::BmiEmr: case RDReport::MusicSummary: case RDReport::NprSoundExchange: case RDReport::SoundExchange: case RDReport::Technical: return true; } return true; } bool RDReport::multipleMonthsAllowed(RDReport::ExportFilter filter) { switch(filter) { case RDReport::CbsiDeltaFlex: case RDReport::TextLog: case RDReport::BmiEmr: case RDReport::RadioTraffic: case RDReport::RadioTraffic2: case RDReport::VisualTraffic: case RDReport::CounterPoint: case RDReport::CounterPoint2: case RDReport::LastFilter: case RDReport::MrMaster: case RDReport::Music1: case RDReport::MusicClassical: case RDReport::MusicPlayout: case RDReport::NaturalLog: case RDReport::WideOrbit: case RDReport::CutLog: case RDReport::ResultsReport: return false; case RDReport::MusicSummary: case RDReport::NprSoundExchange: case RDReport::SoundExchange: case RDReport::SpinCount: case RDReport::Technical: return true; } return true; } bool RDReport::aggregateTuningHoursRequired(RDReport::ExportFilter filter) { bool ret=false; switch(filter) { case RDReport::CbsiDeltaFlex: case RDReport::TextLog: case RDReport::BmiEmr: case RDReport::RadioTraffic: case RDReport::RadioTraffic2: case RDReport::VisualTraffic: case RDReport::CounterPoint: case RDReport::CounterPoint2: case RDReport::MrMaster: case RDReport::Music1: case RDReport::MusicClassical: case RDReport::MusicPlayout: case RDReport::NaturalLog: case RDReport::WideOrbit: case RDReport::CutLog: case RDReport::ResultsReport: case RDReport::MusicSummary: case RDReport::NprSoundExchange: case RDReport::SpinCount: case RDReport::Technical: case RDReport::LastFilter: ret=false; break; case RDReport::SoundExchange: ret=true; break; } return ret; } QString RDReport::errorText(RDReport::ErrorCode code) { QString ret; switch(code) { case RDReport::ErrorOk: ret=QObject::tr("Report complete!"); break; case RDReport::ErrorCanceled: ret=QObject::tr("Report canceled!"); break; case RDReport::ErrorCantOpen: ret=QObject::tr("Unable to open report file!"); break; } return ret; } QString RDReport::leftJustify(const QString &str,int width) { QString ret=str.left(width); while(ret.length()