Logo Search packages:      
Sourcecode: datakiosk version File versions  Download package

datareport.cpp

/***************************************************************************
 *   Copyright (C) 2005 by Adam Treat                                      *
 *   treat@kde.org                                                         *
 *                                                                         *
 *   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; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#include "datareport.h"

#include "project.h"
#include "datatable.h"
#include "datakiosk.h"
#include "datatablesearch.h"
#include "actioncollection.h"
#include "databaseconnection.h"

#include <qmap.h>
#include <qfile.h>
#include <qtextstream.h>
#include <qwidgetstack.h>

#include <kurl.h>
#include <kdebug.h>
#include <kstandarddirs.h>
#include <ktempfile.h>
#include <klibloader.h>
#include <kmessagebox.h>

#include <kzip.h>
#include <karchive.h>

#define widget (kapp->mainWidget())

using namespace ActionCollection;

DataReport::DataReport( Project *project ) :
        QHBox( project->dataTableStack() ),
        m_parent( 0 ),
        m_project( project ),
        m_part( 0 ),
        m_browser( 0 ),
        m_numRows( 0 ),
        m_needsRefresh( false ),
        m_isInitialized( false )
{
    setup();
    setIconName( "kugar" );
    m_project->setupDataReport( this, iconName() );
}

DataReport::DataReport( Project *project, DataTable* parent ) :
        QHBox( project->dataTableStack() ),
        m_parent( parent ),
        m_project( project ),
        m_part( 0 ),
        m_browser( 0 ),
        m_numRows( 0 ),
        m_needsRefresh( false ),
        m_isInitialized( false )
{
    setup();
    setIconName( "kugar" );
    m_project->setupDataReport( this, iconName(), parent );
}

DataReport::~DataReport()
{
    delete m_part;
}

void DataReport::setup()
{
    KLibFactory * factory = KLibLoader::self() ->factory( "libkugarpart" );
    if ( factory )
    {
        m_part = ( KParts::ReadOnlyPart * ) factory->create( this,
                 "kugarviewer_part", "KParts::ReadOnlyPart" );

        m_browser = KParts::BrowserExtension::childObject( m_part );
        setAsActiveReport( true );
    }
    else
    {
        KMessageBox::error( this, i18n( "No libkugarpart found.  Do you have Kugar (www.koffice.org/kugar) installed?" ), i18n( "Could Not Create Report" ) );
        return ;
    }

    static_cast<DataKiosk*>( widget ) ->partManager() ->addPart( m_part, true ); // sets as the active part
}

void DataReport::setAsActiveReport( bool active )
{
    if ( active )
        connect( action( "print" ), SIGNAL( activated() ), m_browser, "1print()" );
    else
        disconnect( action( "print" ), SIGNAL( activated() ), m_browser, "1print()" );
}

void DataReport::initialize()
{
    refreshReport();
}

QMap<QString, int> DataReport::readTemplateFields() const
{
    QMap<QString, int> templateFields;

    if ( m_templateURL.isEmpty() )
        return templateFields;

    QDomDocument doc;
    bool openedDom = false;

    KZip zip( m_templateURL );
    if ( zip.open( IO_ReadOnly ) )
    {
        kdDebug() << m_templateURL << " is a zip file" <<  endl;
        const KArchiveFile *maindoc = dynamic_cast<const KArchiveFile*>( zip.directory()->entry( "maindoc.xml" ) );
        if ( maindoc )
            openedDom = doc.setContent( maindoc->data() );
        zip.close();
    }
    else
    {
        QFile f( m_templateURL );
        if ( f.open( IO_ReadOnly ) )
        {
            kdDebug() << m_templateURL << " is a regular file" <<  endl;
            openedDom = doc.setContent( &f );
            f.close();
        }
    }

    if ( !openedDom )
        return templateFields;

    kdDebug() << "successfully opened the template dom" <<  endl;

    QDomNodeList details = doc.elementsByTagName( "Detail" );
    for ( uint i = 0; i < details.length(); i++ )
    {
        QDomElement detail = details.item( i ).toElement();
/*        kdDebug() << detail.attribute( "Level" ) << endl;*/
        QDomNodeList fields = detail.elementsByTagName( "Field" );
        for ( uint i = 0; i < fields.length(); i++ )
        {
            QDomElement field = fields.item( i ).toElement();
/*            kdDebug() << field.attribute( "Field" ) << endl;*/
            templateFields[ field.attribute( "Field" ) ] =
                detail.attribute( "Level" ).toInt();
        }
    }

    return templateFields;
}

