Changeset 220:88f1d19f209a in SMSSender


Ignore:
Timestamp:
May 29, 2012 6:34:23 PM (8 years ago)
Author:
Sämy Zehnder <saemy.zehnder@…>
Branch:
default
Message:
  • Waiting for all tasks to finish at application close to ensure no one tries to do some sql stuff in a thread after the database is already closed.
  • Added ensureLoggedin() to IAccount.
  • Added missing config option "shared" to datatypes.pro
  • VersionedStorage? does not use a table for the version storage anymore but stores the version into the user_version pragma of sqlite.
  • Replaced SMAKE_VISIBLE by DATATYPES_SHARED_EXPORT.
  • Using haltIfCancelRequested() in STask now; this enables cancelling tasks.
  • Moved EAbortException into STask.
  • Added Info.plist which is used when deploying for mac.
  • Added -fvisibility=hidden flag for all projects.
  • Showing the minimum progress instead the mean progress if different task are running. This avoids progress jumps.

Schoolnet:

  • Moved status away from the question into a QuestionManager? where it is account dependent now (also affects db).
  • Added AnsweredAtSchoolnet? status for a question.
  • Improved schoolnet question list a lot.
Files:
12 added
5 deleted
59 edited
1 moved

Legend:

Unmodified
Added
Removed
  • gateways/ETHZ/src/business/bcaccount.cpp

    r217 r220  
    2727
    2828#include <exceptions/stdexceptions.h>
    29 #include <exceptions/eabortloadingexception.h>
    3029#include <exceptions/loginaccountexceptions.h>
    3130
  • gateways/ETHZ/src/business/bcaccount_task_sendsms.cpp

    r217 r220  
    2222#include <snetworkreply.h>
    2323#include <snumber.h>
    24 #include <exceptions/eabortloadingexception.h>
    2524
    2625#include "bcgateway.h"
     
    5453
    5554    try {
     55        setProgress(0);
    5656        try {
    57             setProgress(0);
    58             try {
    59                 setProgress(10);
     57            setProgress(10);
    6058
    61                 QStringList longSMSList = account()->splitTextToLongSMS(message());
     59            QStringList longSMSList = account()->splitTextToLongSMS(message());
    6260
    63                 int percent_per_sms = 70 / (longSMSList.size() * ceil(recipients().size() / (double)MAX_RECIPIENTS_PER_SMS));
    64                 QSetIterator<SContact> i(recipients());
    65                 foreach (QString msgPart, longSMSList) {
    66                     i.toFront();
    67                     do {
    68                         sendPartOfSMS(msgPart, i.next());
     61            int percent_per_sms = 70 / (longSMSList.size() * ceil(recipients().size() / (double)MAX_RECIPIENTS_PER_SMS));
     62            QSetIterator<SContact> i(recipients());
     63            foreach (QString msgPart, longSMSList) {
     64                i.toFront();
     65                do {
     66                    haltIfCancelRequested();
     67                    sendPartOfSMS(msgPart, i.next());
    6968
    70                         setProgress(progress() + percent_per_sms);
    71                     } while(i.hasNext()); // While there are recipients, for which the sms has not been sent yet
    72                 }
    73 
    74                 setSpecialResult(Account::srSuccess);
    75             } catch (EAbortException& e) {
    76                 // Processing of this is done later in this method
     69                    setProgress(progress() + percent_per_sms);
     70                } while(i.hasNext()); // While there are recipients, for which the sms has not been sent yet
    7771            }
    7872
    79             setProgress(90);
    80         } catch (EException& e) {
    81             qWarning() << "Error occured while sending sms.";
    82             setSpecialResult(Account::srError);
    83             throw;
     73            setSpecialResult(Account::srSuccess);
     74        } catch (EAbortException& e) {
     75            // Processing of this is done later in this method
    8476        }
    8577
    86         if (isCancelled()) {
    87             uint oldSpecialResult = specialResult();
    88             setSpecialResult(Account::srCancelled);
     78        setProgress(90);
     79    } catch (EException& e) {
     80        qWarning() << "Error occured while sending sms.";
     81        setSpecialResult(Account::srError);
     82        throw;
     83    }
    8984
    90             if (oldSpecialResult == Account::srSuccess) {
    91                 EException(tr("The sending of the sms was requested to cancel, but (a part of) the sms has already been sent."))
    92                         .raise();
    93             }
     85    if (isCancelled()) {
     86        uint oldSpecialResult = specialResult();
     87        setSpecialResult(Account::srCancelled);
     88
     89        if (oldSpecialResult == Account::srSuccess) {
     90            EException(tr("The sending of the sms was requested to cancel, but (a part of) the sms has already been sent."))
     91                    .raise();
    9492        }
    95     } catch (...) {
    96         throw;
    9793    }
    9894
  • gateways/Post/src/business/bcaccount.cpp

    r217 r220  
    2727
    2828#include <exceptions/stdexceptions.h>
    29 #include <exceptions/eabortloadingexception.h>
    3029#include <exceptions/loginaccountexceptions.h>
    3130
  • gateways/Post/src/business/bcaccount_task_sendsms.cpp

    r217 r220  
    2222#include <snetworkreply.h>
    2323#include <snumber.h>
    24 #include <exceptions/eabortloadingexception.h>
    2524
    2625namespace Post {
     
    5150
    5251    try {
     52        setProgress(0);
    5353        try {
    54             setProgress(0);
    55             try {
    56                 account()->reloadOnlineParams();
     54            account()->reloadOnlineParams();
    5755
    58                 QStringList longSMSList = account()->splitTextToLongSMS(message());
    59                 int shortSMSCnt         = account()->shortSMSCount(message());
     56            QStringList longSMSList = account()->splitTextToLongSMS(message());
     57            int shortSMSCnt         = account()->shortSMSCount(message());
    6058
    61                 if (shortSMSCnt * recipients().size() > account()->freeSMSCount()) {
    62                     EException(tr("You have only %1 SMS for free but you need at least %2 SMS left to send this message.")
    63                                    .arg(account()->freeSMSCount()).arg(shortSMSCnt * recipients().size()))
    64                             .raise();
    65                 }
    66                 setProgress(10);
     59            if (shortSMSCnt * recipients().size() > account()->freeSMSCount()) {
     60                EException(tr("You have only %1 SMS for free but you need at least %2 SMS left to send this message.")
     61                               .arg(account()->freeSMSCount()).arg(shortSMSCnt * recipients().size()))
     62                        .raise();
     63            }
     64            setProgress(10);
    6765
    68                 int percent_per_sms = 70 / (longSMSList.size() * ceil(recipients().size() / (double)MAX_RECIPIENTS_PER_SMS));
    69                 QSetIterator<SContact> i(recipients());
    70                 foreach (QString msgPart, longSMSList) {
    71                     i.toFront();
    72                     do {
    73                         removeRecipients();
     66            int percent_per_sms = 70 / (longSMSList.size() * ceil(recipients().size() / (double)MAX_RECIPIENTS_PER_SMS));
     67            QSetIterator<SContact> i(recipients());
     68            foreach (QString msgPart, longSMSList) {
     69                i.toFront();
     70                do {
     71                    removeRecipients();
    7472
    75                         int recipientCount = 0;
    76                         while (i.hasNext() && (recipientCount < MAX_RECIPIENTS_PER_SMS)) {
    77                             addRecipient(i.next());
    78                             ++recipientCount;
    79                         }
     73                    int recipientCount = 0;
     74                    while (i.hasNext() && (recipientCount < MAX_RECIPIENTS_PER_SMS)) {
     75                        addRecipient(i.next());
     76                        ++recipientCount;
     77                    }
    8078
    81                         sendLongSMS(msgPart);
     79                    haltIfCancelRequested();
     80                    sendLongSMS(msgPart);
    8281
    83                         setProgress(progress() + percent_per_sms);
    84                     } while(i.hasNext()); // While there are recipients, for which the sms has not been sent yet
    85                 }
    86 
    87                 setSpecialResult(Account::srSuccess);
    88             } catch (EAbortException& e) {
    89                 // Processing of this is done later in this method
     82                    setProgress(progress() + percent_per_sms);
     83                } while(i.hasNext()); // While there are recipients, for which the sms has not been sent yet
    9084            }
    9185
    92             account()->reloadOnlineParams();
    93 
    94             setProgress(90);
    95             //account()->logout();
    96         } catch (EException& e) {
    97             qWarning() << "Error occured while sending sms.";
    98             setSpecialResult(Account::srError);
    99             throw;
     86            setSpecialResult(Account::srSuccess);
     87        } catch (EAbortException& e) {
     88            // Processing of this is done later in this method
    10089        }
    10190
    102         if (isCancelled()) {
    103             uint oldSpecialResult = specialResult();
    104             setSpecialResult(Account::srCancelled);
     91        account()->reloadOnlineParams();
    10592
    106             if (oldSpecialResult == Account::srSuccess) {
    107                 EException(tr("The sending of the sms was requested to cancel, but (a part of) the sms has already been sent."))
    108                         .raise();
    109             }
     93        setProgress(90);
     94    } catch (EException& e) {
     95        qWarning() << "Error occured while sending sms.";
     96        setSpecialResult(Account::srError);
     97        throw;
     98    }
     99
     100    if (isCancelled()) {
     101        uint oldSpecialResult = specialResult();
     102        setSpecialResult(Account::srCancelled);
     103
     104        if (oldSpecialResult == Account::srSuccess) {
     105            EException(tr("The sending of the sms was requested to cancel, but (a part of) the sms has already been sent."))
     106                    .raise();
    110107        }
    111     } catch (...) {
    112         throw;
    113108    }
    114109
  • gateways/Schoolnet/lib/schoolnet.qrc

    r193 r220  
    22    <qresource prefix="/images">
    33        <file>schoolnet.ico</file>
    4         <file>open.png</file>
     4        <file>question_open.png</file>
     5        <file>question_correct_enabled.png</file>
     6        <file>question_correct_disabled.png</file>
     7        <file>question_wrong_enabled.png</file>
     8        <file>question_wrong_disabled.png</file>
     9        <file>question_loading.png</file>
     10        <file>question_open_disabled.png</file>
    511    </qresource>
    612    <qresource prefix="/certs">
  • gateways/Schoolnet/schoolnet.pro

    r217 r220  
    33TARGET = Schoolnet
    44QT += xml
     5
    56include(locale/locale.pri)
    67HEADERS += src/business/contactimporter/bccontactimporter.h \
     
    1617    src/persistence/daaccountstorageofficer.h \
    1718    src/persistence/deepthought/dadeepthoughtstorage.h \
    18     src/ui/models/questionitemdelegate.h \
    1919    src/ui/models/questionitemmodel.h \
     20    src/ui/models/questionsortmodel.h \
    2021    src/ui/models/questiontreemodel.h \
    21     src/ui/models/questiontreemodel_p.h \
    2222    src/ui/widgets/vcquestionbox/vcquestionbox.h \
    2323    src/ui/widgets/vcsettings/vcsettingswidget.h \
    2424    src/ui/vcquestionlist/vcquestionlist.h \
    2525    src/ui/vcaccount_guiintegration.h \
    26     src/library.h
     26    src/library.h \
     27    src/business/deepthought/datatypes_p.h
    2728SOURCES += src/business/contactimporter/bccontactimporter.cpp \
    2829    src/business/deepthought/datatypes.cpp \
     
    4344    src/persistence/daaccountstorageofficer.cpp \
    4445    src/persistence/deepthought/dadeepthoughtstorage.cpp \
    45     src/ui/models/questionitemdelegate.cpp \
    4646    src/ui/models/questionitemmodel.cpp \
     47    src/ui/models/questionsortmodel.cpp \
    4748    src/ui/models/questiontreemodel.cpp \
    4849    src/ui/widgets/vcquestionbox/vcquestionbox.cpp \
  • gateways/Schoolnet/src/business/bcaccount.cpp

    r218 r220  
    3434
    3535#include <exceptions/stdexceptions.h>
    36 #include <exceptions/eabortloadingexception.h>
    3736#include <exceptions/loginaccountexceptions.h>
    3837
     
    5251BCAccount::BCAccount(QObject* parent, IStorage* storage)
    5352    : AbstractAccount(parent)
    54     , httpHelper_(new BCHttpHelper(this))
    5553    , sendAs_(saNumber)
    5654    , useDeepThought_(true)
    5755{
     56    httpHelper_ = new BCHttpHelper(this);
     57
    5858    init(new DAAccountStorageOfficer(this, this, storage), new SStdAccountValidator(this, this));
    5959
  • gateways/Schoolnet/src/business/bcaccount_task_sendsms.cpp

    r217 r220  
    2121
    2222#include <snumber.h>
    23 #include <exceptions/eabortloadingexception.h>
    2423
    2524#include "business/bchttphelper.h"
     
    5150     */
    5251
     52    try {
     53        setProgress(0);
     54        try {
     55            account()->reloadOnlineParams();
    5356
    54     try {
    55         try {
    56             setProgress(0);
    57             try {
    58                 account()->reloadOnlineParams();
     57            QStringList longSMSList = account()->splitTextToLongSMS(message());
     58            int shortSMSCnt         = account()->shortSMSCount(message());
    5959
    60                 QStringList longSMSList = account()->splitTextToLongSMS(message());
    61                 int shortSMSCnt         = account()->shortSMSCount(message());
     60            if (shortSMSCnt * recipients().size() > account()->freeSMSCount()) {
     61                EException(tr("You have only %1 SMS for free but you need at least %2 SMS left to send this message.")
     62                               .arg(account()->freeSMSCount()).arg(shortSMSCnt * recipients().size()))
     63                        .raise();
     64            }
     65            setProgress(10);
    6266
    63                 if (shortSMSCnt * recipients().size() > account()->freeSMSCount()) {
    64                     EException(tr("You have only %1 SMS for free but you need at least %2 SMS left to send this message.")
    65                                    .arg(account()->freeSMSCount()).arg(shortSMSCnt * recipients().size()))
    66                             .raise();
    67                 }
    68                 setProgress(10);
     67            int percent_per_sms = 70 / (longSMSList.size() * ceil(recipients().size() / (double)MAX_RECIPIENTS_PER_SMS));
     68            QSetIterator<SContact> i(recipients());
     69            foreach (QString msgPart, longSMSList) {
     70                i.toFront();
     71                do {
     72                    removeRecipients();
    6973
    70                 int percent_per_sms = 70 / (longSMSList.size() * ceil(recipients().size() / (double)MAX_RECIPIENTS_PER_SMS));
    71                 QSetIterator<SContact> i(recipients());
    72                 foreach (QString msgPart, longSMSList) {
    73                     i.toFront();
    74                     do {
    75                         removeRecipients();
     74                    int recipientCount = 0;
     75                    while (i.hasNext() && (recipientCount < MAX_RECIPIENTS_PER_SMS)) {
     76                        addRecipient(i.next());
     77                        ++recipientCount;
     78                    }
    7679
    77                         int recipientCount = 0;
    78                         while (i.hasNext() && (recipientCount < MAX_RECIPIENTS_PER_SMS)) {
    79                             addRecipient(i.next());
    80                             ++recipientCount;
    81                         }
     80                    haltIfCancelRequested();
     81                    sendLongSMS(msgPart);
    8282
    83                         sendLongSMS(msgPart);
    84 
    85                         setProgress(progress() + percent_per_sms);
    86                     } while(i.hasNext()); // While there are recipients, for which the sms has not been sent yet
    87                 }
    88 
    89                 setSpecialResult(Account::srSuccess);
    90             } catch (EAbortException& e) {
    91                 // Processing of this is done later in this method
     83                    setProgress(progress() + percent_per_sms);
     84                } while(i.hasNext()); // While there are recipients, for which the sms has not been sent yet
    9285            }
    9386
    94             account()->reloadOnlineParams();
    95 
    96             setProgress(90);
    97         } catch (EException& e) {
    98             qWarning() << "Error occured while sending sms.";
    99             setSpecialResult(Account::srError);
    100             throw;
     87            setSpecialResult(Account::srSuccess);
     88        } catch (EAbortException& e) {
     89            // Processing of this is done later in this method
    10190        }
    10291
    103         if (isCancelled()) {
    104             uint oldSpecialResult = specialResult();
    105             setSpecialResult(Account::srCancelled);
     92        account()->reloadOnlineParams();
    10693
    107             if (oldSpecialResult == Account::srSuccess) {
    108                 EException(tr("The sending of the sms was requested to cancel, but (a part of) the sms has already been sent."))
    109                         .raise();
    110             }
     94        setProgress(90);
     95    } catch (EException& e) {
     96        qWarning() << "Error occured while sending sms.";
     97        setSpecialResult(Account::srError);
     98        throw;
     99    }
     100
     101    if (isCancelled()) {
     102        uint oldSpecialResult = specialResult();
     103        setSpecialResult(Account::srCancelled);
     104
     105        if (oldSpecialResult == Account::srSuccess) {
     106            EException(tr("The sending of the sms was requested to cancel, but (a part of) the sms has already been sent."))
     107                    .raise();
    111108        }
    112     } catch (...) {
    113         throw;
    114109    }
    115110
  • gateways/Schoolnet/src/business/contactimporter/bccontactimporter.cpp

    r217 r220  
    2121#include <scontact.h>
    2222#include <snumber.h>
     23
     24#include <eexception.h>
    2325
    2426#include "business/bcgateway.h"
  • gateways/Schoolnet/src/business/deepthought/datatypes.cpp

    r217 r220  
    1616 along with this program.  If not, see <http://www.gnu.org/licenses/>.
    1717 */
    18 
    1918#include "datatypes.h"
     19#include "datatypes_p.h"
    2020
    2121#include "exceptions/eexception.h"
     
    6767    return d->answers;
    6868}
    69 uint Question::status() const {
    70     return d->status;
    71 }
    72 
    73 bool Question::isStatusAtSchoolnetChecked() const {
    74     return status() & StatusAtSchoolnetChecked;
    75 }
    7669
    7770bool Question::hasCorrectAnswer() const {
     
    107100void Question::setAnswers(const QList<Answer>& answers) {
    108101    SET_IF_DIFFERENT(d->answers, answers)
    109 }
    110 void Question::setStatus(uint status) {
    111     SET_IF_DIFFERENT(d->status, status)
    112 }
    113 
    114 void Question::setStatusAtSchoolnetChecked(bool isIt) {
    115     if (isIt) {
    116         setStatus(d->status | StatusAtSchoolnetChecked);
    117     } else {
    118         setStatus(d->status & !StatusAtSchoolnetChecked);
    119     }
    120102}
    121103
     
    165147    return d->correct;
    166148}
    167 bool Answer::doesDeepThoughtKnowsCorrectness() const {
     149bool Answer::doesDeepThoughtKnowCorrectness() const {
    168150    return d->deepThoughtKnowsCorrectness;
    169151}
  • gateways/Schoolnet/src/business/deepthought/datatypes.h

    r218 r220  
    3838namespace Schoolnet {
    3939
    40 class BCAccount;
    41 
    42 class QuestionData : public SShareable {
    43     Q_OBJECT
    44 
    45 public:
    46     QuestionData()
    47         : id(-1)
    48         , status(0)
    49     {}
    50     QuestionData(const QuestionData& other)
    51         : SShareable()
    52         , id(other.id)
    53         , nrNodeGuId(other.nrNodeGuId)
    54         , eventTarget(other.eventTarget)
    55         , language(other.language)
    56         , text(other.text)
    57         , hint(other.hint)
    58         , answers(other.answers)
    59         , status(other.status)
    60     {}
    61 
    62 public:
    63     int            id;
    64     QString        nrNodeGuId;
    65     QString        eventTarget;
    66     QString        language;
    67     QString        text;
    68     QString        hint;
    69     QList<Answer>  answers;
    70     uint           status;
    71 };
    72 
     40class QuestionData;
    7341class Question {
    7442    S_SHARED(Question, public)
     
    7745public:
    7846    enum Status {
    79         StatusAtSchoolnetChecked = 0x01
     47        StatusAtSchoolnetChecked = 0x1,
     48        AnsweredAtSchoolnet = 0x2
     49        // next should be 0x4
    8050    };
    8151
     
    9060    QString        text() const;
    9161    QString        hint() const;
    92     uint           status() const;
    93     bool           isStatusAtSchoolnetChecked() const;
    9462
    9563    QList<Answer>  answers() const;
     
    10371    void           setHint(const QString& hint);
    10472    void           setAnswers(const QList<Answer>& answers);
    105     void           setStatus(const uint status);
    106     void           setStatusAtSchoolnetChecked(bool isIt);
    10773
    10874    void           addAnswer(const Answer& answer);
     
    11783
    11884
    119 class AnswerData : public SShareable {
    120     Q_OBJECT
    121 
    122 public:
    123     AnswerData()
    124         : question(NULL)
    125     {}
    126     AnswerData(const Question& question)
    127         : question(question)
    128     {}
    129     AnswerData(const AnswerData& other)
    130         : SShareable()
    131         , question(other.question)
    132         , id(other.id)
    133         , text(other.text)
    134         , answered(other.answered)
    135         , correct(other.correct)
    136         , deepThoughtKnowsCorrectness(other.deepThoughtKnowsCorrectness)
    137     {}
    138 
    139 public:
    140     Question question;
    141     QString  id;
    142     QString  text;
    143     bool     answered;
    144     bool     correct;
    145     bool     deepThoughtKnowsCorrectness;
    146 };
    147 
     85class AnswerData;
    14886class Answer {
    14987    S_SHARED(Answer, public)
     
    15391    friend class QuestionManager; // setters
    15492    friend class DADeepThoughtStorage; // setters
     93    friend class AnswerToDeepThoughtTask; // setDeepThoughtKnowsCorrectness(bool)
    15594
    15695public:
     
    165104    bool     isAnswered() const;
    166105    bool     isCorrect() const;
    167     bool     doesDeepThoughtKnowsCorrectness() const;
     106    bool     doesDeepThoughtKnowCorrectness() const;
    168107
    169108    void     setId(const QString& id);
  • gateways/Schoolnet/src/business/deepthought/deepthought_daemon_deepthought.cpp

    r217 r220  
    3232    : QObject(parent)
    3333{
    34     connect(QuestionManager::instance(), SIGNAL(answerProvided(const Answer&)),
    35             this, SLOT(onAnswerProvided(const Answer&)));
     34    connect(QuestionManager::instance(), SIGNAL(answerCorrectnessProvided(const Answer&)),
     35            this, SLOT(onAnswerCorrectnessProvided(const Answer&)));
    3636
    3737    connect(Library::instance()->managerFactory()->accountManager(), SIGNAL(accountAdded(IAccount*)),
     
    101101
    102102/*** This part comes in touch if answers are provided by the user and are checked for their correctness at schoolnet. ***/
    103 void DeepThoughtDaemon::onAnswerProvided(const Answer& answer) {
    104     if (answer.doesDeepThoughtKnowsCorrectness()) {
     103void DeepThoughtDaemon::onAnswerCorrectnessProvided(const Answer& answer) {
     104    if (answer.doesDeepThoughtKnowCorrectness()) {
    105105        // Don't bother DeepThought with things he already knows...
    106106        return;
  • gateways/Schoolnet/src/business/deepthought/deepthought_daemon_schoolnet.cpp

    r217 r220  
    3232    , account_(account)
    3333{
    34     connect(QuestionManager::instance(), SIGNAL(answerProvided(const Answer&)),
    35             this, SLOT(onAnswerProvided(const Answer&)));
     34    connect(QuestionManager::instance(), SIGNAL(answerCorrectnessProvided(const Answer&)),
     35            this, SLOT(onAnswerCorrectnessProvided(const Answer&)));
    3636
    3737    connect(account, SIGNAL(loggedIn(IAccount*)), this, SLOT(onAccountLoggedIn()));
     
    6060
    6161/* This part is used to profit from correct answers which are provided by either the user (on an other account) or deepthought. */
    62 void SchoolnetDaemon::onAnswerProvided(const Answer& answer) {
     62void SchoolnetDaemon::onAnswerCorrectnessProvided(const Answer& answer) {
    6363    if (!answer.isCorrect()) {
    6464        // The answer is incorrect and therefore not interresting for us
     
    9292
    9393void SchoolnetDaemon::onDeepThoughtDataFetchFinished(STask* task) {
    94     if (task->taskResult() != STask::rSuccess) {
     94    Q_UNUSED(task)
     95    /* Don't do this, since we reload the next time, the account loggs in.
     96     * The problem with this here is, that we login twice (ensureLoggedIn)
     97     * and call CheckQuestionStatusAtSchoolnetTask twice at startup (since
     98     * we are listening at the loggin event of the account there).
     99     */
     100
     101    /*if (task->taskResult() != STask::rSuccess) {
    95102        return;
    96103    }
     
    101108
    102109    STask* cqsas = new CheckQuestionStatusAtSchoolnetTask(account());
    103     cqsas->start();
     110    cqsas->start();*/
    104111}
    105112
  • gateways/Schoolnet/src/business/deepthought/deepthought_daemons.h

    r217 r220  
    4848
    4949private slots:
    50     void onAnswerProvided(const Answer& answer);
     50    void onAnswerCorrectnessProvided(const Answer& answer);
    5151    void onAccountLoggedIn();
    5252    void onDeepThoughtDataFetchFinished(STask* task);
     
    7474    void fetchRemoteQuestions();
    7575
    76     void onAnswerProvided(const Answer& answer);
     76    void onAnswerCorrectnessProvided(const Answer& answer);
    7777
    7878private:
  • gateways/Schoolnet/src/business/deepthought/deepthought_task_answertodeepthougth.cpp

    r217 r220  
    2121#include <snetworkhelper.h>
    2222#include <snetworkreply.h>
     23
     24#include <exceptions/enetworkexception.h>
    2325
    2426#include "datatypes.h"
     
    4850                                                                            .arg(answer().id())
    4951                                                                            .arg(question.hint()));
    50     // TODO: Check if the proposal was successful
    51     Q_UNUSED(reply);
     52    if (reply->error() == QNetworkReply::NoError) {
     53        answer().setDeepThoughtKnowsCorrectness(true);
     54        QuestionManager::instance()->saveQuestion(question);
     55    } else {
     56        ENetworkException(reply->error(), "Could not inform DeepThought about the correct answer!")
     57                .addDebugInfo("questionId", question.id())
     58                .addDebugInfo("answerId", answer().id())
     59                .raise();
     60    }
    5261}
    5362
  • gateways/Schoolnet/src/business/deepthought/deepthought_task_answertoschoolnet.cpp

    r217 r220  
    2020
    2121#include <iaccount_tasks.h>
     22
     23#include <eexception.h>
    2224
    2325#include "business/bcaccount.h"
     
    4951    }
    5052
    51     STask* login = account()->createLoginTask();
    52     startSubTask(login, true);
     53    account()->ensureLoggedIn();
    5354
    5455    Question question = answer().question();
     
    170171    if (isGratulation) {
    171172        // Correct answer
    172         QuestionManager::instance()->provideAnswer(selectedAnswer, true);
     173        QuestionManager::instance()->provideAnswerCorrectness(selectedAnswer, true);
    173174        questionInfo = qiCorrectAnswerFound;
    174175    } else {
     
    182183        if (isSorryHint) {
    183184            // Answered wrong, but hint is given.
    184             QuestionManager::instance()->provideAnswer(selectedAnswer, false);
     185            QuestionManager::instance()->provideAnswerCorrectness(selectedAnswer, false);
    185186            questionInfo = qiIncorrectAnswerFound;
    186187
     
    193194            foreach (Answer answer, question.answers()) {
    194195                if (answerStr.toLower().contains(answer.text().toLower())) {
    195                     // Would have been correct.
    196                     QuestionManager::instance()->provideAnswer(answer, true);
     196                    // This one would have been correct.
     197                    QuestionManager::instance()->provideAnswerCorrectness(answer, true);
    197198                    questionInfo = qiCorrectAnswerFound;
    198199                    break;
  • gateways/Schoolnet/src/business/deepthought/deepthought_task_check_questionstatus_at_schoolnet.cpp

    r217 r220  
    1919#include "deepthought_tasks.h"
    2020
     21#include <iaccount_tasks.h>
     22
    2123#include "business/bcaccount.h"
    2224#include "business/bchttphelper.h"
     
    3840    QList<Question> questions = QuestionManager::instance()->questions();
    3941
     42    account()->ensureLoggedIn();
     43
    4044    int pos = 0;
    4145    foreach (Question question, questions) {
    4246        setProgress(pos++ / (double)questions.size() * 100);
    4347
    44         if (question.isStatusAtSchoolnetChecked()) {
     48        if (QuestionManager::instance()->isStatusAtSchoolnetChecked(account(), question)) {
    4549            continue;
    4650        }
    4751
    48         if (!QuestionManager::instance()->isQuestionAnsweredAtSchoolnet(account(), question)) {
    49             QString html = QString::fromUtf8(account()->httpHelper()->syncGet(URL_GETPOINTSPAGE + question.nrNodeGuId()));
     52        try {
     53            if (!QuestionManager::instance()->isQuestionAnsweredAtSchoolnet(account(), question)) {
     54                QString html = QString::fromUtf8(account()->httpHelper()->syncGet(URL_GETPOINTSPAGE + question.nrNodeGuId()));
    5055
    51             QString submitId = question.eventTarget().replace(':', '_') + "_cmdAnswer";
    52             QRegExp rx("<a disabled=\"disabled\" id=\"" + submitId + "\">Get it!</a>");
    53             rx.setCaseSensitivity(Qt::CaseInsensitive);
     56                QString submitId = question.eventTarget().replace(':', '_') + "_cmdAnswer";
     57                QRegExp rx("<a disabled=\"disabled\" id=\"" + submitId + "\">Get it!</a>");
     58                rx.setCaseSensitivity(Qt::CaseInsensitive);
    5459
    55             if (rx.indexIn(html) != -1) {
    56                 QuestionManager::instance()->setQuestionAnsweredAtSchoolnet(account(), question);
     60                if (rx.indexIn(html) != -1) {
     61                    QuestionManager::instance()->setQuestionAnsweredAtSchoolnet(account(), question);
     62                }
    5763            }
     64
     65            QuestionManager::instance()->saveQuestion(question);
     66            QuestionManager::instance()->setStatusAtSchoolnetChecked(account(), question);
     67        } catch (const EException &ex) {
     68            // Do not stop whole process just because of one question..
     69            qDebug() << ex.chainedWhat();
    5870        }
    59 
    60         question.setStatusAtSchoolnetChecked(true);
    61         QuestionManager::instance()->saveQuestion(question);
    6271    }
    6372}
  • gateways/Schoolnet/src/business/deepthought/deepthought_task_fetchdatafromdeepthought.cpp

    r217 r220  
    2626#include <snetworkhelper.h>
    2727#include <snetworkreply.h>
     28
     29#include <eexception.h>
    2830
    2931#include "datatypes.h"
     
    122124        // Now send updates about answers which were marked as answered
    123125        foreach (Answer answer, answeredAnswers) {
    124             QuestionManager::instance()->provideAnswer(answer, answer.isCorrect());
     126            QuestionManager::instance()->provideAnswerCorrectness(answer, answer.isCorrect());
    125127        }
    126128    }
  • gateways/Schoolnet/src/business/deepthought/questionmanager.cpp

    r217 r220  
    1616 along with this program.  If not, see <http://www.gnu.org/licenses/>.
    1717 */
    18 
    1918#include "questionmanager.h"
    2019
     20#include <iaccount.h>
     21#include <imanagers.h>
     22
     23#include "library.h"
    2124#include "business/bcaccount.h"
    22 #include "datatypes.h"
    2325
    2426#include "persistence/deepthought/dadeepthoughtstorage.h"
     
    5961        if (acceptedLanguages.contains(question.language()) &&  // Correct language?
    6062            !question.hasCorrectAnswer() &&                     // Not already answered?
    61             question.isStatusAtSchoolnetChecked() &&            // Has the status at schoolnet been checked?
     63            isStatusAtSchoolnetChecked(account, question) &&    // Has the status at schoolnet been checked?
    6264            !isQuestionAnsweredAtSchoolnet(account, question))  // Not already answered at schoolnet?
    6365        {
     
    6668    }
    6769    return openQuestions;
     70}
     71
     72/**
     73 * Checks if this question is not answered yet at any schoolnet account.
     74 */
     75bool QuestionManager::isQuestionUnansweredAtSomeAccount(const Question &question) const {
     76    return getAccountsWhereQuestionUnanswered(question).size() > 0;
     77}
     78
     79/**
     80 * Returns the accounts where the question is unanswered.<br/>
     81 * Questions where the status is not checked yet are not returned.
     82 */
     83QSet<BCAccount *> QuestionManager::getAccountsWhereQuestionUnanswered(const Question &question) const {
     84    QSet<BCAccount *>  ret;
     85    foreach (IAccount *account, Library::instance()->managerFactory()->accountManager()->getAccountList()) {
     86        if (account->gateway() == BCGateway::instance()) {
     87            BCAccount* acc = static_cast<BCAccount*>(account);
     88
     89            if (!isStatusAtSchoolnetChecked(acc, question)) {
     90                continue;
     91            }
     92
     93            if (!isQuestionAnsweredAtSchoolnet(acc, question)) {
     94                ret.insert(acc);
     95            }
     96        }
     97    }
     98    return ret;
     99}
     100
     101/**
     102 * @see DADeepThoughtStorage::isStatusAtSchoolnetChecked()
     103 */
     104bool QuestionManager::isStatusAtSchoolnetChecked(BCAccount *account, const Question &question) const {
     105    return DADeepThoughtStorage::instance()->isQuestionStatusAtSchoolnetChecked(account, question);
     106}
     107
     108/**
     109 * Returns all accounts where the question status at schoolnet is checked.
     110 *
     111 * @see DADeepThoughtStorage::isStatusAtSchoolnetChecked()
     112 */
     113QSet<BCAccount *> QuestionManager::getAccountsWhereQuestionStatusAtSchoolnetChecked(const Question &question) const {
     114    QSet<BCAccount *>  ret;
     115    foreach (IAccount *account, Library::instance()->managerFactory()->accountManager()->getAccountList()) {
     116        if (account->gateway() == BCGateway::instance()) {
     117            BCAccount* acc = static_cast<BCAccount*>(account);
     118
     119            if (isStatusAtSchoolnetChecked(acc, question)) {
     120                ret.insert(acc);
     121            }
     122        }
     123    }
     124    return ret;
     125}
     126
     127/**
     128 * @see DADeepThoughtStorage::setStatusAtSchoolnetChecked()
     129 */
     130void QuestionManager::setStatusAtSchoolnetChecked(BCAccount *account, const Question &question) const {
     131    DADeepThoughtStorage::instance()->setQuestionStatusAtSchoolnetChecked(account, question);
    68132}
    69133
     
    76140
    77141/**
     142 * Sets the "status at schoolnet checked" and the "question answered at schoolnet" flag.
     143 *
    78144 * @see DADeepThoughtStorage::setQuestionAnsweredAtSchoolnet()
    79145 */
    80146void QuestionManager::setQuestionAnsweredAtSchoolnet(BCAccount* account, const Question& question) const {
     147    setStatusAtSchoolnetChecked(account, question);
    81148    DADeepThoughtStorage::instance()->setQuestionAnsweredAtSchoolnet(account, question);
    82149}
     
    107174 * Afterwards it emits the answerProvided signal.
    108175 */
    109 void QuestionManager::provideAnswer(Answer answer, bool correct) {
     176void QuestionManager::provideAnswerCorrectness(Answer answer, bool correct) {
    110177    answer.setAnswered(true);
    111178    answer.setCorrect(correct);
    112179    saveQuestion(answer.question());
    113180
    114     emit answerProvided(answer);
     181    emit answerCorrectnessProvided(answer);
    115182}
    116183
  • gateways/Schoolnet/src/business/deepthought/questionmanager.h

    r217 r220  
    3838
    3939public:
    40     QList<Question> questions();
    41     QList<Question> openQuestions(BCAccount* account, const QStringList& acceptedLanguages);
     40    QList<Question>   questions();
     41    QList<Question>   openQuestions(BCAccount* account, const QStringList& acceptedLanguages);
    4242
    43     void            pushQuestionBack(const Question& question);
    44     void            saveQuestion(Question question);
    45     void            deleteQuestion(Question question);
     43    void              pushQuestionBack(const Question& question);
     44    void              saveQuestion(Question question);
     45    void              deleteQuestion(Question question);
    4646
    47     bool            isQuestionAnsweredAtSchoolnet(BCAccount* account, const Question& question) const;
    48     void            setQuestionAnsweredAtSchoolnet(BCAccount* account, const Question& question) const;
     47    bool              isStatusAtSchoolnetChecked(BCAccount *account, const Question &question) const;
     48    QSet<BCAccount *> getAccountsWhereQuestionStatusAtSchoolnetChecked(const Question &question) const;
     49    void              setStatusAtSchoolnetChecked(BCAccount *account, const Question &question) const;
     50    bool              isQuestionUnansweredAtSomeAccount(const Question &question) const;
     51    QSet<BCAccount *> getAccountsWhereQuestionUnanswered(const Question &question) const;
     52    bool              isQuestionAnsweredAtSchoolnet(BCAccount* account, const Question& question) const;
     53    void              setQuestionAnsweredAtSchoolnet(BCAccount* account, const Question& question) const;
    4954
    50     void            provideAnswer(Answer answer, bool correct);
     55    void              provideAnswerCorrectness(Answer answer, bool correct);
    5156
    5257signals:
    53     void            answerProvided(const Answer& answer);
     58    void              answerCorrectnessProvided(const Answer& answer);
    5459
    5560private:
    56     void            appendQuestion(const Question& question);
    57     void            appendQuestions(const QList<Question>& questions);
     61    void              appendQuestion(const Question& question);
     62    void              appendQuestions(const QList<Question>& questions);
    5863
    5964private:
    60     bool            questionListInitialized_;
     65    bool questionListInitialized_;
    6166    QList<Question> questions_;
    6267};
  • gateways/Schoolnet/src/persistence/deepthought/dadeepthoughtstorage.cpp

    r218 r220  
    3030S_SINGLETON_IMPL(DADeepThoughtStorage)
    3131
    32 const uint DADeepThoughtStorage::ACTUAL_DB_VERSION = 2;
     32const uint DADeepThoughtStorage::ACTUAL_DB_VERSION = 3;
    3333
    3434DADeepThoughtStorage::DADeepThoughtStorage() {
     
    5050
    5151void DADeepThoughtStorage::doVersionUpdate(uint storageVersion) {
    52     switch (storageVersion) {
    53         case 0: initDB();      // The db is uninitialized
    54         case 1: version1To2(); // Added status flag to t_question
    55 
    56 
    57         break;
    58         default: {
    59             EException("Unimplemented newest version update").raise();
     52    connection().transaction();
     53    try {
     54        switch (storageVersion) {
     55            case 0: initDB();      // The db is uninitialized
     56            case 1: version1To2(); // Added status flag to t_question
     57            case 2: version2To3(); // Renamed t_answered_at_schoolnet into t_status_at_schoolnet; Moved status flag from t_question to t_status_at_schoolnet
     58
     59
     60            break;
     61            default: {
     62                EException("Unimplemented newest version update").raise();
     63            }
    6064        }
     65
     66        connection().commit();
     67    } catch (EException& e) {
     68        connection().rollback();
     69        EException("Unable to do version update.")
     70                .chain(e)
     71                .raise();
    6172    }
    6273}
     
    7687                          "language            TEXT    NOT NULL, "
    7788                          "questionText        TEXT    NOT NULL "
    78                          ");")) {
    79             EException(query.lastError().text()).raise();
    80         }
     89                         ");")) throw 0;
     90
    8191        if (!query.exec("CREATE TABLE IF NOT EXISTS t_answer ("
    8292                          "id         TEXT    NOT NULL, "
     
    8898                          "PRIMARY KEY (questionId, id), "
    8999                          "FOREIGN KEY (questionId) REFERENCES t_question (id) ON UPDATE CASCADE ON DELETE CASCADE "
    90                         ");")) {
    91             EException(query.lastError().text()).raise();
    92         }
     100                        ");")) throw 0;
     101
    93102        if (!query.exec("CREATE TABLE IF NOT EXISTS t_answered_at_schoolnet ("
    94103                          "account    TEXT    NOT NULL, "
     
    96105                          "PRIMARY KEY (account, questionId), "
    97106                          "FOREIGN KEY (questionId) REFERENCES t_question (id) ON UPDATE CASCADE ON DELETE CASCADE "
    98                         ");")) {
    99             EException(query.lastError().text()).raise();
    100         }
     107                        ");")) throw 0;
     108
    101109        if (!query.exec("CREATE TABLE IF NOT EXISTS t_lastrun ("
    102110                          "lastrun INTEGER "
    103                         ");")) {
    104             EException(query.lastError().text()).raise();
    105         }
    106     } catch (EException& e) {
    107         Storage::EWriteException(QObject::tr("Could not write the table definitions to the database."))
    108                     .addDebugInfo("sql-error", e.what())
    109                     .raise();
     111                        ");")) throw 0;
     112    } catch (...) {
     113        EException("Error in initDB")
     114                .addDebugInfo("sql-error", query.lastError().text())
     115                .raise();
    110116    }
    111117}
     
    115121                      "ADD COLUMN status INTEGER NOT NULL DEFAULT 0;"))
    116122    {
    117         EException("Could not update the database to the newest version.")
     123        EException("Error in version1To2.")
    118124                .addDebugInfo("sql-error", query.lastError().text())
    119125                .raise();
    120126    }
    121127}
     128void DADeepThoughtStorage::version2To3() {
     129    QSqlQuery query = createQuery();
     130    try {
     131        // Rename t_answered_at_schoolnet into t_status_at_schoolnet
     132        if (!query.exec("ALTER TABLE t_answered_at_schoolnet RENAME TO t_status_at_schoolnet;")) throw 0;
     133        if (!query.exec("ALTER TABLE t_status_at_schoolnet ADD COLUMN status INTEGER NOT NULL DEFAULT 0;")) throw 0;
     134
     135        // Remove the status column from t_question
     136        if (!query.exec("CREATE TEMPORARY TABLE t_question_backup AS "
     137                        "SELECT id, nrNodeGuId, eventTarget, language, questionText "
     138                        "  FROM t_question;")) throw 0;
     139        if (!query.exec("DROP TABLE t_question;")) throw 0;
     140        if (!query.exec("CREATE TABLE t_question AS SELECT * FROM t_question_backup;")) throw 0;
     141        if (!query.exec("DROP TABLE t_question_backup;")) throw 0;
     142    } catch (...) {
     143        EException("Error in version2To3.")
     144                .addDebugInfo("sql-error", query.lastError().text())
     145                .raise();
     146    }
     147}
    122148
    123149QSqlDatabase DADeepThoughtStorage::connection() const {
     
    129155
    130156
    131 int DADeepThoughtStorage::_getQuestionId(const Question& question) {
     157int DADeepThoughtStorage::_getQuestionId(const Question &question) {
    132158    QSqlQuery query = createQuery();
    133159
     
    153179}
    154180
    155 void DADeepThoughtStorage::updateQuestion(Question& question) {
     181void DADeepThoughtStorage::updateQuestion(Question &question) {
    156182    QSqlQuery query = createQuery();
    157183    if (question.id() > 0) {
    158184        query.prepare("UPDATE t_question SET "
    159185                          "language=:language, "
    160                           "questionText=:questionText, "
    161                           "status=:status "
     186                          "questionText=:questionText "
    162187                        "WHERE (id = :id); ");
    163188        query.bindValue(":id", question.id());
    164189    } else {
    165         query.prepare("INSERT INTO t_question (nrNodeGuId, eventTarget, language, questionText, status) "
    166                         "VALUES(:nrNodeGuId, :eventTarget, :language, :questionText, :status); ");
     190        query.prepare("INSERT INTO t_question (nrNodeGuId, eventTarget, language, questionText) "
     191                        "VALUES(:nrNodeGuId, :eventTarget, :language, :questionText); ");
    167192        query.bindValue(":nrNodeGuId", question.nrNodeGuId());
    168193        query.bindValue(":eventTarget", question.eventTarget());
     
    170195    query.bindValue(":language", question.language());
    171196    query.bindValue(":questionText", question.text());
    172     query.bindValue(":status", question.status());
    173197
    174198    if (!query.exec()) {
     
    219243}
    220244
    221 void DADeepThoughtStorage::updateAnswer(const Answer& answer) {
     245void DADeepThoughtStorage::updateAnswer(const Answer &answer) {
    222246    QSqlQuery query = createQuery();
    223247
     
    247271    query.bindValue(":answered", answer.isAnswered());
    248272    query.bindValue(":correct", answer.isCorrect());
    249     query.bindValue(":deepThought", answer.doesDeepThoughtKnowsCorrectness());
     273    query.bindValue(":deepThought", answer.doesDeepThoughtKnowCorrectness());
    250274
    251275    if (!query.exec()) {
     
    257281}
    258282
    259 void DADeepThoughtStorage::removeQuestion(Question& question) {
     283void DADeepThoughtStorage::removeQuestion(Question &question) {
    260284    QSqlQuery query = createQuery();
    261285
     
    303327    QSqlQuery query = createQuery();
    304328
    305     query.prepare("SELECT nrNodeGuId, eventTarget, language, questionText, status "
     329    query.prepare("SELECT nrNodeGuId, eventTarget, language, questionText "
    306330                    "FROM t_question "
    307331                    "WHERE (id = :id);");
     
    327351    question.setLanguage(query.value(2).toString());
    328352    question.setText(query.value(3).toString());
    329     question.setStatus(query.value(4).toUInt());
    330353
    331354    question.setAnswers(getAnswers(question));
     
    334357}
    335358
    336 QList<Answer> DADeepThoughtStorage::getAnswers(const Question& question) {
     359QList<Answer> DADeepThoughtStorage::getAnswers(const Question &question) {
    337360    QSqlQuery query = createQuery();
    338361
     
    357380    return answers;
    358381}
    359 Answer DADeepThoughtStorage::getAnswer(const Question& question, const QString& answerId) {
     382Answer DADeepThoughtStorage::getAnswer(const Question &question, const QString &answerId) {
    360383    QSqlQuery query = createQuery();
    361384
     
    392415}
    393416
    394 
    395417/**
    396  * Returns true, if the given question is already answered at schoolnet for the given account.
     418 * Returns the status of this question at the given schoolnet account.
    397419 * ATTENTION: This does not check the real state of the question at schoolnet. It justs reads
    398420 *            the state from the database. When the user answered some questions directly at
     
    401423 * @param account The schoolnet account
    402424 * @param question The question
    403  * @return True, If the question is answered
    404  */
    405 bool DADeepThoughtStorage::isQuestionAnsweredAtSchoolnet(BCAccount* account, const Question& question) const {
    406     QSqlQuery query = createQuery();
    407 
    408     query.prepare("SELECT *"
    409                     "FROM t_answered_at_schoolnet "
     425 * @return @see Question::Status
     426 */
     427uint DADeepThoughtStorage::questionStatusAtSchoolnet(BCAccount *account, const Question &question) const {
     428    QSqlQuery query = createQuery();
     429
     430    query.prepare("SELECT status "
     431                    "FROM t_status_at_schoolnet "
    410432                    "WHERE (account = :account) "
    411433                      "AND (questionId = :questionId);");
     
    414436
    415437    if (!query.exec()) {
    416         EException(QObject::tr("Could not get the status of a question at schoolnet from the database."))
    417             .addDebugInfo("sql-query", query.lastQuery())
    418             .addDebugInfo("sql-response", query.lastError().text())
    419             .raise();
    420     }
    421 
    422     return query.next();
     438        EException("Could not read the schoolnet-status of this question from the database.")
     439                .addDebugInfo("sql-query", query.lastQuery())
     440                .addDebugInfo("sql-response", query.lastError().text())
     441                .addDebugInfo("accountId", account->username())
     442                .addDebugInfo("questionId", question.id())
     443                .raise();
     444    }
     445
     446    if (!query.next()) {
     447        return 0;
     448    } else {
     449        return query.value(0).toInt();
     450    }
     451}
     452
     453void DADeepThoughtStorage::setQuestionStatusAtSchoolnet(BCAccount *account, const Question &question, uint status) const {
     454    QSqlQuery query = createQuery();
     455
     456    query.prepare("REPLACE INTO t_status_at_schoolnet (account, questionId, status) VALUES (:account, :questionId, :status);");
     457    query.bindValue(":account", account->username());
     458    query.bindValue(":questionId", question.id());
     459    query.bindValue(":status", status);
     460
     461    if (!query.exec()) {
     462        EException(QObject::tr("Failed to update/insert the question status in the database."))
     463                .addDebugInfo("sql-query", query.lastQuery())
     464                .addDebugInfo("sql-response", query.lastError().text())
     465                .addDebugInfo("accountId", account->username())
     466                .addDebugInfo("questionId", question.id())
     467                .raise();
     468    }
     469}
     470
     471/**
     472 * Returns true, if the given status of the given question has been checked for the given schoolnet account.
     473 * @see {@link #getQuestionStatusAtSchoolnet(BCAccount*, const Question&) const}
     474 *
     475 * @param account The schoolnet account
     476 * @param question The question
     477 * @return True, If the question is answered
     478 */
     479bool DADeepThoughtStorage::isQuestionStatusAtSchoolnetChecked(BCAccount *account, const Question &question) const {
     480    return questionStatusAtSchoolnet(account, question) & Question::StatusAtSchoolnetChecked;
     481}
     482
     483/**
     484 * Marks the given question status as checked from schoolnet.
     485 *
     486 * @param account The schoolnet account
     487 * @param question The question
     488 */
     489void DADeepThoughtStorage::setQuestionStatusAtSchoolnetChecked(BCAccount *account, const Question &question) const {
     490    uint newStatus = questionStatusAtSchoolnet(account, question) | Question::StatusAtSchoolnetChecked;
     491    setQuestionStatusAtSchoolnet(account, question, newStatus);
     492}
     493
     494
     495/**
     496 * Returns true, if the given question is already answered at schoolnet for the given account.
     497 * @see {@link #getQuestionStatusAtSchoolnet(BCAccount*, const Question&) const}
     498 *
     499 * @param account The schoolnet account
     500 * @param question The question
     501 * @return True, If the question is answered
     502 */
     503bool DADeepThoughtStorage::isQuestionAnsweredAtSchoolnet(BCAccount *account, const Question &question) const {
     504    return questionStatusAtSchoolnet(account, question) & Question::AnsweredAtSchoolnet;
    423505}
    424506
     
    429511 * @param question The question
    430512 */
    431 void DADeepThoughtStorage::setQuestionAnsweredAtSchoolnet(BCAccount* account, const Question& question) const {
    432     if (isQuestionAnsweredAtSchoolnet(account, question)) {
    433         // This combination is already inserted
    434         return;
    435     }
    436 
    437     QSqlQuery query = createQuery();
    438 
    439     query.prepare("INSERT INTO t_answered_at_schoolnet (account, questionId) "
    440                     "VALUES (:account, :questionId);");
    441     query.bindValue(":account", account->username());
    442     query.bindValue(":questionId", question.id());
    443 
    444     if (!query.exec()) {
    445         EException(QObject::tr("Failed to insert the question status to the database."))
    446              .addDebugInfo("sql-query", query.lastQuery())
    447              .addDebugInfo("sql-response", query.lastError().text())
    448              .raise();
    449     }
     513void DADeepThoughtStorage::setQuestionAnsweredAtSchoolnet(BCAccount *account, const Question &question) const {
     514    uint newStatus = questionStatusAtSchoolnet(account, question) | Question::AnsweredAtSchoolnet;
     515    setQuestionStatusAtSchoolnet(account, question, newStatus);
    450516}
    451517
  • gateways/Schoolnet/src/persistence/deepthought/dadeepthoughtstorage.h

    r217 r220  
    4646    QList<Question>  getQuestions();
    4747
    48     bool             isQuestionAnsweredAtSchoolnet(BCAccount* account, const Question& question) const;
    49     void             setQuestionAnsweredAtSchoolnet(BCAccount* account, const Question& question) const;
     48    uint             questionStatusAtSchoolnet(BCAccount *account, const Question &question) const;
     49    void             setQuestionStatusAtSchoolnet(BCAccount *account, const Question &question, uint status) const;
     50
     51    bool             isQuestionStatusAtSchoolnetChecked(BCAccount *account, const Question &question) const;
     52    void             setQuestionStatusAtSchoolnetChecked(BCAccount *account, const Question &question) const;
     53
     54    bool             isQuestionAnsweredAtSchoolnet(BCAccount *account, const Question &question) const;
     55    void             setQuestionAnsweredAtSchoolnet(BCAccount *account, const Question &question) const;
    5056
    5157public:
     
    5763private:
    5864    Question        getQuestion(int questionId);
    59     Answer          getAnswer(const Question& question, const QString& answerId);
    60     QList<Answer>   getAnswers(const Question& question);
     65    Answer          getAnswer(const Question &question, const QString &answerId);
     66    QList<Answer>   getAnswers(const Question &question);
    6167
    6268private:
     
    6470    QSqlQuery    createQuery() const;
    6571
    66     int          _getQuestionId(const Question& question);
    67     void         updateAnswer(const Answer& answer);
     72    int          _getQuestionId(const Question &question);
     73    void         updateAnswer(const Answer &answer);
    6874
    6975private:
     
    7177    void initDB();
    7278    void version1To2();
     79    void version2To3();
    7380
    7481private:
  • gateways/Schoolnet/src/ui/models/questiontreemodel.cpp

    r217 r220  
    1717 */
    1818#include "questiontreemodel.h"
    19 #include "questiontreemodel_p.h"
    2019
    2120#include <QDebug>
    2221#include <QIcon>
    23 
     22#include <QMovie>
     23
     24#include <iaccount.h>
     25#include <imanagers.h>
     26
     27#include "library.h"
     28#include "business/bcaccount.h"
     29#include "business/bcgateway.h"
     30#include "business/deepthought/deepthoughthelper.h"
    2431#include "business/deepthought/questionmanager.h"
    2532
     
    2936    : QAbstractItemModel(parent)
    3037{
     38    hasLoggedInAccounts_ = getLoggedInSchoolnetAccounts().size() > 0;
    3139    reloadList();
    32 }
    33 QuestionTreeModel::~QuestionTreeModel() {
    34 }
    35 
    36 /*void QuestionTreeModel::accountAdded(IAccount* account) {
    37     if (items_.contains(account)) {
     40
     41    connect(QuestionManager::instance(), SIGNAL(answerCorrectnessProvided(const Answer&)),
     42            this, SLOT(reloadList()));
     43    connect(Library::instance()->managerFactory()->accountManager(), SIGNAL(accountInitStateChanged(IAccount*)),
     44            this, SLOT(onAccountInitDataChanged(IAccount*)));
     45}
     46
     47void QuestionTreeModel::reloadList() {
     48    beginResetModel(); {
     49        items_.clear();
     50        foreach (Question question, QuestionManager::instance()->questions()) {
     51            if (isQuestionStatusAtSchoolnetCheckedAtSomeLoggedInAccount(question)) {
     52                items_.append(question);
     53            }
     54        }
     55    } endResetModel();
     56}
     57
     58void QuestionTreeModel::onAnswerProvidingTaskFinished(STask *task) {
     59    Q_ASSERT(loadingAnswers_.contains(task));
     60
     61    beginResetModel(); {
     62        loadingAnswers_.remove(task);
     63    } endResetModel();
     64}
     65void QuestionTreeModel::onAccountInitDataChanged(IAccount *account) {
     66    if (account->gateway() != BCGateway::instance()) {
    3867        return;
    3968    }
    4069
    41     int pos = items_.count();
    42     beginInsertRows(QModelIndex(), pos, pos);
    43     items_.append(account);
    44     endInsertRows();
    45 }
    46 
    47 void QuestionTreeModel::accountUpdated(IAccount* account) {
    48     int row = items_.indexOf(account);
    49 
    50     QModelIndex idx_start = index(row, 0);
    51     QModelIndex idx_end   = index(row, columnCount()-1);
    52 
    53     if (idx_start.isValid() && idx_end.isValid()) {
    54         emit dataChanged(idx_start, idx_end);
    55     }
    56 }
    57 
    58 void QuestionTreeModel::accountRemoved(IAccount* account) {
    59     if (!items_.contains(account)) {
    60         return;
    61     }
    62 
    63     int pos = items_.indexOf(account);
    64     beginRemoveRows(QModelIndex(), pos, pos);
    65     items_.removeAll(account);
    66     endRemoveRows();
    67 }*/
    68 
    69 void QuestionTreeModel::reloadList() {
    70     items_.clear();
    71     items_.append(QuestionManager::instance()->questions());
    72 
    73     reset();
     70    bool newHasLoggedInAccounts = getLoggedInSchoolnetAccounts().size() > 0;
     71    if (hasLoggedInAccounts_ != newHasLoggedInAccounts) {
     72        beginResetModel(); {
     73            hasLoggedInAccounts_ = newHasLoggedInAccounts;
     74        } endResetModel();
     75    }
     76
     77    if (hasLoggedInAccounts_) {
     78        reloadList(); // Data depends on the logged in accounts
     79    }
    7480}
    7581
     
    7884        return QVariant();
    7985
     86    TreeNode* node = dataObject(index);
     87    Q_ASSERT(node);
     88    if (node->isQuestion()) {
     89        // It's a question
     90        Question question = node->question();
     91
     92        switch (role) {
     93            case Qt::DisplayRole:
     94            case Qt::EditRole:
     95                switch (index.column()) {
     96                    case ColQuestionText:
     97                        return question.text();
     98                }
     99                break;
     100
     101            case Qt::DecorationRole:
     102                switch (index.column()) {
     103                    case ColStatus: {
     104                        switch (questionStatus(question)) {
     105                            case qsCanBeAnswered: return QIcon(":/images/question_open.png");
     106                            case qsCanNotBeAnsweredAnymore: return QIcon(":/images/question_open_disabled.png");
     107                            case qsAnsweredCorrect: return QIcon(":/images/question_correct_disabled.png");
     108                            case qsAnsweredWrong: return QIcon(":/images/question_wrong_disabled.png");
     109                            case qsLoading: return QIcon(":/images/question_loading.png");
     110                        }
     111                    }
     112                }
     113                break;
     114
     115            case Qt::ToolTipRole:
     116                switch (questionStatus(question)) {
     117                    case qsCanBeAnswered: return tr("Unanswered question");
     118                    case qsCanNotBeAnsweredAnymore: return tr("Can not be answered anymore (already answered at Schoolnet?)");
     119                    case qsAnsweredCorrect: return tr("Correctly answered question");
     120                    case qsAnsweredWrong: return tr("Incorrectly answered question");
     121                    case qsLoading: return tr("Processing answer proposal");
     122                }
     123                break;
     124
     125            case Qt::TextColorRole:
     126                if (questionStatus(question) != qsCanBeAnswered) {
     127                    return Qt::gray;
     128                }
     129                break;
     130        }
     131
     132    } else {
     133        // It's an answer
     134        Answer answer = node->answer();
     135
     136        switch (role) {
     137            case Qt::DisplayRole:
     138                switch (index.column()) {
     139                    case ColAnswerText:
     140                        return answer.text();
     141                }
     142                break;
     143
     144            case Qt::CheckStateRole:
     145                switch (index.column()) {
     146                    case ColAnswerText:
     147                        if (loadingAnswers_.values().contains(answer)) {
     148                            return Qt::PartiallyChecked;
     149                        } else {
     150                            return (answer.isCorrect()) ? Qt::Checked : Qt::Unchecked;
     151                        }
     152                }
     153                break;
     154
     155            case Qt::ToolTipRole:
     156                switch (answerStatus(answer)) {
     157                    case asCanBeAnswered: return tr("Unanswered");
     158                    case asLoading: return tr("Processing answer proposal");
     159                    case asAnsweredWrong: return tr("Incorrect answer");
     160                    case asAnsweredCorrect: return tr("Correct answer");
     161                    case asCanNotBeAnsweredAnymore: return tr("Question already answered");
     162                }
     163                break;
     164
     165            case Qt::TextColorRole:
     166                if (answerStatus(answer) != asCanBeAnswered) {
     167                    return Qt::gray;
     168                }
     169                break;
     170        }
     171    }
     172
     173    return QVariant();
     174}
     175
     176QuestionTreeModel::TreeNode* QuestionTreeModel::dataObject(const QModelIndex& index) const {
     177    if (!index.isValid() || (index.row() >= items_.size())) {
     178        return NULL;
     179    } else {
     180        return static_cast<TreeNode*>(index.internalPointer());
     181    }
     182}
     183
     184Qt::ItemFlags QuestionTreeModel::flags(const QModelIndex& index) const {
     185    if (!index.isValid())
     186        return Qt::NoItemFlags;
     187
     188    Qt::ItemFlags flags = QAbstractItemModel::flags(index);
     189
    80190    TreeNode* node = static_cast<TreeNode*>(index.internalPointer());
    81191    if (node->isQuestion()) {
    82192        // It's a question
    83         Question question = node->question();
    84 
    85         if (role == Qt::DisplayRole) {
    86             switch (index.column()) {
    87                 case ColQuestionText:
    88                     return question.text();
    89             }
    90         }
    91 
    92         if (role == Qt::DecorationRole) {
    93             switch (index.column()) {
    94                 case ColStatus: {
    95                     return QIcon(":/images/open.png");
    96                 }
    97             }
     193        //Question question = node->question();
     194        if (index.column() == ColQuestionText) {
     195            flags |= Qt::ItemIsEditable;
    98196        }
    99197    } else {
     
    101199        Answer answer = node->answer();
    102200
    103         if (role == Qt::DisplayRole) {
    104             switch (index.column()) {
    105                 case ColAnswerText:
    106                     return answer.text();
    107             }
    108         }
    109 
    110         if (role == Qt::CheckStateRole) {
    111             switch (index.column()) {
    112                 case ColAnswerText:
    113                     return (answer.isCorrect()) ? Qt::Checked : Qt::Unchecked;
    114             }
    115         }
    116     }
    117 
    118     return QVariant();
    119 }
    120 
    121 Qt::ItemFlags QuestionTreeModel::flags(const QModelIndex& index) const {
     201        switch (index.column()) {
     202            case ColAnswerText:
     203                if (answerStatus(answer) == asCanBeAnswered) {
     204                    flags |= Qt::ItemIsUserCheckable;
     205                }
     206                break;
     207        }
     208    }
     209
     210    return flags;
     211}
     212
     213bool QuestionTreeModel::setData(const QModelIndex& index, const QVariant& value, int role) {
    122214    if (!index.isValid())
    123         return Qt::NoItemFlags;
    124 
    125     Qt::ItemFlags flags = QAbstractItemModel::flags(index);
     215        return false;
    126216
    127217    TreeNode* node = static_cast<TreeNode*>(index.internalPointer());
    128218    if (node->isQuestion()) {
    129         // This It's a question
    130         Question question = node->question();
    131     } else {
    132         // This It's an answer
    133         Answer answer = node->answer();
    134 
    135         switch (index.column()) {
    136             case ColAnswerText:
    137                 flags |= Qt::ItemIsUserCheckable;
    138         }
    139     }
    140 
    141     return flags;
    142 }
    143 
    144 bool QuestionTreeModel::setData(const QModelIndex& index, const QVariant& value, int role /* = Qt::EditRole */) {
    145     if (!index.isValid())
    146         return false;
    147 
    148     TreeNode* node = static_cast<TreeNode*>(index.internalPointer());
    149     if (node->isQuestion()) {
    150         // This It's a question
    151         Question question = node->question();
    152     } else {
    153         // This It's an answer
     219        // It's a question
     220        //Question question = node->question();
     221    } else {
     222        // It's an answer
    154223        Answer answer = node->answer();
    155224
     
    157226            if (index.column() == ColAnswerText) {
    158227                const Qt::CheckState cs = static_cast<Qt::CheckState>(value.toInt());
    159                 Q_UNUSED(cs);
     228                if (cs == Qt::Checked) {
     229                    BCAccount* account = getAccountWhereUnanswered(answer.question());
     230                    if (account) {
     231                        beginResetModel(); {
     232                            STask* task = DeepThoughtHelper::instance()->proposeAnswer(account, answer);
     233                            loadingAnswers_.insert(task, answer);
     234                            // Reload the list after the task finished, so that the list gets reloaded even if no new
     235                            // knowledge about the answer correctness is gained (Already answered questions).
     236                            connect(task, SIGNAL(finished(STask*)), this, SLOT(onAnswerProvidingTaskFinished(STask*)));
     237                        } endResetModel();
     238                    }
     239                }
    160240
    161241                return true;
     
    210290
    211291int QuestionTreeModel::rowCount(const QModelIndex& parent) const {
     292    if (!hasLoggedInAccounts_) {
     293        return 0;
     294    }
     295
    212296    if (!parent.isValid()) {
    213297        return items_.size();
     
    223307}
    224308
     309QuestionTreeModel::QuestionStatus QuestionTreeModel::questionStatus(const Question& question) const {
     310    if (question.hasCorrectAnswer()) {
     311        return qsAnsweredCorrect;
     312    } else if (!isQuestionUnansweredAtSomeLoggedInAccount(question)) {
     313        foreach (Answer answer, question.answers()) {
     314            if (answer.isAnswered() && !answer.isCorrect()) {
     315                return qsAnsweredWrong;
     316            }
     317        }
     318        return qsCanNotBeAnsweredAnymore;
     319    } else {
     320        foreach (Answer answer, question.answers()) {
     321            if (loadingAnswers_.values().contains(answer)) {
     322                return qsLoading;
     323            }
     324        }
     325
     326        return qsCanBeAnswered;
     327    }
     328}
     329
     330QuestionTreeModel::AnswerStatus QuestionTreeModel::answerStatus(const Answer& answer) const {
     331    if (answer.isAnswered()) {
     332        return answer.isCorrect() ? asAnsweredCorrect : asAnsweredWrong;
     333    } else if (loadingAnswers_.values().contains(answer)) {
     334        return asLoading;
     335    } else if (!isQuestionUnansweredAtSomeLoggedInAccount(answer.question())) {
     336        return asCanNotBeAnsweredAnymore;
     337    } else if (questionStatus(answer.question()) == qsLoading) {
     338        return asCanNotBeAnsweredAnymore; // an other answer is loading
     339    } else {
     340        return asCanBeAnswered;
     341    }
     342}
     343
     344bool QuestionTreeModel::isQuestionStatusAtSchoolnetCheckedAtSomeLoggedInAccount(const Question &question) const {
     345    foreach (BCAccount* account, QuestionManager::instance()->getAccountsWhereQuestionStatusAtSchoolnetChecked(question)) {
     346        if (account->isLoggedIn()) {
     347            return true;
     348        }
     349    }
     350    return false;
     351}
     352
     353bool QuestionTreeModel::isQuestionUnansweredAtSomeLoggedInAccount(const Question &question) const {
     354    foreach (BCAccount* account, QuestionManager::instance()->getAccountsWhereQuestionUnanswered(question)) {
     355        if (account->isLoggedIn()) {
     356            return true;
     357        }
     358    }
     359    return false;
     360}
     361
     362QSet<BCAccount *> QuestionTreeModel::getLoggedInSchoolnetAccounts() const {
     363    QSet<BCAccount *> ret;
     364
     365    QSet<IAccount*> accounts = Library::instance()->managerFactory()->accountManager()->getAccountList();
     366    foreach (IAccount* account, accounts) {
     367        if (account->gateway() == BCGateway::instance()) {
     368            BCAccount *acc = static_cast<BCAccount*>(account);
     369            if (acc->isLoggedIn()) {
     370                ret.insert(acc);
     371            }
     372        }
     373    }
     374    return ret;
     375}
     376
     377/**
     378  * Returns an account where the question of the given question is still unanswered.
     379  */
     380BCAccount * QuestionTreeModel::getAccountWhereUnanswered(const Question &question) const {
     381    QSet<BCAccount *> accounts = getLoggedInSchoolnetAccounts();
     382    foreach (BCAccount *account, accounts) {
     383        if (!QuestionManager::instance()->isQuestionAnsweredAtSchoolnet(account, question)) {
     384            return account;
     385        }
     386    }
     387    return NULL;
     388}
    225389
    226390/***********************************************/
    227391
    228 TreeNode::TreeNode(const Question& question)
     392QuestionTreeModel::TreeNode::TreeNode(const Question& question)
    229393    : question_(question)
    230394    , answer_(NULL)
    231395{}
    232 TreeNode::TreeNode(const Answer& answer)
     396QuestionTreeModel::TreeNode::TreeNode(const Answer& answer)
    233397    : question_(NULL)
    234398    , answer_(answer)
    235399{}
    236 TreeNode::TreeNode(const TreeNode& other)
     400QuestionTreeModel::TreeNode::TreeNode(const TreeNode& other)
    237401    : question_(other.question_)
    238402    , answer_(other.answer_)
     
    240404
    241405
    242 Question TreeNode::question() const {
     406Question QuestionTreeModel::TreeNode::question() const {
    243407    Q_ASSERT(question_);
    244408    return question_;
    245409}
    246 Answer TreeNode::answer() const {
     410Answer QuestionTreeModel::TreeNode::answer() const {
    247411    Q_ASSERT(answer_);
    248412    return answer_;
    249413}
    250414
    251 bool TreeNode::isQuestion() const {
     415bool QuestionTreeModel::TreeNode::isQuestion() const {
    252416    return question_;
    253417}
    254 bool TreeNode::isAnswer() const {
     418bool QuestionTreeModel::TreeNode::isAnswer() const {
    255419    return answer_;
    256420}
  • gateways/Schoolnet/src/ui/models/questiontreemodel.h

    r194 r220  
    1919#define SCHOOLNET_QUESTIONTREEMODEL_H
    2020
    21 #include <QObject>
     21#include <QMap>
    2222#include <QAbstractItemModel>
    2323
    2424#include "business/deepthought/datatypes.h"
    2525
     26class IAccount;
     27class STask;
     28
    2629namespace Schoolnet {
     30
     31class BCAccount;
    2732
    2833class QuestionTreeModel: public QAbstractItemModel {
     
    3035
    3136public:
    32     explicit QuestionTreeModel(QObject* parent = 0);
    33     ~QuestionTreeModel();
     37    class TreeNode {
     38    public:
     39        TreeNode(const Question& question);
     40        TreeNode(const Answer& answer);
     41        TreeNode(const TreeNode& other);
     42
     43    public:
     44        Question question() const;
     45        Answer answer() const;
     46
     47        bool isQuestion() const;
     48        bool isAnswer() const;
     49    private:
     50        Question question_;
     51        Answer answer_;
     52    };
     53
     54public:
     55    explicit QuestionTreeModel(QObject *parent);
    3456
    3557    enum QuestionColumns {
     
    4769
    4870    QVariant      data(const QModelIndex& index, int role = Qt::DisplayRole) const;
     71    TreeNode *    dataObject(const QModelIndex& index) const;
    4972    int           columnCount(const QModelIndex& parent = QModelIndex()) const;
    5073    int           rowCount(const QModelIndex& parent = QModelIndex()) const;
     
    5477    QModelIndex   parent(const QModelIndex& index = QModelIndex()) const;
    5578
     79public:
     80    enum QuestionStatus {
     81        qsCanBeAnswered,          // No answer provided and question is open at some schoolnet account
     82        qsLoading,                // An answer has been provided by the user, but correctness check not "through"
     83        qsAnsweredWrong,          // This question has a wrong answer and is not open at any schoolnet account
     84        qsAnsweredCorrect,        // This question has a correct answer
     85        qsCanNotBeAnsweredAnymore // This question has no wrong answer but is not open at any schoolnet account
     86    };
     87    enum AnswerStatus {
     88        asCanBeAnswered,          // unanswered an question open at some logged-in account
     89        asLoading,                // this answer has been provided by the user, but correctness check not "through"
     90        asAnsweredWrong,          // this answer is wrong
     91        asAnsweredCorrect,        // this answer is correct
     92        asCanNotBeAnsweredAnymore // this question has already been answered (another answer) and is not open at another logged-in account -> cannot give any answer anymore
     93    };
    5694
    57 private:
    58     void          reloadList();
     95public:
     96    QuestionStatus questionStatus(const Question &question) const;
     97    AnswerStatus answerStatus(const Answer &answer) const;
     98
     99    bool isQuestionStatusAtSchoolnetCheckedAtSomeLoggedInAccount(const Question &question) const;
     100    bool isQuestionUnansweredAtSomeLoggedInAccount(const Question &question) const;
     101    QSet<BCAccount *> getLoggedInSchoolnetAccounts() const;
     102    BCAccount * getAccountWhereUnanswered(const Question &question) const;
     103
     104private slots:
     105    void reloadList();
     106
     107    void onAnswerProvidingTaskFinished(STask *task);
     108    void onAccountInitDataChanged(IAccount *account);
    59109
    60110private:
    61111    QList<Question> items_;
     112    bool hasLoggedInAccounts_;
     113    QMap<STask*, Answer> loadingAnswers_;
    62114};
    63115
  • gateways/Schoolnet/src/ui/vcquestionlist/vcquestionlist.cpp

    r194 r220  
    2020#include "ui_vcquestionlist.h"
    2121
     22#include <iaccount.h>
     23#include <imanagers.h>
     24
     25#include "library.h"
     26#include "business/bcgateway.h"
     27
     28#include "ui/models/questionsortmodel.h"
    2229#include "ui/models/questiontreemodel.h"
    2330
     
    2936    ui.setupUi(this);
    3037
    31     QuestionTreeModel* model = new QuestionTreeModel(this);
    32     ui.treeQuestions->setModel(model);
     38    questionTreeModel_ = new QuestionTreeModel(this);
     39    sortModel_ = new QuestionSortModel(questionTreeModel_, this);
     40    sortModel_->setSortCaseSensitivity(Qt::CaseInsensitive);
     41    sortModel_->setDynamicSortFilter(true);
     42    sortModel_->sort(QuestionTreeModel::ColQuestionText);
     43    ui.treeQuestions->setModel(sortModel_);
    3344    ui.treeQuestions->header()->setResizeMode(QuestionTreeModel::ColQuestionText, QHeaderView::Stretch);
     45
     46    connect(ui.btnsNavigation, SIGNAL(rejected()), this, SLOT(reject()));
     47    connect(Library::instance()->managerFactory()->accountManager(), SIGNAL(accountInitStateChanged(IAccount *)),
     48            this, SLOT(onAccountInitStateChanged(IAccount *)));
     49
     50    onAccountInitStateChanged(NULL);
     51}
     52
     53void VCQuestionList::on_treeQuestions_clicked(QModelIndex index) {
     54    QModelIndex srcIndex = sortModel_->mapToSource(index);
     55    if (!srcIndex.isValid()) {
     56        return;
     57    }
     58    if (questionTreeModel_->dataObject(srcIndex)->isAnswer()) {
     59        return;
     60    }
     61
     62    if (!ui.treeQuestions->isExpanded(index)) {
     63        ui.treeQuestions->collapseAll();
     64        ui.treeQuestions->expand(index);
     65    }
     66}
     67
     68void VCQuestionList::onAccountInitStateChanged(IAccount *account) {
     69    if (account) {
     70        if (account->gateway() != BCGateway::instance()) {
     71            return;
     72        }
     73    }
     74
     75    foreach (IAccount *account, Library::instance()->managerFactory()->accountManager()->getAccountList()) {
     76        if (account->gateway() == BCGateway::instance()) {
     77            if (account->isLoggedIn()) { // There is a logged-in account -> hide the "no logged-in accounts"-message
     78                ui.txtNoLoggedInAccounts->setVisible(false);
     79                return;
     80            }
     81        }
     82    }
     83
     84    ui.txtNoLoggedInAccounts->setVisible(true);
    3485}
    3586
  • gateways/Schoolnet/src/ui/vcquestionlist/vcquestionlist.h

    r194 r220  
    2424#include "ui_vcquestionlist.h"
    2525
     26class IAccount;
     27
    2628namespace Schoolnet {
     29
     30class QuestionSortModel;
     31class QuestionTreeModel;
    2732
    2833class VCQuestionList : public QDialog {
     
    3237    explicit VCQuestionList(QWidget *parent = 0);
    3338
     39private slots:
     40    void on_treeQuestions_clicked(QModelIndex index);
     41    void onAccountInitStateChanged(IAccount *account);
     42
    3443private:
    3544    Ui::VCQuestionListClass ui;
     45
     46    QuestionTreeModel *questionTreeModel_;
     47    QuestionSortModel *sortModel_;
    3648};
    3749
  • gateways/Schoolnet/src/ui/vcquestionlist/vcquestionlist.ui

    r193 r220  
    1919  <layout class="QVBoxLayout" name="verticalLayout">
    2020   <item>
     21    <widget class="QLabel" name="txtNoLoggedInAccounts">
     22     <property name="styleSheet">
     23      <string notr="true">font-weight: bold;
     24padding: 5px 10px;
     25background-color: #FFA;
     26border: 1px solid grey;</string>
     27     </property>
     28     <property name="text">
     29      <string>No schoolnet account is logged in.</string>
     30     </property>
     31     <property name="alignment">
     32      <set>Qt::AlignCenter</set>
     33     </property>
     34     <property name="wordWrap">
     35      <bool>true</bool>
     36     </property>
     37    </widget>
     38   </item>
     39   <item>
    2140    <widget class="QTreeView" name="treeQuestions">
    2241     <property name="iconSize">
     
    3251      <bool>false</bool>
    3352     </property>
    34      <property name="animated">
    35       <bool>true</bool>
    36      </property>
    3753     <property name="allColumnsShowFocus">
    3854      <bool>true</bool>
     
    4460      <bool>true</bool>
    4561     </property>
    46      <attribute name="headerDefaultSectionSize">
    47       <number>24</number>
    48      </attribute>
    49      <attribute name="headerMinimumSectionSize">
    50       <number>24</number>
    51      </attribute>
    52      <attribute name="headerStretchLastSection">
    53       <bool>false</bool>
    54      </attribute>
    5562     <attribute name="headerDefaultSectionSize">
    5663      <number>24</number>
  • gateways/Sunrise/src/business/bcaccount.cpp

    r218 r220  
    3131
    3232#include <exceptions/stdexceptions.h>
    33 #include <exceptions/eabortloadingexception.h>
    3433#include <exceptions/loginaccountexceptions.h>
    3534
  • gateways/Sunrise/src/business/bcaccount_task_sendsms.cpp

    r217 r220  
    2626#include <snetworkreply.h>
    2727#include <snumber.h>
    28 #include <exceptions/eabortloadingexception.h>
    2928
    3029#include "bcnetworkhelper.h"
     
    4544    qDebug() << "Start sending SMS";
    4645
    47     BCAccount::SendAs sendAs = account()->sendAs();
    48     if (sendAs == BCAccount::saAsk) {
    49         // Ask the user about the message format
    50 
    51         class SendTypeDialogWrapper : public IDialogWrapper {
    52         public:
    53             enum DialogCode {
    54                 SendAsSMS = 0x100,
    55                 SendAsMMS = 0x101
    56             };
    57         public:
    58             SendTypeDialogWrapper(QObject* parent)
    59                 : IDialogWrapper(parent)
    60             {}
    61         public:
    62             int createAndExecDialog() {
    63                 // Here we are in the gui-thread.
    64                 QMessageBox mBox;
    65                 mBox.setIcon(QMessageBox::Question);
    66                 mBox.setWindowTitle(QObject::tr("Select message format"));
    67                 mBox.setText(tr("How should the message be sent?"));
    68 
    69                 QPushButton* smsButton = mBox.addButton(tr("SMS"), QMessageBox::AcceptRole);
    70                 QPushButton* mmsButton = mBox.addButton(tr("MMS"), QMessageBox::AcceptRole);
    71                 mBox.setDefaultButton(mmsButton);
    72 
    73                 // Execute the dialog
    74                 int rCode = mBox.exec();
    75 
    76                 // Return the correct code
    77                 if (smsButton == mBox.clickedButton()) {
    78                     return SendAsSMS;
    79                 } else if (mmsButton == mBox.clickedButton()) {
    80                     return SendAsMMS;
    81                 } else {
    82                     return rCode;
     46    try {
     47        try {
     48            BCAccount::SendAs sendAs = account()->sendAs();
     49            if (sendAs == BCAccount::saAsk) {
     50                // Ask the user about the message format
     51
     52                class SendTypeDialogWrapper : public IDialogWrapper {
     53                public:
     54                    enum DialogCode {
     55                        SendAsSMS = 0x100,
     56                        SendAsMMS = 0x101
     57                    };
     58                public:
     59                    SendTypeDialogWrapper(QObject* parent)
     60                        : IDialogWrapper(parent)
     61                    {}
     62                public:
     63                    int createAndExecDialog() {
     64                        // Here we are in the gui-thread.
     65                        QMessageBox mBox;
     66                        mBox.setIcon(QMessageBox::Question);
     67                        mBox.setWindowTitle(QObject::tr("Select message format"));
     68                        mBox.setText(tr("How should the message be sent?"));
     69
     70                        QPushButton* smsButton = mBox.addButton(tr("SMS"), QMessageBox::AcceptRole);
     71                        QPushButton* mmsButton = mBox.addButton(tr("MMS"), QMessageBox::AcceptRole);
     72                        mBox.setDefaultButton(mmsButton);
     73
     74                        // Execute the dialog
     75                        int rCode = mBox.exec();
     76
     77                        // Return the correct code
     78                        if (smsButton == mBox.clickedButton()) {
     79                            return SendAsSMS;
     80                        } else if (mmsButton == mBox.clickedButton()) {
     81                            return SendAsMMS;
     82                        } else {
     83                            return rCode;
     84                        }
     85                    }
     86                };
     87
     88                IDialogWrapper *dw = new SendTypeDialogWrapper(this);
     89                int rCode = Library::instance()->managerFactory()->guiManager()->execDialog(dw);
     90                delete dw;
     91
     92                if (rCode == SendTypeDialogWrapper::SendAsSMS) {
     93                    sendAs = BCAccount::saSMS;
     94                } else if (rCode == SendTypeDialogWrapper::SendAsMMS) {
     95                    sendAs = BCAccount::saMMS;
     96                }
     97
     98                if (sendAs == BCAccount::saAsk) { // The dialog was cancelled
     99                    cancel();
     100                    return;
    83101                }
    84102            }
    85         };
    86 
    87         IDialogWrapper *dw = new SendTypeDialogWrapper(this);
    88         int rCode = Library::instance()->managerFactory()->guiManager()->execDialog(dw);
    89         delete dw;
    90 
    91         if (rCode == SendTypeDialogWrapper::SendAsSMS) {
    92             sendAs = BCAccount::saSMS;
    93         } else if (rCode == SendTypeDialogWrapper::SendAsMMS) {
    94             sendAs = BCAccount::saMMS;
     103
     104            lastHtml_ = http()->syncGet(URL_SMS)->readAll();
     105            account()->parseOnlineStats(lastHtml_);
     106
     107            QStringList longSMSList = account()->splitTextToLongSMS(message());
     108            int shortSMSCnt         = account()->shortSMSCount(message());
     109
     110            if (shortSMSCnt * recipients().size() > account()->freeSMSCount()) {
     111                EException(tr("You have only %1 SMS for free but you need at least %2 SMS left to send this message.")
     112                               .arg(account()->freeSMSCount()).arg(shortSMSCnt * recipients().size()))
     113                        .raise();
     114            }
     115            setProgress(10);
     116
     117            int percent_per_sms = 70 / (longSMSList.size() * ceil(recipients().size() / (double)MAX_RECIPIENTS_PER_SMS));
     118            QSetIterator<SContact> i(recipients());
     119            foreach (QString msgPart, longSMSList) {
     120                i.toFront();
     121                do {
     122                    removeRecipients();
     123
     124                    int recipientCount = 0;
     125                    while (i.hasNext() && (recipientCount < MAX_RECIPIENTS_PER_SMS)) {
     126                        addRecipient(i.next());
     127                        ++recipientCount;
     128                    }
     129
     130                    haltIfCancelRequested();
     131                    sendLongSMS(msgPart, sendAs);
     132
     133                    setProgress(progress() + percent_per_sms);
     134                } while(i.hasNext()); // While there are recipients, for which the sms has not been sent yet
     135            }
     136        } catch (EAbortException &ex) {
     137            // Processing of this is done later in this method
    95138        }
    96139
    97         if (sendAs == BCAccount::saAsk) { // The dialog was cancelled
    98             cancel();
    99             return;
    100         }
    101     }
    102 
    103     lastHtml_ = http()->syncGet(URL_SMS)->readAll();
    104     account()->parseOnlineStats(lastHtml_);
    105 
    106     QStringList longSMSList = account()->splitTextToLongSMS(message());
    107     int shortSMSCnt         = account()->shortSMSCount(message());
    108 
    109     if (shortSMSCnt * recipients().size() > account()->freeSMSCount()) {
    110         EException(tr("You have only %1 SMS for free but you need at least %2 SMS left to send this message.")
    111                        .arg(account()->freeSMSCount()).arg(shortSMSCnt * recipients().size()))
    112                 .raise();
    113     }
    114     setProgress(10);
    115 
    116     int percent_per_sms = 70 / (longSMSList.size() * ceil(recipients().size() / (double)MAX_RECIPIENTS_PER_SMS));
    117     QSetIterator<SContact> i(recipients());
    118     foreach (QString msgPart, longSMSList) {
    119         i.toFront();
    120         do {
    121             removeRecipients();
    122 
    123             int recipientCount = 0;
    124             while (i.hasNext() && (recipientCount < MAX_RECIPIENTS_PER_SMS)) {
    125                 addRecipient(i.next());
    126                 ++recipientCount;
    127             }
    128 
    129             sendLongSMS(msgPart, sendAs);
    130 
    131             setProgress(progress() + percent_per_sms);
    132         } while(i.hasNext()); // While there are recipients, for which the sms has not been sent yet
    133     }
    134 
    135     account()->reloadOnlineStats();
    136 
    137     setProgress(90);
     140        account()->reloadOnlineStats();
     141
     142        setProgress(90);
     143    } catch (EException& e) {
     144        qWarning() << "Error occured while sending sms.";
     145        setSpecialResult(Account::srError);
     146        throw;
     147    }
    138148
    139149    if (isCancelled()) {
  • gateways/SwisscomXtraZone/src/business/bcaccount.cpp

    r217 r220  
    2727
    2828#include <exceptions/stdexceptions.h>
    29 #include <exceptions/eabortloadingexception.h>
    3029#include <exceptions/loginaccountexceptions.h>
    3130
  • gateways/SwisscomXtraZone/src/business/bcaccount_task_sendsms.cpp

    r217 r220  
    2222#include <snetworkreply.h>
    2323#include <snumber.h>
    24 #include <exceptions/eabortloadingexception.h>
    2524
    2625namespace SwisscomXtraZone {
     
    5150
    5251    try {
     52        setProgress(0);
    5353        try {
    54             setProgress(0);
    55             try {
    56                 account()->reloadOnlineParams();
     54            account()->reloadOnlineParams();
    5755
    58                 QStringList longSMSList = account()->splitTextToLongSMS(message());
    59                 int shortSMSCnt         = account()->shortSMSCount(message());
     56            QStringList longSMSList = account()->splitTextToLongSMS(message());
     57            int shortSMSCnt         = account()->shortSMSCount(message());
    6058
    61                 if (shortSMSCnt * recipients().size() > account()->freeSMSCount()) {
    62                     EException(tr("You have only %1 SMS for free but you need at least %2 SMS left to send this message.")
    63                                    .arg(account()->freeSMSCount()).arg(shortSMSCnt * recipients().size()))
    64                             .raise();
    65                 }
    66                 setProgress(10);
     59            if (shortSMSCnt * recipients().size() > account()->freeSMSCount()) {
     60                EException(tr("You have only %1 SMS for free but you need at least %2 SMS left to send this message.")
     61                               .arg(account()->freeSMSCount()).arg(shortSMSCnt * recipients().size()))
     62                        .raise();
     63            }
     64            setProgress(10);
    6765
    68                 int percent_per_sms = 70 / (longSMSList.size() * ceil(recipients().size() / (double)MAX_RECIPIENTS_PER_SMS));
    69                 QSetIterator<SContact> i(recipients());
    70                 foreach (QString msgPart, longSMSList) {
    71                     i.toFront();
    72                     do {
    73                         removeRecipients();
     66            int percent_per_sms = 70 / (longSMSList.size() * ceil(recipients().size() / (double)MAX_RECIPIENTS_PER_SMS));
     67            QSetIterator<SContact> i(recipients());
     68            foreach (QString msgPart, longSMSList) {
     69                i.toFront();
     70                do {
     71                    removeRecipients();
    7472
    75                         int recipientCount = 0;
    76                         while (i.hasNext() && (recipientCount < MAX_RECIPIENTS_PER_SMS)) {
    77                             addRecipient(i.next());
    78                             ++recipientCount;
    79                         }
     73                    int recipientCount = 0;
     74                    while (i.hasNext() && (recipientCount < MAX_RECIPIENTS_PER_SMS)) {
     75                        addRecipient(i.next());
     76                        ++recipientCount;
     77                    }
    8078
    81                         sendLongSMS(msgPart);
     79                    haltIfCancelRequested();
     80                    sendLongSMS(msgPart);
    8281
    83                         setProgress(progress() + percent_per_sms);
    84                     } while(i.hasNext()); // While there are recipients, for which the sms has not been sent yet
    85                 }
    86 
    87                 setSpecialResult(Account::srSuccess);
    88             } catch (EAbortException& e) {
    89                 // Processing of this is done later in this method
     82                    setProgress(progress() + percent_per_sms);
     83                } while(i.hasNext()); // While there are recipients, for which the sms has not been sent yet
    9084            }
    9185
    92             account()->reloadOnlineParams();
    93 
    94             setProgress(90);
    95             //account()->logout();
    96         } catch (EException& e) {
    97             qWarning() << "Error occured while sending sms.";
    98             setSpecialResult(Account::srError);
    99             throw;
     86            setSpecialResult(Account::srSuccess);
     87        } catch (EAbortException& e) {
     88            // Processing of this is done later in this method
    10089        }
    10190
    102         if (isCancelled()) {
    103             uint oldSpecialResult = specialResult();
    104             setSpecialResult(Account::srCancelled);
     91        account()->reloadOnlineParams();
    10592
    106             if (oldSpecialResult == Account::srSuccess) {
    107                 EException(tr("The sending of the sms was requested to cancel, but (a part of) the sms has already been sent."))
    108                         .raise();
    109             }
     93        setProgress(90);
     94        //account()->logout();
     95    } catch (EException& e) {
     96        qWarning() << "Error occured while sending sms.";
     97        setSpecialResult(Account::srError);
     98        throw;
     99    }
     100
     101    if (isCancelled()) {
     102        uint oldSpecialResult = specialResult();
     103        setSpecialResult(Account::srCancelled);
     104
     105        if (oldSpecialResult == Account::srSuccess) {
     106            EException(tr("The sending of the sms was requested to cancel, but (a part of) the sms has already been sent."))
     107                    .raise();
    110108        }
    111     } catch (...) {
    112         throw;
    113109    }
    114110
  • gateways/gateways.pri

    r217 r220  
    2828}
    2929
    30 QMAKE_LFLAGS += -fvisibility=hidden
    31 
    3230LIBS += -L$$PWD/../lib/
    3331unix {
  • lib/datatypes/datatypes.pro

    r218 r220  
    1010CONFIG += thread \
    1111    exceptions \
    12     rtti
     12    rtti \
     13    shared
    1314DESTDIR = ../
    1415QMAKE_CLEAN += $$DESTDIR/lib$$TARGET*
     
    4748    src/abstract/persistence/versionedstorage.h \
    4849    src/exceptions/eexception.h \
    49     src/exceptions/eabortloadingexception.h \
    5050    src/exceptions/elibraryloadexception.h \
    5151    src/exceptions/eloginaccountloginexception.h \
     
    101101    src/abstract/persistence/versionedstorage.cpp \
    102102    src/exceptions/eexception.cpp \
    103     src/exceptions/eabortloadingexception.cpp \
    104103    src/exceptions/enetworkexception.cpp \
    105104    src/exceptions/persistence/estoragewriteexception.cpp \
  • lib/datatypes/src/abstract/abstractaccount.cpp

    r217 r220  
    222222    return checkStillLoggedIn();
    223223}
     224void AbstractAccount::ensureLoggedIn() {
     225    if (!checkStillLoggedIn()) {
     226        STask *loginTask = createLoginTask();
     227
     228        QEventLoop eventLoop;
     229        connect(loginTask, SIGNAL(finished(STask*)), &eventLoop, SLOT(quit()));
     230        loginTask->start();
     231        eventLoop.exec(QEventLoop::WaitForMoreEvents | QEventLoop::ExcludeUserInputEvents);
     232
     233        if (loginTask->hadException()) {
     234            EException("Could not ensure that the account is logged in.")
     235                    .chain(loginTask->exception())
     236                    .raise();
     237        }
     238    }
     239}
    224240
    225241IAccountLoginTask* AbstractAccount::createLoginTask() {
  • lib/datatypes/src/abstract/abstractaccount.h

    r218 r220  
    5858    IAccountLoginTask*         createLoginTask();
    5959    bool                       isLoggedIn() const;
     60    void                       ensureLoggedIn();
    6061
    6162public: /* IAccount - Send sms */
  • lib/datatypes/src/abstract/persistence/versionedstorage.cpp

    r217 r220  
    2727void VersionedStorage::init(const QSqlDatabase& db, uint newestVersion) {
    2828    QSqlQuery query = QSqlQuery(db);
    29     if (!query.exec("CREATE TABLE IF NOT EXISTS t_version_info ("
    30                       "version INTEGER NOT NULL "
    31                     ");"))
    32     {
    33         EException("Could not create the version information table in the database.")
    34                 .addDebugInfo("sql-error", query.lastError().text())
    35                 .raise();
    36     }
    37 
    38     if (!query.exec("SELECT version "
    39                       "FROM t_version_info "
    40                     "UNION SELECT 1 "
    41                     "ORDER BY version DESC "
    42                     "LIMIT 1;"))
     29    if (!query.exec("PRAGMA user_version;"))
    4330    {
    4431        EException("Could not get the version information of the database.")
     
    6754    }
    6855
    69     if (!query.next() || (query.value(0).toInt() <= 1)) {
    70         /* No "real" tables in the database (1 is for t_version_info)
    71          * -> set the storageVersion to uninitialized. */
     56    if (!query.next() || (query.value(0).toInt() <= 0)) {
     57        /* No tables in the database -> set the storageVersion to uninitialized. */
    7258        storageVersion = 0;
    7359    }
     60    query.clear(); // Freeup the ressources
    7461
    7562    if (storageVersion < newestVersion) {
    7663        doVersionUpdate(storageVersion);
    7764
    78         query.prepare("REPLACE INTO t_version_info VALUES(?);");
    79         query.addBindValue(newestVersion);
    80         if (!query.exec()) {
     65        if (!query.exec(QString("PRAGMA user_version=%1;").arg(newestVersion))) {
    8166            EException("Could not update the version information of the database.")
    8267                    .addDebugInfo("sql-error", query.lastError().text())
  • lib/datatypes/src/exceptions/eexception.cpp

    r194 r220  
    2121
    2222EException::EException()
    23     : std::runtime_error("")
     23    : chainedException_(NULL)
    2424{
    2525}
    2626EException::EException(const QString& msg)
    27     : std::runtime_error(msg.toAscii().constData())
    28     , chainedException_(NULL)
     27    : chainedException_(NULL)
    2928{
    3029    setWhat(msg);
    3130}
    3231EException::EException(const EException& other)
    33     : std::runtime_error(other)
    34     , message_(other.message_)
    35     , chainedException_(NULL)
     32    : message_(other.message_)
     33    , chainedException_(other.chainedException_)
    3634    , debugInfos_(other.debugInfos_)
    3735{
    3836}
     37EException & EException::operator =(const EException &other) {
     38    message_ = other.message_;
     39    chainedException_ = other.chainedException_;
     40    debugInfos_ = other.debugInfos_;
     41
     42    return *this;
     43}
    3944EException::~EException() throw() {
    4045}
     46
    4147void EException::raise() {
    4248    throw *this;
  • lib/datatypes/src/exceptions/eexception.h

    r218 r220  
    2626#include <QStringList>
    2727
    28 #include <stdexcept>
    29 
    3028#include "datatypes_global.h"
    3129
    32 #ifndef Q_WS_WIN
    33     #define SMAKE_VISIBLE __attribute__ ((visibility("default")))
    34 #else
    35     #define SMAKE_VISIBLE
    36 #endif
    37 
    38 class EException;
    39 class DATATYPES_SHARED_EXPORT SMAKE_VISIBLE EException : public std::runtime_error {
     30class DATATYPES_SHARED_EXPORT EException /*: public std::runtime_error */ {
    4031public:
    4132    EException();
     
    6556    virtual EException* createClonedInstance() const throw();
    6657
     58public:
     59    EException & operator =(const EException &other);
     60
    6761private:
    6862    QString                    message_;
  • lib/datatypes/src/exceptions/elibraryloadexception.h

    r218 r220  
    2525namespace Library {
    2626
    27 class DATATYPES_SHARED_EXPORT SMAKE_VISIBLE ELoadException: public EException {
     27class DATATYPES_SHARED_EXPORT ELoadException : public EException {
    2828public:
    2929    enum Purpose {
  • lib/datatypes/src/exceptions/eloginaccountloginexception.h

    r218 r220  
    2525namespace LoginAccount {
    2626
    27 class DATATYPES_SHARED_EXPORT SMAKE_VISIBLE ELoginException: public EException {
     27class DATATYPES_SHARED_EXPORT ELoginException : public EException {
    2828public:
    2929    enum Purpose {
  • lib/datatypes/src/exceptions/enetworkexception.h

    r218 r220  
    2525#include <QNetworkReply>
    2626
    27 class DATATYPES_SHARED_EXPORT SMAKE_VISIBLE ENetworkException : public EException {
     27class DATATYPES_SHARED_EXPORT ENetworkException : public EException {
    2828public:
    2929    ENetworkException(QNetworkReply::NetworkError networkError, const QString& message);
  • lib/datatypes/src/exceptions/eparseexception.h

    r218 r220  
    2121#include "eexception.h"
    2222
    23 #include <QMetaType>
    24 
    25 class DATATYPES_SHARED_EXPORT SMAKE_VISIBLE EParseException: public EException {
     23class DATATYPES_SHARED_EXPORT EParseException : public EException {
    2624public:
    2725    explicit EParseException(const QString& msg);
  • lib/datatypes/src/exceptions/httpexceptions.h

    r218 r220  
    2525
    2626namespace Http {
    27     class DATATYPES_SHARED_EXPORT SMAKE_VISIBLE EReadException: public EException {
     27    class DATATYPES_SHARED_EXPORT EReadException : public EException {
    2828    public:
    2929        explicit EReadException(const QString& msg);
     
    3636    };
    3737
    38     class DATATYPES_SHARED_EXPORT SMAKE_VISIBLE ESSLException: public EException {
     38    class DATATYPES_SHARED_EXPORT ESSLException : public EException {
    3939    public:
    4040        explicit ESSLException(const QList<QSslError>& errors);
  • lib/datatypes/src/exceptions/persistence/estoragereadexception.h

    r218 r220  
    2424
    2525namespace Storage {
    26     class DATATYPES_SHARED_EXPORT SMAKE_VISIBLE EReadException: public EException {
     26    class DATATYPES_SHARED_EXPORT EReadException : public EException {
    2727    public:
    2828        explicit EReadException(const QString& msg);
  • lib/datatypes/src/exceptions/persistence/estoragewriteexception.h

    r218 r220  
    2424
    2525namespace Storage {
    26     class DATATYPES_SHARED_EXPORT SMAKE_VISIBLE EWriteException: public EException {
     26    class DATATYPES_SHARED_EXPORT EWriteException : public EException {
    2727    public:
    2828        explicit EWriteException(const QString& msg);
  • lib/datatypes/src/interfaces/iaccount.h

    r218 r220  
    8686    virtual IAccountLoginTask* createLoginTask() =0;
    8787    virtual bool               isLoggedIn() const =0;
     88    virtual void               ensureLoggedIn() =0;
    8889
    8990public:
  • lib/datatypes/src/interfaces/persistence/iaccountlistmanager.h

    r218 r220  
    2525
    2626#include "datatypes_global.h"
     27#include "eexception.h"
    2728
    2829class IAccount;
    2930class AbstractAccount;
    3031class SDummyAccount;
    31 class EException;
    3232
    3333class DATATYPES_SHARED_EXPORT IAccountListManager {
  • lib/datatypes/src/interfaces/persistence/iaccountstoragemanager.h

    r218 r220  
    2525
    2626#include "datatypes_global.h"
     27#include "eexception.h"
    2728
    2829class IAccount;
    2930class SDummyAccount;
    30 class EException;
    3131
    3232class DATATYPES_SHARED_EXPORT IAccountStorageManager {
  • lib/datatypes/src/managers/staskmanager.cpp

    r218 r220  
    2020#include <QCoreApplication>
    2121#include <QMutexLocker>
     22#include <QTimer>
    2223
    2324S_SINGLETON_IMPL(STaskManager)
     
    2627    : QObject(NULL) // singleton
    2728    , tasksMutex_(QMutex::Recursive)
     29    , tornDown_(false)
    2830{
    2931    moveToThread(qApp->thread());
     
    4749
    4850/**
     51 * Removes all waiting tasks, cancels all running tasks and waits until they all are finished.
     52 * No new tasks are accepted after a call to this method.
     53 */
     54void STaskManager::tearDown() {
     55    QMutexLocker locker(&tasksMutex_);
     56
     57    tornDown_ = true;
     58
     59    // Remove waiting tasks
     60    while (!taskQueue_.isEmpty()) {
     61        taskQueue_.takeFirst()->deleteLater();
     62    }
     63
     64    // Cancel running tasks
     65    foreach (STask* task, runningTasks_) {
     66        task->cancel();
     67    }
     68
     69    // Wait until all tasks are finished
     70    locker.unlock();
     71    int runs = 0;
     72    while (!runningTasks_.isEmpty() && (runs < 10)) {
     73        QEventLoop eventLoop;
     74        connect(this, SIGNAL(taskFinished(STask *)), &eventLoop, SLOT(quit()));
     75        QTimer::singleShot(1000, &eventLoop, SLOT(quit())); // To not wait forever
     76        eventLoop.exec(QEventLoop::WaitForMoreEvents | QEventLoop::ExcludeUserInputEvents);
     77
     78        ++runs;
     79    }
     80}
     81
     82/**
    4983 * Takes ownership of the task
    5084 */
    5185void STaskManager::addTaskToQueue(STask* task) {
    5286    if (!task) { // Don't accept null tasks
     87        return;
     88    }
     89
     90    if (tornDown_) { // After tear-down no new tasks are accepted anymore
    5391        return;
    5492    }
  • lib/datatypes/src/managers/staskmanager.h

    r218 r220  
    4545    QList<STask*> runningTasksOfThisType(uint uid, const QString& nmespace) const;
    4646
     47public:
     48    void tearDown();
     49
    4750signals:
    4851    void taskEnqueued(STask* task);
     
    6164    QList<STask*>  taskQueue_;
    6265    QList<STask*>  runningTasks_;
     66
     67    bool           tornDown_;
    6368};
    6469
  • lib/datatypes/src/snetworkhelper.cpp

    r219 r220  
    229229}
    230230
    231 
    232231void SNetworkHelper::waitFor(const SNetworkReply& networkReply) {
    233232    QEventLoop eventLoop;
     
    238237    if (networkReply->error() != QNetworkReply::NoError) {
    239238        ENetworkException(networkReply->error(), networkReply->errorString())
    240             .addDebugInfo("url", networkReply->request().url().toString())
    241             .raise();
     239                .addDebugInfo("url", networkReply->request().url().toString())
     240                .raise();
    242241    }
    243242}
  • lib/datatypes/src/stask.cpp

    r218 r220  
    2727#include "smacros.h"
    2828#include "managers/staskmanager.h"
    29 #include "exceptions/eabortloadingexception.h"
    30 
    3129
    3230STask::STask(uint UID, const QString& nmespace)
     
    4442    , cancelled_(false)
    4543    , finished_(false)
     44    , autoHaltOnCancelRequested_(false)
    4645    , exception_(NULL)
    4746{
     
    216215void STask::safeDoTheWork() {
    217216    sDebug() << "We are starting!";
     217
     218    autoHaltOnCancelRequested_ = true;
    218219    try {
    219220        doTheWork();
    220     } catch (EException* e) {
     221    } catch (const EAbortException &e) {
     222        // Just ignore it
     223    } catch (const EException *e) {
    221224        setException(*e); // Do not create a cloned instance, since this instance is probably not owned by anyone
    222225        qWarning() << exception().chainedWhat();
    223     } catch (EException& e) {
     226    } catch (const EException &e) {
    224227        setException(e);
    225228        qWarning() << exception().chainedWhat();
    226     } catch (std::runtime_error* e) {
     229   } catch (...) {
    227230        /* If an exception lands here or in any catch below you either did not throw an instance of
    228231         * EException or
    229232         *   o) you have forgotten to add the "-shared-libgcc" compiler flag
    230          *   o) the exception (or one of its parents) has Implementation data in its header file
    231          *   o) you have forgotten to add SMAKE_VISIBLE in the class definition of the exception
    232          * (These points hold only, if the Implementation of the task lies in a dynamically
    233          * loaded library.)
     233         *   o) the exception (or one of its parents) has implementation data in its header file
     234         *   o) you have forgotten to add DATATYPES_SHARED_EXPORT in the class definition of the exception
     235         * (These points hold only, if the implementation of the task lies in a shared library)
    234236         */
    235         EException* ex = static_cast<EException*>(e);
    236         setException(*ex); // Do not create a cloned instance, since this instance is probably not owned by anyone
    237         qWarning() << exception().chainedWhat();
    238     } catch (std::runtime_error& e) {
    239         const EException& ex = static_cast<const EException&>(e);
    240         setException(ex);
    241         qWarning() << exception().chainedWhat();
    242     } catch (...) {
    243237        setException(EException(tr("Unknown exception occured during executing the task.")));
    244238        qWarning() << exception().chainedWhat();
    245239    }
     240    autoHaltOnCancelRequested_ = false;
    246241
    247242    // Wait until all subtasks are finished
     
    479474    QMutexLocker locker(&dataMutex());
    480475
     476    if (autoHaltOnCancelRequested_)  haltIfCancelRequested();
     477
    481478    SET_IF_DIFFERENT(progress_, progress);
    482479    emit changed(this, STask::pProgress);
     
    484481void STask::setStatus(uint status) {
    485482    QMutexLocker locker(&dataMutex());
     483
     484    if (autoHaltOnCancelRequested_)  haltIfCancelRequested();
    486485
    487486    SET_IF_DIFFERENT(status_, status);
     
    741740    return originalException()->toString();
    742741}
     742
     743/*********************************************************/
     744
     745EAbortException::EAbortException()
     746    : EException("")
     747{
     748}
     749
     750void EAbortException::raise() {
     751    throw *this;
     752}
     753
     754EAbortException* EAbortException::createClonedInstance() const throw() {
     755    return new EAbortException(*this);
     756}
  • lib/datatypes/src/stask.h

    r218 r220  
    183183    uint           specialResult_;
    184184
    185     STask*          parent_;
    186 
    187     QList<STask*>   waitingFor_;
     185    STask*         parent_;
     186
     187    QList<STask*>  waitingFor_;
    188188    mutable QMutex waitForMutex_;
    189     QWaitCondition  waitForWaitCondition_;
    190 
    191     QList<STask*>   subTasks_;
    192 
    193     bool    started_;
    194     bool    cancelled_;
    195     bool    finished_;
     189    QWaitCondition waitForWaitCondition_;
     190
     191    QList<STask*>  subTasks_;
     192
     193    bool started_;
     194    bool cancelled_;
     195    bool finished_;
     196    bool autoHaltOnCancelRequested_;
    196197
    197198    QSharedPointer<EException> exception_;
     
    230231};
    231232
     233/**
     234 * Exception which gets thrown when calling SThread::haltIfCancelRequested().
     235 */
     236class DATATYPES_SHARED_EXPORT EAbortException : public EException {
     237public:
     238    EAbortException();
     239
     240public: /* EException */
     241    virtual void raise();
     242    EAbortException* createClonedInstance() const throw();
     243};
     244
     245
    232246#endif /* STASK_H_ */
  • lib/datatypes/src/stask_p.h

    r217 r220  
    6060};
    6161
    62 class SMAKE_VISIBLE ESubtaskException: public EException {
     62class ESubtaskException : public EException {
    6363public:
    6464    explicit ESubtaskException(const EException& exception);
  • smssender-app.pro

    r217 r220  
    1313    sql \
    1414    xml
     15
    1516LIBS += -Llib/
    1617LIBS += -lcrypto++ \
    1718    -ldatatypes
     19
    1820mac {
    1921    TARGET = SMSSender
     
    2224    INCLUDEPATH += /Library
    2325    LIBS += -L/Library/crypto++
     26    QMAKE_INFO_PLIST = Info.plist
    2427}
    2528unix {
  • smssender.pri

    r217 r220  
    1616QMAKE_CXXFLAGS += -shared-libgcc
    1717QMAKE_CXXFLAGS_DEBUG += -gdwarf-2 -g3
     18QMAKE_LFLAGS += -fvisibility=hidden
  • src/bootstrap.cpp

    r217 r220  
    2525
    2626#include <ilibrary.h>
     27#include <managers/staskmanager.h>
    2728
    2829#include "business/bcaccountmanager.h"
     
    3839}
    3940Bootstrap::~Bootstrap() {
     41    STaskManager::instance()->tearDown(); // Stop all tasks
    4042}
    4143
  • src/ui/components/taskstatusbar.cpp

    r217 r220  
    1919#include "taskstatusbar.h"
    2020
     21#include <algorithm>
    2122#include <QHBoxLayout>
    2223
     
    8384    } else {
    8485        QString lastTitle = "";
    85         int progress = 0;
     86        uint minProgress = 100;
     87        uint meanProgress = 0;
    8688        bool allTitlesTheSame = true;
    8789        foreach (STask* task, tasks) {
    88             progress += task->progress();
     90            minProgress = std::min(task->progress(), minProgress);
     91            meanProgress += task->progress();
    8992
    9093            if ((lastTitle != "") && (task->title() != lastTitle)) {
     
    9396            lastTitle = task->title();
    9497        }
    95         progress = progress / tasks.count();
     98        meanProgress = meanProgress / tasks.count();
    9699
    97100        if (allTitlesTheSame && !lastTitle.isEmpty()) {
    98             setMessage(tr("%1... (%2%)").arg(tasks.first()->title()).arg(progress));
     101            setMessage(tr("%1... (%2%)").arg(tasks.first()->title()).arg(meanProgress));
    99102            setProgressMode((tasks.count() > 1) ? TaskStatusBar::MultipleProgress : TaskStatusBar::SingleProgress);
    100103        } else {
    101             setMessage(tr("%1... (%2%)").arg(tr("Working")).arg(progress));
     104            setMessage(tr("%1... (%2%)").arg(tr("Working")).arg(minProgress));
    102105            setProgressMode(TaskStatusBar::MultipleProgress);
    103106        }
  • src/ui/vcmain/vcmain.cpp

    r217 r220  
    6666        accountFilterModel_->setSortCaseSensitivity(Qt::CaseInsensitive);
    6767        accountFilterModel_->setDynamicSortFilter(true);
    68         accountFilterModel_->sort(0);
     68    accountFilterModel_->sort(AccountTreeModel::ColName);
    6969        ui.lstAccounts->setModel(accountFilterModel_);
    7070        ui.lstAccounts->setModelColumn(AccountTreeModel::ColNameWFreeCount);
Note: See TracChangeset for help on using the changeset viewer.