fixed account parsing

master
Boernsman 2021-02-19 11:24:20 +01:00
parent ac17e1a8de
commit fe9b604c04
5 changed files with 251 additions and 103 deletions

View File

@ -109,17 +109,17 @@ void IntegrationPluginTempo::discoverThings(ThingDiscoveryInfo *info)
if (info->thingClassId() == accountThingClassId) {
Q_FOREACH(Tempo *tempo, m_tempoConnections) {
tempo->getAccounts();
Thing *parentThing = m_tempoConnections.key(tempo);
if (!parentThing) {
ThingId parentThingId = m_tempoConnections.key(tempo);
if (parentThingId.isNull()) {
qCWarning(dcTempo()) << "Parent not found";
return;
}
connect(tempo, &Tempo::accountsReceived, info, [info, parentThing, this, tempo] (const QList<Tempo::Account> &accounts) {
connect(tempo, &Tempo::accountsReceived, info, [info, parentThingId] (const QList<Tempo::Account> &accounts) {
Q_FOREACH(Tempo::Account account, accounts) {
if (account.status == Tempo::Status::Archived)
continue;
ThingDescriptor descriptor(accountThingClassId, account.name, account.customer.name, parentThing->id());
ThingDescriptor descriptor(accountThingClassId, account.name, account.customer.name, parentThingId);
ParamList params;
params << Param(accountThingKeyParamTypeId, account.key);
descriptor.setParams(params);
@ -130,15 +130,15 @@ void IntegrationPluginTempo::discoverThings(ThingDiscoveryInfo *info)
} else if (info->thingClassId() == teamThingClassId) {
Q_FOREACH(Tempo *tempo, m_tempoConnections) {
tempo->getTeams();
Thing *parentThing = m_tempoConnections.key(tempo);
if (!parentThing) {
ThingId parentThingId = m_tempoConnections.key(tempo);
if (parentThingId.isNull()) {
qCWarning(dcTempo()) << "Parent not found";
return;
}
connect(tempo, &Tempo::teamsReceived, info, [info, parentThing, this, tempo] (const QList<Tempo::Team> &teams) {
connect(tempo, &Tempo::teamsReceived, info, [info, parentThingId] (const QList<Tempo::Team> &teams) {
Q_FOREACH(Tempo::Team team, teams) {
ThingDescriptor descriptor(teamThingClassId, team.name, team.summary, parentThing->id());
ThingDescriptor descriptor(teamThingClassId, team.name, team.summary, parentThingId);
ParamList params;
params << Param(teamThingIdParamTypeId, team.id);
descriptor.setParams(params);
@ -160,9 +160,9 @@ void IntegrationPluginTempo::setupThing(ThingSetupInfo *info)
if (thing->thingClassId() == tempoConnectionThingClassId) {
Tempo *tempo;
if (m_tempoConnections.contains(thing)) {
if (m_tempoConnections.contains(thing->id())) {
qCDebug(dcTempo()) << "Setup after reconfiguration, cleaning up";
m_tempoConnections.take(thing)->deleteLater();
m_tempoConnections.take(thing->id())->deleteLater();
}
if (m_setupTempoConnections.keys().contains(thing->id())) {
// This thing setup is after a pairing process
@ -171,7 +171,7 @@ void IntegrationPluginTempo::setupThing(ThingSetupInfo *info)
if (!tempo) {
qCWarning(dcTempo()) << "Tempo connection object not found for thing" << thing->name();
}
m_tempoConnections.insert(thing, tempo);
m_tempoConnections.insert(thing->id(), tempo);
info->finish(Thing::ThingErrorNoError);
} else {
//device loaded from the device database, needs a new access token;
@ -187,15 +187,18 @@ void IntegrationPluginTempo::setupThing(ThingSetupInfo *info)
connect(info, &ThingSetupInfo::aborted, tempo, &Tempo::deleteLater);
connect(tempo, &Tempo::authenticationStatusChanged, info, [info, tempo, this] (bool authenticated){
if (authenticated) {
m_tempoConnections.insert(info->thing(), tempo);
m_tempoConnections.insert(info->thing()->id(), tempo);
connect(tempo, &Tempo::connectionChanged, this, &IntegrationPluginTempo::onConnectionChanged);
connect(tempo, &Tempo::authenticationStatusChanged, this, &IntegrationPluginTempo::onAuthenticationStatusChanged);
connect(tempo, &Tempo::accountsReceived, this, &IntegrationPluginTempo::onAccountsReceived);
connect(tempo, &Tempo::teamsReceived, this, &IntegrationPluginTempo::onTeamsReceived);
connect(tempo, &Tempo::accountWorklogsReceived, this, &IntegrationPluginTempo::onAccountWorkloadReceived);
connect(tempo, &Tempo::teamWorklogsReceived, this, &IntegrationPluginTempo::onTeamWorkloadReceived);
info->finish(Thing::ThingErrorNoError);
}
});
tempo->getAccounts();
}
connect(tempo, &Tempo::connectionChanged, this, &IntegrationPluginTempo::onConnectionChanged);
connect(tempo, &Tempo::authenticationStatusChanged, this, &IntegrationPluginTempo::onAuthenticationStatusChanged);
connect(tempo, &Tempo::accountsReceived, this, &IntegrationPluginTempo::onReceivedAccounts);
} else if (thing->thingClassId() == accountThingClassId ||
thing->thingClassId() == teamThingClassId){
@ -223,53 +226,68 @@ void IntegrationPluginTempo::postSetupThing(Thing *thing)
connect(m_pluginTimer15min, &PluginTimer::timeout, this, [this]() {
qCDebug(dcTempo()) << "Refresh timer timout, polling all Tempo accounts.";
Q_FOREACH (Thing *thing, myThings().filterByThingClassId(tempoConnectionThingClassId)) {
Tempo *tempo = m_tempoConnections.value(thing);
Tempo *tempo = m_tempoConnections.value(thing->id());
if (!tempo) {
qWarning(dcTempo()) << "No Tempo connection found for" << thing->name();
continue;
}
tempo->getAccounts();
tempo->getTeams();
Q_FOREACH (Thing *childThing, myThings().filterByParentId(thing->id())) {
QString key = childThing->paramValue(accountThingKeyParamTypeId).toString();
QDate from(1970, 1, 1);
tempo->getWorkloadByAccount(key, from, QDate::currentDate());
if (childThing->thingClassId() == accountThingClassId) {
QString key = childThing->paramValue(accountThingKeyParamTypeId).toString();
QDate from(1970, 1, 1);
tempo->getWorkloadByAccount(key, from, QDate::currentDate());
} else if (childThing->thingClassId() == teamThingClassId) {
int id = childThing->paramValue(teamThingIdParamTypeId).toInt();
QDate from(1970, 1, 1);
tempo->getWorkloadByTeam(id, from, QDate::currentDate());
}
}
}
});
}
if (thing->thingClassId() == tempoConnectionThingClassId) {
Tempo *tempo = m_tempoConnections.value(thing);
Tempo *tempo = m_tempoConnections.value(thing->id());
if (!tempo) {
qCWarning(dcTempo()) << "Tempo connection not found for" << thing->name();
return;
}
tempo->getAccounts();
} else if (thing->thingClassId() == accountThingClassId) {
Tempo *tempo = m_tempoConnections.value(thing->parentId());
QString key = thing->paramValue(accountThingKeyParamTypeId).toString();
QDate from(1970, 1, 1);
tempo->getWorkloadByAccount(key, from, QDate::currentDate());
tempo->getAccounts();
} else if (thing->thingClassId() == teamThingClassId) {
}
}
void IntegrationPluginTempo::executeAction(ThingActionInfo *info)
{
Thing *thing = info->thing();
Action action = info->action();
if (thing->thingClassId() == tempoConnectionThingClassId) {
} else if (thing->thingClassId() == accountThingClassId) {
Tempo *tempo = m_tempoConnections.value(thing->parentId());
int id = thing->paramValue(teamThingIdParamTypeId).toInt();
QDate from(1970, 1, 1);
tempo->getWorkloadByTeam(id, from, QDate::currentDate());
tempo->getTeams();
}
}
void IntegrationPluginTempo::thingRemoved(Thing *thing)
{
qCDebug(dcTempo()) << "Thing removed" << thing->name();
if (thing->thingClassId() == tempoConnectionThingClassId) {
m_tempoConnections.take(thing->id())->deleteLater();
}
if (myThings().isEmpty()) {
qCDebug(dcTempo()) << "Stopping plugin timer";
hardwareManager()->pluginTimerManager()->unregisterTimer(m_pluginTimer15min);
m_pluginTimer15min = nullptr;
}
}
void IntegrationPluginTempo::onConnectionChanged(bool connected)
{
Tempo *tempo = static_cast<Tempo *>(sender());
Thing *thing = m_tempoConnections.key(tempo);
Thing *thing = myThings().findById(m_tempoConnections.key(tempo));
if (!thing)
return;
thing->setStateValue(tempoConnectionConnectedStateTypeId, connected);
@ -284,31 +302,107 @@ void IntegrationPluginTempo::onAuthenticationStatusChanged(bool authenticated)
{
qCDebug(dcTempo()) << "Authentication changed" << authenticated;
Tempo *tempoConnection = static_cast<Tempo *>(sender());
Thing *thing = m_tempoConnections.key(tempoConnection);
Tempo *tempo = static_cast<Tempo *>(sender());
Thing *thing = myThings().findById(m_tempoConnections.key(tempo));
if (!thing)
return;
thing->setStateValue(tempoConnectionLoggedInStateTypeId, authenticated);
}
void IntegrationPluginTempo::onReceivedAccounts(const QList<Tempo::Account> &accounts)
void IntegrationPluginTempo::onAccountsReceived(const QList<Tempo::Account> accounts)
{
qCDebug(dcTempo()) << "Received" << accounts.count() << "accounts";
qCDebug(dcTempo()) << "Accounts received";
Q_FOREACH(Tempo::Account account, accounts) {
qCDebug(dcTempo()) << " - Account" << account.name;
qCDebug(dcTempo()) << " - Key" << account.key;
qCDebug(dcTempo()) << " - Monthly budget" << account.monthlyBudget;
qCDebug(dcTempo()) << " - Lead" << account.lead.displayName;
qCDebug(dcTempo()) << " - Is Global" << account.global;
qCDebug(dcTempo()) << " - Contact type" << account.contact.type;
qCDebug(dcTempo()) << " - Contact account id" << account.contact.accountId;
qCDebug(dcTempo()) << " - Contact" << account.contact.displayName;
qCDebug(dcTempo()) << " - Category Id" << account.category.id;
qCDebug(dcTempo()) << " - Category name" << account.category.name;
qCDebug(dcTempo()) << " - Category key" << account.category.key;
qCDebug(dcTempo()) << " - Customer id" << account.customer.id;
qCDebug(dcTempo()) << " - Customer key" << account.customer.key;
qCDebug(dcTempo()) << " - Customer name" << account.customer.name;
Thing *thing = myThings().findByParams(ParamList() << Param(accountThingKeyParamTypeId, account.key));
if (!thing) {
continue;
}
thing->setName(account.name);
thing->setStateValue(accountConnectedStateTypeId, (account.status != Tempo::Status::Archived));
thing->setStateValue(accountLeadStateTypeId, account.lead.displayName);
thing->setStateValue(accountGlobalStateTypeId, account.global);
thing->setStateValue(accountCategoryStateTypeId, account.category.name);
thing->setStateValue(accountCustomerStateTypeId, account.customer.name);
thing->setStateValue(accountContactStateTypeId, account.contact.displayName);
thing->setStateValue(accountMonthlyBudgetStateTypeId, account.monthlyBudget);
if (account.status == Tempo::Status::Open) {
thing->setStateValue(accountStatusStateTypeId, "Open");
} else if (account.status == Tempo::Status::Closed) {
thing->setStateValue(accountStatusStateTypeId, "Closed");
} else if (account.status == Tempo::Status::Archived) {
thing->setStateValue(accountStatusStateTypeId, "Archived");
}
}
}
void IntegrationPluginTempo::onTeamsReceived(const QList<Tempo::Team> teams)
{
qCDebug(dcTempo()) << "Teams received";
Q_FOREACH(Tempo::Team team, teams) {
Thing *thing = myThings().findByParams(ParamList() << Param(teamThingIdParamTypeId, team.id));
if (!thing) {
continue;
}
thing->setName(team.name);
thing->setStateValue(teamConnectedStateTypeId, true);
thing->setStateValue(teamLeadStateTypeId, team.lead.displayName);
}
}
void IntegrationPluginTempo::onAccountWorkloadReceived(const QString &accountKey, QList<Tempo::Worklog> workloads)
{
qCDebug(dcTempo()) << "Account workload received, account key:" << accountKey << "Worklog etries: "<< workloads.count();
Thing *thing = myThings().findByParams(ParamList() << Param(accountThingKeyParamTypeId, accountKey));
if (!thing)
if (!thing) {
qCWarning(dcTempo()) << "Could not find account thing for account key" << accountKey;
return;
}
uint totalTimeSpentSeconds = 0;
uint thisMonthTimeSpentSeconds = 0;
QDate today = QDate::currentDate();
Q_FOREACH(Tempo::Worklog workload, workloads) {
if (workload.createdAt.date().month() == today.month()) {
thisMonthTimeSpentSeconds += workload.timeSpentSeconds;
}
totalTimeSpentSeconds += workload.timeSpentSeconds;
}
thing->setStateValue(accountTotalTimeSpentStateTypeId, totalTimeSpentSeconds/3600.00);
thing->setStateValue(accountMonthTimeSpentStateTypeId, thisMonthTimeSpentSeconds/3600.00);
}
void IntegrationPluginTempo::onTeamWorkloadReceived(int teamId, QList<Tempo::Worklog> workloads)
{
qCDebug(dcTempo()) << "Team workload received, team ID:" << teamId << "Worklog etries: "<< workloads.count();
Thing *thing = myThings().findByParams(ParamList() << Param(teamThingIdParamTypeId, teamId));
if (!thing) {
qCWarning(dcTempo()) << "Could not find team thing for account key" << teamId;
return;
}
uint totalTimeSpentSeconds = 0;
Q_FOREACH(Tempo::Worklog workload, workloads) {
totalTimeSpentSeconds += workload.timeSpentSeconds;
}
thing->setStateValue(accountTotalTimeSpentStateTypeId, totalTimeSpentSeconds/60);
thing->setStateValue(teamTotalTimeSpentStateTypeId, totalTimeSpentSeconds/3600.00);
}

View File

@ -52,20 +52,22 @@ public:
void discoverThings(ThingDiscoveryInfo *info) override;
void setupThing(ThingSetupInfo *info) override;
void postSetupThing(Thing *thing) override;
void executeAction(ThingActionInfo *info) override;
void thingRemoved(Thing *thing) override;
private:
PluginTimer *m_pluginTimer15min = nullptr;
QHash<ThingId, Tempo *> m_setupTempoConnections;
QHash<Thing *, Tempo *> m_tempoConnections;
QHash<ThingId, Tempo *> m_tempoConnections;
private slots:
void onConnectionChanged(bool connected);
void onAuthenticationStatusChanged(bool authenticated);
void onReceivedAccounts(const QList<Tempo::Account> &accounts);
void onAccountsReceived(const QList<Tempo::Account> accounts);
void onTeamsReceived(const QList<Tempo::Team> teams);
void onAccountWorkloadReceived(const QString &accountKey, QList<Tempo::Worklog> workloads);
void onTeamWorkloadReceived(int teamId, QList<Tempo::Worklog> workloads);
};
#endif // INTEGRATIONPLUGINTEMPO_H

View File

@ -41,22 +41,6 @@
"displayNameEvent": "Logged in changed",
"defaultValue": true,
"type": "bool"
},
{
"id": "f3b9581b-7828-4fbe-be5f-3e8aad78a71e",
"name": "userDisplayName",
"displayName": "User name",
"displayNameEvent": "User name changed",
"defaultValue": "",
"type": "QString"
},
{
"id": "db70444d-bf67-4133-b2de-54aefbdd7149",
"name": "autoAddAccounts",
"displayName": "Auto add accounts",
"displayNameEvent": "Auto add accounts",
"defaultValue": true,
"type": "bool"
}
]
},
@ -72,7 +56,8 @@
"name": "key",
"displayName": "Key",
"defaultValue": "",
"type": "QString"
"type": "QString",
"readOnly": true
}
],
"stateTypes": [
@ -90,11 +75,11 @@
"name": "status",
"displayName": "Status",
"displayNameEvent": "Status changed",
"defaultValue": "OPEN",
"defaultValue": "Open",
"possibleValues": [
"OPEN",
"CLOSED",
"ARCHIVED"
"Open",
"Closed",
"Archived"
],
"type": "QString"
},
@ -152,8 +137,8 @@
"displayName": "Total time spent",
"displayNameEvent": "Total time spent changed",
"defaultValue": 0,
"type": "uint",
"unit": "Minutes"
"type": "double",
"unit": "Hours"
},
{
"id": "81bec4e8-9fd3-43d1-b339-2a7fdd83e8cb",
@ -161,8 +146,8 @@
"displayName": "This month time spent",
"displayNameEvent": "This month time spent changed",
"defaultValue": 0,
"type": "uint",
"unit": "Minutes"
"type": "double",
"unit": "Hours"
}
]
},
@ -178,7 +163,8 @@
"name": "id",
"displayName": "Id",
"defaultValue": 0,
"type": "int"
"type": "int",
"readOnly": true
}
],
"stateTypes": [
@ -190,6 +176,23 @@
"defaultValue": true,
"cached": false,
"type": "bool"
},
{
"id": "667a9d8d-4e80-4c7c-938c-d698853fa4b1",
"name": "lead",
"displayName": "Lead",
"displayNameEvent": "Lead changed",
"defaultValue": "",
"type": "QString"
},
{
"id": "a694682e-3c2a-4146-aa56-9e75fd82bcab",
"name": "totalTimeSpent",
"displayName": "Total time spent",
"displayNameEvent": "Total time spent changed",
"defaultValue": 0,
"type": "double",
"unit": "Hours"
}
]
}

View File

@ -134,15 +134,24 @@ void Tempo::getAccounts()
account.lead.self = lead["self"].toString();
account.lead.accountId = lead["accountId"].toString();
account.lead.displayName = lead["displayName"].toString();
//TODO Customer
QVariantMap customer = map["customer"].toMap();
account.customer.self = lead["self"].toString();
//TODO Category
account.customer.self = customer["self"].toString();
account.customer.key = customer["key"].toString();
account.customer.id = customer["id"].toInt();
account.customer.name = customer["name"].toString();
QVariantMap category = map["category"].toMap();
account.category.self = lead["self"].toString();
//TODO Contact
account.category.self = category["self"].toString();
account.category.key = category["key"].toString();
account.category.name = category["name"].toString();
account.category.id = category["id"].toInt();
QVariantMap contact = map["contact"].toMap();
account.contact.self = lead["self"].toString();
account.contact.self = contact["self"].toString();
account.contact.type = contact["type"].toString();
account.contact.accountId = contact["accountId"].toString();
account.contact.displayName = contact["displayName"].toString();
accounts.append(account);
}
@ -152,12 +161,14 @@ void Tempo::getAccounts()
});
}
void Tempo::getWorkloadByAccount(const QString &accountKey, QDate from, QDate to)
void Tempo::getWorkloadByAccount(const QString &accountKey, QDate from, QDate to, int offset, int limit)
{
QUrl url = QUrl(m_baseControlUrl+"/worklogs/account/"+accountKey);
QUrlQuery query;
query.addQueryItem("from", from.toString(Qt::DateFormat::ISODate));
query.addQueryItem("to", to.toString(Qt::DateFormat::ISODate));
query.addQueryItem("offset", QString::number(offset));
query.addQueryItem("limit", QString::number(limit));
url.setQuery(query);
qCDebug(dcTempo()) << "Get workload by account. Url" << url.toString();
@ -174,30 +185,41 @@ void Tempo::getWorkloadByAccount(const QString &accountKey, QDate from, QDate to
return;
}
QVariantMap dataMap = QJsonDocument::fromJson(rawData).toVariant().toMap();
QVariantList worklogList = dataMap.value("results").toList();
QList<Worklog> worklogs;
Q_FOREACH(QVariant var, worklogList) {
QVariantMap map = var.toMap();
Worklog worklog;
worklog.self = map["self"].toString();
worklog.tempoWorklogId = map["tempoWorklogId"].toInt();
worklog.jiraWorklogId = map["jiraWorklogId"].toInt();
worklog.issue = map["issue"].toMap().value("key").toString();
worklog.timeSpentSeconds = map["timeSpentSeconds"].toInt();
//TODO startDate: required (date-only)
//TODO startTime: required (time-only)
worklog.description = map["description"].toString();
//TODO createdAt: required (datetime)
//TODO updatedAt: required (datetime)
worklog.authorAccountId = map["author"].toMap().value("accountId").toString();
worklog.authorDisplayName = map["author"].toMap().value("displayName").toString();
worklogs.append(worklog);
}
QList<Worklog> worklogs = parseJsonForWorklog(dataMap);
if (!worklogs.isEmpty())
emit accountWorklogsReceived(accountKey, worklogs);
});
}
void Tempo::getWorkloadByTeam(int teamId, QDate from, QDate to, int offset, int limit)
{
QUrl url = QUrl(m_baseControlUrl+"/worklogs/team/"+QString::number(teamId));
QUrlQuery query;
query.addQueryItem("from", from.toString(Qt::DateFormat::ISODate));
query.addQueryItem("to", to.toString(Qt::DateFormat::ISODate));
query.addQueryItem("offset", QString::number(offset));
query.addQueryItem("limit", QString::number(limit));
url.setQuery(query);
qCDebug(dcTempo()) << "Get workload by account. Url" << url.toString();
QNetworkRequest request(url);
request.setRawHeader("Authorization", "Bearer "+m_token.toUtf8());
QNetworkReply *reply = m_networkManager->get(request);
connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
connect(reply, &QNetworkReply::finished, this, [this, teamId, reply]{
QByteArray rawData = reply->readAll();
if (!checkStatusCode(reply, rawData)) {
return;
}
QVariantMap dataMap = QJsonDocument::fromJson(rawData).toVariant().toMap();
QList<Worklog> worklogs = parseJsonForWorklog(dataMap);
if (!worklogs.isEmpty())
emit teamWorklogsReceived(teamId, worklogs);
});
}
void Tempo::setAuthenticated(bool state)
{
if (state != m_authenticated) {
@ -214,6 +236,30 @@ void Tempo::setConnected(bool state)
}
}
QList<Tempo::Worklog> Tempo::parseJsonForWorklog(const QVariantMap &data)
{
QVariantList worklogList = data.value("results").toList();
QList<Worklog> worklogs;
Q_FOREACH(QVariant var, worklogList) {
QVariantMap map = var.toMap();
Worklog worklog;
worklog.self = map["self"].toString();
worklog.tempoWorklogId = map["tempoWorklogId"].toInt();
worklog.jiraWorklogId = map["jiraWorklogId"].toInt();
worklog.issue = map["issue"].toMap().value("key").toString();
worklog.timeSpentSeconds = map["timeSpentSeconds"].toInt();
//TODO startDate: required (date-only)
//TODO startTime: required (time-only)
worklog.description = map["description"].toString();
worklog.createdAt = QDateTime::fromString(map["createdAt"].toString(), Qt::ISODate);
worklog.updatedAt = QDateTime::fromString(map["updatedAt"].toString(), Qt::ISODate);
worklog.authorAccountId = map["author"].toMap().value("accountId").toString();
worklog.authorDisplayName = map["author"].toMap().value("displayName").toString();
worklogs.append(worklog);
}
return worklogs;
}
bool Tempo::checkStatusCode(QNetworkReply *reply, const QByteArray &rawData)
{
// Check for the internet connection

View File

@ -63,8 +63,9 @@ public:
struct Category {
QUrl self;
QString accountId;
QString displayName;
QString key;
int id;
QString name;
};
struct Customer {
@ -116,11 +117,11 @@ public:
void getTeams();
void getAccounts();
void getWorkloadByAccount(const QString &accountKey, QDate from, QDate to);
void getWorkloadByAccount(const QString &accountKey, QDate from, QDate to, int offset = 0, int limit = 50);
void getWorkloadByTeam(int teamId, QDate from, QDate to, int offset = 0, int limit = 50);
private:
QByteArray m_baseTokenUrl = "https://api.tempo.io/oauth/token/";
QByteArray m_baseControlUrl = "https://api.tempo.io/core/3/";
QByteArray m_baseControlUrl = "https://api.tempo.io/core/3";
QString m_token;
QString m_jiraCloudInstanceName;
@ -132,6 +133,7 @@ private:
bool m_authenticated = false;
bool m_connected = false;
QList<Worklog> parseJsonForWorklog(const QVariantMap &data);
bool checkStatusCode(QNetworkReply *reply, const QByteArray &rawData);
private slots:
@ -143,6 +145,7 @@ signals:
void teamsReceived(const QList<Team> teams);
void accountsReceived(const QList<Account> accounts);
void accountWorklogsReceived(const QString &accountKey, QList<Worklog> worklogs);
void teamWorklogsReceived(int teamId, QList<Worklog> worklogs);
};
#endif // TEMPO_H