void DataReport::refreshReport()
{
    DatabaseConnection * database = m_project->databaseConnection( m_parent->connection() );
    if ( !database )
        return;

    if ( m_tables.count() < 1 )
        return ;

    QTime t;
    t.start();

    if ( !dataSearch().isEmpty() )
        m_project->invokeSearch( dataSearch() );

    QApplication::setOverrideCursor( QCursor( Qt::WaitCursor ) );

    QStringList select;
    QStringList from;
    QStringList orderby;
    QMap<QString,QString> relationList;

    DataTableList list;
    QStringList::Iterator names = m_tables.begin();
    for ( ; names != m_tables.end(); ++names )
    {
        DataTable * d = m_project->dataTableByName( *names );
        if ( d )
            list.append( d );
    }

    QMap<QString, int> templateFields = readTemplateFields();
    DataTableList::Iterator it = list.begin();
    for ( ; it != list.end(); ++it )
    {
        if ( !from.empty() )
            from.append( " LEFT JOIN " );

        if ( !( *it )->searchFilter().isEmpty() )
        {
            from.append( "(SELECT * FROM " );
            from.append( ( *it ) ->tableName() );
            from.append( " WHERE " );
            QString searchFilter = ( *it ) ->searchFilter().startsWith(" AND ") ?
                                   ( *it ) ->searchFilter().remove( 0, 5 ) :
                                   ( *it ) ->searchFilter();
            from.append( searchFilter );
            from.append( ")" );
        }
        else
        {
            from.append( ( *it ) ->tableName() );
        }
        from.append( " as " );
        from.append( ( *it ) ->alias() );

        if ( !( *it ) ->isRootTable() )
        {
            from.append( " ON " );
            from.append( m_project->dataTableByName( ( *it ) ->parentName() ) ->alias() );
            from.append( "." );
            from.append( ( *it ) ->parentKey() );
            from.append( "=" );
            from.append( ( *it ) ->alias() );
            from.append( "." );
            from.append( ( *it ) ->foreignKey() );
        }

        DataFieldList fields = ( *it ) ->fieldList();
        DataFieldList::Iterator it2 = fields.begin();
        for ( ; it2 != fields.end(); ++it2 )
        {
            QString canonicalName = ( *it ) ->alias() + "." + ( *it2 ) ->name();

            if ( ( *it2 ) ->hidden() || ( *it2 ) ->isVirtual() || !templateFields.contains( canonicalName ) )
                continue;

            if ( ( *it2 )->relation() )
            {
                DataField *reporter = ( *it2 )->relation()->getReportField();
                if ( reporter )
                {
                    QString alias = ( *it ) ->alias() + "_" + ( *it2 ) ->name() + "2" + ( *it2 )->relation()->table();

                    if ( !reporter->relation() )
                    {
                        QString fullFieldName = alias + "." + reporter->name();
                        relationList[ fullFieldName ] = canonicalName;
                        select.append( fullFieldName );
                        select.append( ", " );
                    }
                    else
                    {
                        QString extalias = alias + "_" + reporter->name() + "2" + reporter->relation()->table();
                        QString fullFieldName = extalias + "." + reporter->relation()->field();
                        relationList[ fullFieldName ] = canonicalName;
                        select.append( fullFieldName );
                        select.append( ", " );
                        from.append( " LEFT JOIN " );
                        from.append( reporter->relation()->table() );
                        from.append( " as " );
                        from.append( extalias );
                        from.append( " ON " );
                        from.append( alias );
                        from.append( "." );
                        from.append( reporter->name() );
                        from.append( "=" );
                        from.append( extalias );
                        from.append( "." );
                        from.append( reporter->relation()->key() );
                    }
                    from.append( " LEFT JOIN " );
                    from.append( ( *it2 )->relation()->table() );
                    from.append( " as " );
                    from.append( alias );
                    from.append( " ON " );
                    from.append( ( *it ) ->alias() );
                    from.append( "." );
                    from.append( ( *it2 ) ->name() );
                    from.append( "=" );
                    from.append( alias );
                    from.append( "." );
                    from.append( ( *it2 )->relation()->key() );
                }
            }
            else
            {
                select.append( canonicalName );
                select.append( ", " );
            }
        }
    }

    QStringList::iterator sort = m_sort.begin();
    for ( ; sort != m_sort.end(); ++sort )
    {
        QStringList table_field_sort = QStringList::split( " ", ( *sort ) );
        QString table_field = ( table_field_sort[ 0 ] ).stripWhiteSpace();
        QString sort = ( table_field_sort[ 1 ] ).stripWhiteSpace();

        QMap<QString,QString>::Iterator relationIt = relationList.begin();
        for ( ; relationIt != relationList.end(); ++relationIt )
        {
            if ( relationIt.data() == table_field )
            {
                orderby.append( relationIt.key() + " " + sort );
                orderby.append( ", " );
            }
            else if ( select.contains( table_field ) )
            {
                orderby.append( table_field + " " + sort );
                orderby.append( ", " );
            }
        }
    }

    //Remove the trailing commas
    if ( !select.empty() )
        select.pop_back();
    if ( !orderby.empty() )
        orderby.pop_back();

    QString fullquerystring = "SELECT " + select.join( "" ) +
                              " FROM " + from.join( "" );
    if ( !orderby.empty() )
        fullquerystring += " ORDER BY " + orderby.join( "" );

    if ( fullquerystring != QString::null )
        kdDebug() << fullquerystring << endl;

    QSqlQuery query( fullquerystring, database->connection() );
    kdDebug() << "SQL Query has finished.  Constructing the report." << endl;

    if ( query.size() > 1000 )
    {
        QApplication::restoreOverrideCursor();
        QString warning = i18n("Creating a report with %1 records is very time consuming and will likely eat your memory. Do you really want to continue?").arg(QString::number( query.size() ));

        int cont = KMessageBox::warningContinueCancel( this, warning,
                   i18n( "Report is > 1000 records." ) );

        if ( cont == KMessageBox::Cancel )
        {
            static_cast<DataKiosk*>( widget ) ->setStatusRight( QString::number( t.elapsed() ) );
            return ;
        }

        QApplication::setOverrideCursor( QCursor( Qt::WaitCursor ) );
    }

    m_numRows = query.size();
    emit reportRefreshed();

    static_cast<DataKiosk*>( widget ) ->setStatusLeft( fullquerystring );

    QStringList xml;
    xml += "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
    xml += "<!DOCTYPE KugarData [\n";
    xml += "<!ELEMENT KugarData (Row* )>\n";
    xml += "<!ATTLIST KugarData\n";
    xml += "\tTemplate CDATA #REQUIRED>\n\n";

    xml += "<!ELEMENT Row EMPTY>\n";
    xml += "<!ATTLIST Row\n";
    xml += "\tlevel CDATA #REQUIRED>\n";
    xml += "]>\n\n";

    xml += "<KugarData Template=\"";
    xml += m_templateURL;
    xml += "\">\n";

    DataRecordList records;
    DataTableList::Iterator it3 = list.begin();
    for ( ; it3 != list.end(); ++it3 )
    {
        records[ ( *it3 )->alias() ] = new QSqlRecord();
    }

    QMap<int, QStringList> currentLevels;
    QMap<int, QStringList> temporaryLevels;
    QValueList<int> levels = templateFields.values();
    QValueList<int>::iterator levelIt = levels.begin();
    for ( ; levelIt != levels.end(); ++levelIt )
    {
        currentLevels[ ( *levelIt ) ] = QStringList();
        temporaryLevels[ ( *levelIt ) ] = QStringList();
    }

    while ( query.next() )
    {
        int i = -1;
        QStringList tt = QStringList::split( ", ", select.join( "" ) );

        QStringList::Iterator it4 = tt.begin();
        for ( ; it4 != tt.end(); ++it4 )
        {
            ++i;

            QString queryFieldName = relationList.contains( ( *it4 ) ) ? relationList[ ( *it4 ) ] : ( *it4 );

            QStringList table_field = QStringList::split( ".", queryFieldName );
            QString table = ( table_field[ 0 ] ).stripWhiteSpace();
            QString field = ( table_field[ 1 ] ).stripWhiteSpace();

//             kdDebug() << queryFieldName << " = " << query.value( i ).toString() << endl;

            if ( !records[ table ]->contains( field ) )
            {
                QSqlField f( field, query.value( i ).type() );
                f.setValue( query.value( i ) );
                records[ table ]->append( f );
            }
            else
                records[ table ]->setValue( field, query.value( i ) );

            QStringList row = temporaryLevels[ templateFields[ queryFieldName ] ];
            row.append( queryFieldName );
            row.append( "=\"" );

            //Kugar can only understand dates in a certain format
            if ( query.value( i ).type() == QVariant::Date ||
                 query.value( i ).type() == QVariant::DateTime )
            {
                row.append( query.value( i ).toDate().toString( "MM/dd/yyyy") );
            }
            else if ( query.value( i ).type() == QVariant::String )
            {
                row.append( sanitize( query.value( i ).toString() ) );
            }
            else
            {
                row.append( query.value( i ).toString() );
            }

            row.append( "\"" );
            row.append( " " );
            temporaryLevels[ templateFields[ queryFieldName ] ] = row;
        }

        DataTableList::Iterator it5 = list.begin();
        for ( ; it5 != list.end(); ++it5 )
        {
            DataFieldList fields = ( *it5 )->calculatedFields();
            DataFieldList::iterator it6 = fields.begin();
            for ( ; it6 != fields.end(); ++it6 )
            {
                QString canonicalName = ( *it5 ) ->alias() + "." + ( *it6 ) ->name();
                if ( !templateFields.contains( canonicalName ) )
                    continue;

                QStringList row = temporaryLevels[ templateFields[ canonicalName ] ];
                row.append( canonicalName );
                row.append( "=\"" );
                row.append( sanitize( ( *it6 )->calculateField( records ).toString() ) );
                row.append( "\"" );
                row.append( " " );
                temporaryLevels[ templateFields[ canonicalName ] ] = row;
            }
        }

        QValueList<int> levels = temporaryLevels.keys();
        QValueList<int>::iterator levelIt = --levels.end();
        for ( ; levelIt != --levels.begin(); --levelIt )
        {
            const int level = ( *levelIt );
            const QStringList cur = currentLevels[ level ];
            const QStringList tmp = temporaryLevels[ level ];

            if ( level == 0 || tmp != cur )
            {
                xml += "<Row level=\"";
                xml += QString::number( level );
                xml += "\" ";
                xml += tmp.join("");
                xml += "/>\n";
                currentLevels[ level ] = tmp;
            }
            temporaryLevels[ level ] = QStringList();
        }
    }

    xml += "</KugarData>";
//     kdDebug() << xml.join("") << endl;

    QString file = locateLocal( "tmp", "datakiosk-kugar-" );
    KTempFile tmp( file, ".kud" );
    *tmp.textStream() << xml.join( "" );
    if ( tmp.sync() )
    {
        static_cast<DataKiosk*>( widget ) ->partManager() ->setActivePart( 0L ); // sets as the active part
        m_part->openURL( KURL::fromPathOrURL( tmp.name() ) );
        static_cast<DataKiosk*>( widget ) ->partManager() ->setActivePart( m_part ); // sets as the active part
        m_needsRefresh = false;
        m_isInitialized = true;

        action( "print" ) ->setEnabled( true );
    }
    tmp.close();

    static_cast<DataKiosk*>( widget ) ->setStatusRight( QString::number( t.elapsed() ) );
    QApplication::restoreOverrideCursor();
}

DataTable *DataReport::parent() const
{
    return m_parent;
}

KParts::ReadOnlyPart *DataReport::kugarPart() const
{
    return m_part;
}

int DataReport::numRows() const
{
    return m_numRows;
}

QString DataReport::name() const
{
    return m_dataReportName;
}

void DataReport::setName( const QString &n )
{
    m_project->addName( n );
    m_project->removeName( m_dataReportName );

    m_dataReportName = n;
    emit signalNameChanged( m_dataReportName );
}

QString DataReport::iconName() const
{
    return m_dataReportIconName;
}

void DataReport::setIconName( const QString &n )
{
    m_dataReportIconName = n;
    m_project->setIconName( this, n );
}

QString DataReport::templateURL() const
{
    return m_templateURL;
}

void DataReport::setTemplateURL( const QString &url )
{
    m_templateURL = url;
}

QString DataReport::dataSearch() const
{
    return m_dataSearch;
}

void DataReport::setDataSearch( const QString &dataSearch )
{
    m_dataSearch = dataSearch;
}

void DataReport::addTable( const QString &table )
{
    m_tables.append( table );
}

void DataReport::clearTables()
{
    m_tables.clear();
}

QStringList DataReport::tables() const
{
    return m_tables;
}

QStringList DataReport::sort() const
{
    return m_sort;
}

void DataReport::setSort( const QStringList &sort )
{
    m_sort = sort;
}

void DataReport::queueRefresh()
{
    m_needsRefresh = true;
}

bool DataReport::needsRefresh()
{
    return m_needsRefresh;
}

bool DataReport::isInitialized()
{
    return m_isInitialized;
}

#ifdef widget
#undef widget
#endif

#include "datareport.moc"

Generated by  Doxygen 1.6.0   Back to index