fixed worklog counting on exceeding API limit

This commit is contained in:
Boernsman 2021-02-23 19:17:46 +01:00
parent fe9b604c04
commit c77b46a2f5
6 changed files with 434 additions and 73 deletions

View File

@ -49,20 +49,17 @@ void IntegrationPluginTempo::startPairing(ThingPairingInfo *info)
if (info->thingClassId() == tempoConnectionThingClassId) {
QString jiraCloudInstanceName = info->params().paramValue(tempoConnectionThingAtlassianAccountNameParamTypeId).toString();
qCDebug(dcTempo()) << "Checking if the Tempo server is reachable: https://api.tempo.io/core/3";
QNetworkReply *reply = hardwareManager()->networkManager()->get(QNetworkRequest(QUrl("https://api.tempo.io/core/3")));
connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
connect(reply, &QNetworkReply::finished, info, [reply, info, this] {
connect(reply, &QNetworkReply::finished, info, [reply, info] {
if (reply->error() != QNetworkReply::NetworkError::HostNotFoundError) {
qCDebug(dcTempo()) << "Tempo server is reachable";
info->finish(Thing::ThingErrorNoError);
info->finish(Thing::ThingErrorNoError, QT_TR_NOOP("Please enter your Tempo API integration token."));
} else {
qCWarning(dcTempo()) << "Got online check error" << reply->error() << reply->errorString();
info->finish(Thing::ThingErrorSetupFailed, tr("Tempo server not reachable, please check the internet connection"));
info->finish(Thing::ThingErrorSetupFailed, tr("Tempo server not reachable, please check the internet connection."));
}
});
} else {
@ -81,8 +78,7 @@ void IntegrationPluginTempo::confirmPairing(ThingPairingInfo *info, const QStrin
qCWarning(dcTempo()) << "No authorization code received.";
return info->finish(Thing::ThingErrorAuthenticationFailure);
}
QString atlassianAccountName = info->params().paramValue(tempoConnectionThingAtlassianAccountNameParamTypeId).toString();
Tempo *tempo = new Tempo(hardwareManager()->networkManager(), atlassianAccountName, secret, this);
Tempo *tempo = new Tempo(hardwareManager()->networkManager(), secret, this);
tempo->getAccounts();
connect(info, &ThingPairingInfo::aborted, tempo, &Tempo::deleteLater);
connect(tempo, &Tempo::authenticationStatusChanged, info, [info, tempo, secret, this] (bool authenticated){
@ -155,7 +151,7 @@ void IntegrationPluginTempo::discoverThings(ThingDiscoveryInfo *info)
void IntegrationPluginTempo::setupThing(ThingSetupInfo *info)
{
Thing *thing = info->thing();
qCDebug(dcTempo()) << "Setup thing";
qCDebug(dcTempo()) << "Setup thing" << thing->name();
if (thing->thingClassId() == tempoConnectionThingClassId) {
@ -172,6 +168,12 @@ void IntegrationPluginTempo::setupThing(ThingSetupInfo *info)
qCWarning(dcTempo()) << "Tempo connection object not found for thing" << thing->name();
}
m_tempoConnections.insert(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);
} else {
//device loaded from the device database, needs a new access token;
@ -182,8 +184,7 @@ void IntegrationPluginTempo::setupThing(ThingSetupInfo *info)
info->finish(Thing::ThingErrorAuthenticationFailure, tr("Token is not available."));
return;
}
QString jiraInstanceName = thing->paramValue(tempoConnectionThingAtlassianAccountNameParamTypeId).toString();
Tempo *tempo = new Tempo(hardwareManager()->networkManager(), jiraInstanceName, token, this);
Tempo *tempo = new Tempo(hardwareManager()->networkManager(), token, this);
connect(info, &ThingSetupInfo::aborted, tempo, &Tempo::deleteLater);
connect(tempo, &Tempo::authenticationStatusChanged, info, [info, tempo, this] (bool authenticated){
if (authenticated) {
@ -236,12 +237,10 @@ void IntegrationPluginTempo::postSetupThing(Thing *thing)
Q_FOREACH (Thing *childThing, myThings().filterByParentId(thing->id())) {
if (childThing->thingClassId() == accountThingClassId) {
QString key = childThing->paramValue(accountThingKeyParamTypeId).toString();
QDate from(1970, 1, 1);
tempo->getWorkloadByAccount(key, from, QDate::currentDate());
tempo->getWorkloadByAccount(key, QDate(1970, 1, 1), QDate::currentDate(), 0, 1000);
} else if (childThing->thingClassId() == teamThingClassId) {
int id = childThing->paramValue(teamThingIdParamTypeId).toInt();
QDate from(1970, 1, 1);
tempo->getWorkloadByTeam(id, from, QDate::currentDate());
tempo->getWorkloadByTeam(id, QDate(1970, 1, 1), QDate::currentDate(), 0, 1000);
}
}
}
@ -260,13 +259,13 @@ void IntegrationPluginTempo::postSetupThing(Thing *thing)
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->getWorkloadByAccount(key, from, QDate::currentDate(), 0, 1000);
tempo->getAccounts();
} else if (thing->thingClassId() == teamThingClassId) {
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->getWorkloadByTeam(id, from, QDate::currentDate(), 0, 1000);
tempo->getTeams();
}
}
@ -276,7 +275,11 @@ void IntegrationPluginTempo::thingRemoved(Thing *thing)
qCDebug(dcTempo()) << "Thing removed" << thing->name();
if (thing->thingClassId() == tempoConnectionThingClassId) {
m_tempoConnections.take(thing->id())->deleteLater();
} else if (thing->thingClassId() == teamThingClassId ||
thing->thingClassId() == accountThingClassId) {
m_worklogBuffer.remove(thing->id());
}
if (myThings().isEmpty()) {
qCDebug(dcTempo()) << "Stopping plugin timer";
hardwareManager()->pluginTimerManager()->unregisterTimer(m_pluginTimer15min);
@ -315,20 +318,20 @@ void IntegrationPluginTempo::onAccountsReceived(const QList<Tempo::Account> acco
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;
// 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) {
@ -368,7 +371,7 @@ void IntegrationPluginTempo::onTeamsReceived(const QList<Tempo::Team> teams)
}
}
void IntegrationPluginTempo::onAccountWorkloadReceived(const QString &accountKey, QList<Tempo::Worklog> workloads)
void IntegrationPluginTempo::onAccountWorkloadReceived(const QString &accountKey, QList<Tempo::Worklog> workloads, int limit, int offset)
{
qCDebug(dcTempo()) << "Account workload received, account key:" << accountKey << "Worklog etries: "<< workloads.count();
Thing *thing = myThings().findByParams(ParamList() << Param(accountThingKeyParamTypeId, accountKey));
@ -377,32 +380,87 @@ void IntegrationPluginTempo::onAccountWorkloadReceived(const QString &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;
if (offset == 0) {
m_worklogBuffer.remove(thing->id());
}
if (workloads.count() >= limit) {
//limit is reached
if (m_worklogBuffer.contains(thing->id())) {
m_worklogBuffer[thing->id()].append(workloads);
} else {
m_worklogBuffer.insert(thing->id(), workloads);
}
Tempo *tempo = m_tempoConnections.value(thing->parentId());
if (tempo) {
tempo->getWorkloadByAccount(accountKey, QDate(1970, 1, 1), QDate::currentDate(), offset+workloads.count(), limit);
}
} else {
uint totalTimeSpentSeconds = 0;
uint thisMonthTimeSpentSeconds = 0;
QDate today = QDate::currentDate();
Q_FOREACH(Tempo::Worklog workload, workloads) {
if ((workload.startDate.month() == today.month()) && (workload.startDate.year() == today.year())) {
thisMonthTimeSpentSeconds += workload.timeSpentSeconds;
}
totalTimeSpentSeconds += workload.timeSpentSeconds;
}
if (m_worklogBuffer.contains(thing->id())) {
Q_FOREACH(Tempo::Worklog workload, m_worklogBuffer.take(thing->id())) {
if ((workload.startDate.month() == today.month()) && (workload.startDate.year() == today.year())) {
thisMonthTimeSpentSeconds += workload.timeSpentSeconds;
}
totalTimeSpentSeconds += workload.timeSpentSeconds;
}
}
thing->setStateValue(accountTotalTimeSpentStateTypeId, totalTimeSpentSeconds/3600.00);
thing->setStateValue(accountMonthTimeSpentStateTypeId, thisMonthTimeSpentSeconds/3600.00);
}
thing->setStateValue(accountTotalTimeSpentStateTypeId, totalTimeSpentSeconds/3600.00);
thing->setStateValue(accountMonthTimeSpentStateTypeId, thisMonthTimeSpentSeconds/3600.00);
}
void IntegrationPluginTempo::onTeamWorkloadReceived(int teamId, QList<Tempo::Worklog> workloads)
void IntegrationPluginTempo::onTeamWorkloadReceived(int teamId, QList<Tempo::Worklog> workloads, int limit, int offset)
{
qCDebug(dcTempo()) << "Team workload received, team ID:" << teamId << "Worklog etries: "<< workloads.count();
qCDebug(dcTempo()) << "Team workload received, team ID:" << teamId << "Worklog entries: "<< 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;
if (offset == 0) {
m_worklogBuffer.remove(thing->id());
}
if (workloads.count() >= limit) {
//limit is reached#
if (m_worklogBuffer.contains(thing->id())) {
m_worklogBuffer[thing->id()].append(workloads);
} else {
m_worklogBuffer.insert(thing->id(), workloads);
}
Tempo *tempo = m_tempoConnections.value(thing->parentId());
if (tempo) {
tempo->getWorkloadByTeam(teamId, QDate(1970, 1, 1), QDate::currentDate(), offset+workloads.count(), limit);
}
} else {
uint totalTimeSpentSeconds = 0;
uint thisMonthTimeSpentSeconds = 0;
QDate today = QDate::currentDate();
Q_FOREACH(Tempo::Worklog workload, workloads) {
if ((workload.startDate.month() == today.month()) && (workload.startDate.year() == today.year())) {
thisMonthTimeSpentSeconds += workload.timeSpentSeconds;
}
totalTimeSpentSeconds += workload.timeSpentSeconds;
}
if (m_worklogBuffer.contains(thing->id())) {
Q_FOREACH(Tempo::Worklog workload, m_worklogBuffer.take(thing->id())) {
if ((workload.startDate.month() == today.month()) && (workload.startDate.year() == today.year())) {
thisMonthTimeSpentSeconds += workload.timeSpentSeconds;
}
totalTimeSpentSeconds += workload.timeSpentSeconds;
}
}
thing->setStateValue(teamTotalTimeSpentStateTypeId, totalTimeSpentSeconds/3600.00);
thing->setStateValue(teamMonthTimeSpentStateTypeId, thisMonthTimeSpentSeconds/3600.00);
}
thing->setStateValue(teamTotalTimeSpentStateTypeId, totalTimeSpentSeconds/3600.00);
}

View File

@ -57,6 +57,7 @@ public:
private:
PluginTimer *m_pluginTimer15min = nullptr;
QHash<ThingId, QList<Tempo::Worklog>> m_worklogBuffer;
QHash<ThingId, Tempo *> m_setupTempoConnections;
QHash<ThingId, Tempo *> m_tempoConnections;
@ -67,7 +68,7 @@ private slots:
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);
void onAccountWorkloadReceived(const QString &accountKey, QList<Tempo::Worklog> workloads, int limit, int offset);
void onTeamWorkloadReceived(int teamId, QList<Tempo::Worklog> workloads, int limit, int offset);
};
#endif // INTEGRATIONPLUGINTEMPO_H

View File

@ -15,15 +15,6 @@
"interfaces": ["account"],
"createMethods": ["user"],
"setupMethod": "displayPin",
"paramTypes": [
{
"id": "b4110c37-8331-4057-8e9f-12f34c2623fe",
"name": "atlassianAccountName",
"displayName": "Atlassian account name",
"type": "QString",
"defaultValue": ""
}
],
"stateTypes": [
{
"id": "15f45315-5419-4e1b-ace3-fc21503d3b70",
@ -50,6 +41,15 @@
"displayName": "Account",
"interfaces": ["connectable"],
"createMethods": ["discovery"],
"settingsTypes": [
{
"id": "56c460b2-37d8-453a-b4f4-8e58be348f85",
"name": "startDate",
"displayName": "Start date",
"defaultValue": "",
"type": "QDate"
}
],
"paramTypes": [
{
"id": "c6aeddae-56af-496d-a419-1635ff9bae50",
@ -193,6 +193,15 @@
"defaultValue": 0,
"type": "double",
"unit": "Hours"
},
{
"id": "f5ec7b30-3074-41e9-b1fc-62b6307ddbe1",
"name": "monthTimeSpent",
"displayName": "This month time spent",
"displayNameEvent": "This month time spent changed",
"defaultValue": 0,
"type": "double",
"unit": "Hours"
}
]
}

View File

@ -34,18 +34,17 @@
#include "tempo.h"
#include "extern-plugininfo.h"
Tempo::Tempo(NetworkAccessManager *networkmanager, const QString &jiraCloudInstanceName, const QString &token, QObject *parent) :
Tempo::Tempo(NetworkAccessManager *networkmanager, const QString &token, QObject *parent) :
QObject(parent),
m_token(token),
m_jiraCloudInstanceName(jiraCloudInstanceName),
m_networkManager(networkmanager)
{
qCDebug(dcTempo()) << "Creating tempo connection to" << m_jiraCloudInstanceName;
qCDebug(dcTempo()) << "Creating tempo connection";
}
Tempo::~Tempo()
{
qCDebug(dcTempo()) << "Deleting tempo connection to" << m_jiraCloudInstanceName;
qCDebug(dcTempo()) << "Deleting tempo connection";
}
QString Tempo::token() const
@ -185,9 +184,11 @@ void Tempo::getWorkloadByAccount(const QString &accountKey, QDate from, QDate to
return;
}
QVariantMap dataMap = QJsonDocument::fromJson(rawData).toVariant().toMap();
int offset = dataMap.value("metadata").toMap().value("offset").toInt();
int limit = dataMap.value("metadata").toMap().value("limit").toInt();
QList<Worklog> worklogs = parseJsonForWorklog(dataMap);
if (!worklogs.isEmpty())
emit accountWorklogsReceived(accountKey, worklogs);
emit accountWorklogsReceived(accountKey, worklogs, limit, offset);
});
}
@ -214,9 +215,11 @@ void Tempo::getWorkloadByTeam(int teamId, QDate from, QDate to, int offset, int
return;
}
QVariantMap dataMap = QJsonDocument::fromJson(rawData).toVariant().toMap();
int offset = dataMap.value("metadata").toMap().value("offset").toInt();
int limit = dataMap.value("metadata").toMap().value("limit").toInt();
QList<Worklog> worklogs = parseJsonForWorklog(dataMap);
if (!worklogs.isEmpty())
emit teamWorklogsReceived(teamId, worklogs);
emit teamWorklogsReceived(teamId, worklogs, limit, offset);
});
}
@ -239,6 +242,11 @@ void Tempo::setConnected(bool state)
QList<Tempo::Worklog> Tempo::parseJsonForWorklog(const QVariantMap &data)
{
QVariantList worklogList = data.value("results").toList();
qCDebug(dcTempo()) << "Worklog received";
qCDebug(dcTempo()) << " - Count:" << data.value("metadata").toMap().value("count");
qCDebug(dcTempo()) << " - Offset:" << data.value("metadata").toMap().value("offset");
qCDebug(dcTempo()) << " - Limit:" << data.value("metadata").toMap().value("limit");
QList<Worklog> worklogs;
Q_FOREACH(QVariant var, worklogList) {
QVariantMap map = var.toMap();
@ -248,8 +256,8 @@ QList<Tempo::Worklog> Tempo::parseJsonForWorklog(const QVariantMap &data)
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.startDate = QDate::fromString(map["startDate"].toString(), Qt::ISODate);
worklog.startTime = QTime::fromString(map["startTime"].toString(), Qt::ISODate);
worklog.description = map["description"].toString();
worklog.createdAt = QDateTime::fromString(map["createdAt"].toString(), Qt::ISODate);
worklog.updatedAt = QDateTime::fromString(map["updatedAt"].toString(), Qt::ISODate);

View File

@ -95,7 +95,8 @@ public:
int jiraWorklogId;
QString issue;
int timeSpentSeconds;
QDateTime startedAt;
QDate startDate;
QTime startTime;
QString description;
QDateTime createdAt;
QDateTime updatedAt;
@ -111,7 +112,7 @@ public:
Lead lead;
};
explicit Tempo(NetworkAccessManager *networkmanager, const QString &jiraCloudInstanceName, const QString &token, QObject *parent = nullptr);
explicit Tempo(NetworkAccessManager *networkmanager, const QString &token, QObject *parent = nullptr);
~Tempo() override;
QString token() const;
@ -123,7 +124,6 @@ public:
private:
QByteArray m_baseControlUrl = "https://api.tempo.io/core/3";
QString m_token;
QString m_jiraCloudInstanceName;
NetworkAccessManager *m_networkManager = nullptr;
@ -144,8 +144,8 @@ 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);
void accountWorklogsReceived(const QString &accountKey, QList<Worklog> worklogs, int limit, int offset);
void teamWorklogsReceived(int teamId, QList<Worklog> worklogs, int limit, int offset);
};
#endif // TEMPO_H

View File

@ -0,0 +1,285 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1">
<context>
<name>IntegrationPluginTempo</name>
<message>
<location filename="../integrationplugintempo.cpp" line="59"/>
<source>Please enter your Tempo API integration token.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../integrationplugintempo.cpp" line="62"/>
<source>Tempo server not reachable, please check the internet connection.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../integrationplugintempo.cpp" line="102"/>
<source>Create a Tempo connection first</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../integrationplugintempo.cpp" line="184"/>
<source>Token is not available.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>tempo</name>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/tempo/plugininfo.h" line="81"/>
<source>Account</source>
<extracomment>The name of the ThingClass ({8be71352-bdfd-450b-903e-79a4ed203701})</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/tempo/plugininfo.h" line="84"/>
<location filename="../../../build-nymea-plugins-Desktop-Debug/tempo/plugininfo.h" line="87"/>
<source>Category</source>
<extracomment>The name of the ParamType (ThingClass: account, EventType: category, ID: {3af6d1c0-bb0a-406f-809b-2c367e1a16bb})
----------
The name of the StateType ({3af6d1c0-bb0a-406f-809b-2c367e1a16bb}) of ThingClass account</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/tempo/plugininfo.h" line="90"/>
<source>Category changed</source>
<extracomment>The name of the EventType ({3af6d1c0-bb0a-406f-809b-2c367e1a16bb}) of ThingClass account</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/tempo/plugininfo.h" line="93"/>
<location filename="../../../build-nymea-plugins-Desktop-Debug/tempo/plugininfo.h" line="96"/>
<location filename="../../../build-nymea-plugins-Desktop-Debug/tempo/plugininfo.h" line="99"/>
<location filename="../../../build-nymea-plugins-Desktop-Debug/tempo/plugininfo.h" line="102"/>
<location filename="../../../build-nymea-plugins-Desktop-Debug/tempo/plugininfo.h" line="105"/>
<location filename="../../../build-nymea-plugins-Desktop-Debug/tempo/plugininfo.h" line="108"/>
<source>Connected</source>
<extracomment>The name of the ParamType (ThingClass: team, EventType: connected, ID: {a125d3b5-676f-49eb-bb93-feae233c2e91})
----------
The name of the StateType ({a125d3b5-676f-49eb-bb93-feae233c2e91}) of ThingClass team
----------
The name of the ParamType (ThingClass: account, EventType: connected, ID: {0b776bc1-9e56-4205-9bc3-b356026f5b64})
----------
The name of the StateType ({0b776bc1-9e56-4205-9bc3-b356026f5b64}) of ThingClass account
----------
The name of the ParamType (ThingClass: tempoConnection, EventType: connected, ID: {15f45315-5419-4e1b-ace3-fc21503d3b70})
----------
The name of the StateType ({15f45315-5419-4e1b-ace3-fc21503d3b70}) of ThingClass tempoConnection</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/tempo/plugininfo.h" line="111"/>
<location filename="../../../build-nymea-plugins-Desktop-Debug/tempo/plugininfo.h" line="114"/>
<location filename="../../../build-nymea-plugins-Desktop-Debug/tempo/plugininfo.h" line="117"/>
<source>Connected changed</source>
<extracomment>The name of the EventType ({a125d3b5-676f-49eb-bb93-feae233c2e91}) of ThingClass team
----------
The name of the EventType ({0b776bc1-9e56-4205-9bc3-b356026f5b64}) of ThingClass account
----------
The name of the EventType ({15f45315-5419-4e1b-ace3-fc21503d3b70}) of ThingClass tempoConnection</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/tempo/plugininfo.h" line="120"/>
<location filename="../../../build-nymea-plugins-Desktop-Debug/tempo/plugininfo.h" line="123"/>
<source>Contact</source>
<extracomment>The name of the ParamType (ThingClass: account, EventType: contact, ID: {ece43b12-4a0d-4e25-b811-b1aca610bea8})
----------
The name of the StateType ({ece43b12-4a0d-4e25-b811-b1aca610bea8}) of ThingClass account</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/tempo/plugininfo.h" line="126"/>
<source>Contact changed</source>
<extracomment>The name of the EventType ({ece43b12-4a0d-4e25-b811-b1aca610bea8}) of ThingClass account</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/tempo/plugininfo.h" line="129"/>
<location filename="../../../build-nymea-plugins-Desktop-Debug/tempo/plugininfo.h" line="132"/>
<source>Customer</source>
<extracomment>The name of the ParamType (ThingClass: account, EventType: Customer, ID: {3dcc1426-51f8-46fa-9967-5a93d7bb2633})
----------
The name of the StateType ({3dcc1426-51f8-46fa-9967-5a93d7bb2633}) of ThingClass account</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/tempo/plugininfo.h" line="135"/>
<source>Customer changed</source>
<extracomment>The name of the EventType ({3dcc1426-51f8-46fa-9967-5a93d7bb2633}) of ThingClass account</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/tempo/plugininfo.h" line="138"/>
<location filename="../../../build-nymea-plugins-Desktop-Debug/tempo/plugininfo.h" line="141"/>
<source>Global</source>
<extracomment>The name of the ParamType (ThingClass: account, EventType: global, ID: {abd55ea0-ad4e-413e-bc77-3e8b7f0a9be4})
----------
The name of the StateType ({abd55ea0-ad4e-413e-bc77-3e8b7f0a9be4}) of ThingClass account</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/tempo/plugininfo.h" line="144"/>
<source>Global changed</source>
<extracomment>The name of the EventType ({abd55ea0-ad4e-413e-bc77-3e8b7f0a9be4}) of ThingClass account</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/tempo/plugininfo.h" line="147"/>
<source>Id</source>
<extracomment>The name of the ParamType (ThingClass: team, Type: thing, ID: {bb90e986-fcfa-47e8-8783-f2b5a887314a})</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/tempo/plugininfo.h" line="150"/>
<source>Key</source>
<extracomment>The name of the ParamType (ThingClass: account, Type: thing, ID: {c6aeddae-56af-496d-a419-1635ff9bae50})</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/tempo/plugininfo.h" line="153"/>
<location filename="../../../build-nymea-plugins-Desktop-Debug/tempo/plugininfo.h" line="156"/>
<location filename="../../../build-nymea-plugins-Desktop-Debug/tempo/plugininfo.h" line="159"/>
<location filename="../../../build-nymea-plugins-Desktop-Debug/tempo/plugininfo.h" line="162"/>
<source>Lead</source>
<extracomment>The name of the ParamType (ThingClass: team, EventType: lead, ID: {667a9d8d-4e80-4c7c-938c-d698853fa4b1})
----------
The name of the StateType ({667a9d8d-4e80-4c7c-938c-d698853fa4b1}) of ThingClass team
----------
The name of the ParamType (ThingClass: account, EventType: lead, ID: {f1f2af66-d09a-4242-9058-401145f662c4})
----------
The name of the StateType ({f1f2af66-d09a-4242-9058-401145f662c4}) of ThingClass account</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/tempo/plugininfo.h" line="165"/>
<location filename="../../../build-nymea-plugins-Desktop-Debug/tempo/plugininfo.h" line="168"/>
<source>Lead changed</source>
<extracomment>The name of the EventType ({667a9d8d-4e80-4c7c-938c-d698853fa4b1}) of ThingClass team
----------
The name of the EventType ({f1f2af66-d09a-4242-9058-401145f662c4}) of ThingClass account</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/tempo/plugininfo.h" line="171"/>
<location filename="../../../build-nymea-plugins-Desktop-Debug/tempo/plugininfo.h" line="174"/>
<source>Logged in</source>
<extracomment>The name of the ParamType (ThingClass: tempoConnection, EventType: loggedIn, ID: {e4b5be87-dbc9-481e-88da-608c71be8bda})
----------
The name of the StateType ({e4b5be87-dbc9-481e-88da-608c71be8bda}) of ThingClass tempoConnection</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/tempo/plugininfo.h" line="177"/>
<source>Logged in changed</source>
<extracomment>The name of the EventType ({e4b5be87-dbc9-481e-88da-608c71be8bda}) of ThingClass tempoConnection</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/tempo/plugininfo.h" line="180"/>
<location filename="../../../build-nymea-plugins-Desktop-Debug/tempo/plugininfo.h" line="183"/>
<source>Monthly budget</source>
<extracomment>The name of the ParamType (ThingClass: account, EventType: monthlyBudget, ID: {44ebbc18-7511-48c0-860b-c4de5f634ed6})
----------
The name of the StateType ({44ebbc18-7511-48c0-860b-c4de5f634ed6}) of ThingClass account</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/tempo/plugininfo.h" line="186"/>
<source>Monthly budget changed</source>
<extracomment>The name of the EventType ({44ebbc18-7511-48c0-860b-c4de5f634ed6}) of ThingClass account</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/tempo/plugininfo.h" line="189"/>
<source>Start date</source>
<extracomment>The name of the ParamType (ThingClass: account, Type: settings, ID: {56c460b2-37d8-453a-b4f4-8e58be348f85})</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/tempo/plugininfo.h" line="192"/>
<location filename="../../../build-nymea-plugins-Desktop-Debug/tempo/plugininfo.h" line="195"/>
<source>Status</source>
<extracomment>The name of the ParamType (ThingClass: account, EventType: status, ID: {7948f15b-7243-404e-9e67-18e915e8b328})
----------
The name of the StateType ({7948f15b-7243-404e-9e67-18e915e8b328}) of ThingClass account</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/tempo/plugininfo.h" line="198"/>
<source>Status changed</source>
<extracomment>The name of the EventType ({7948f15b-7243-404e-9e67-18e915e8b328}) of ThingClass account</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/tempo/plugininfo.h" line="201"/>
<source>Team</source>
<extracomment>The name of the ThingClass ({11c85176-e7fe-44b4-995a-24757273f3af})</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/tempo/plugininfo.h" line="204"/>
<location filename="../../../build-nymea-plugins-Desktop-Debug/tempo/plugininfo.h" line="207"/>
<source>Tempo</source>
<extracomment>The name of the vendor ({58fc1ab7-b8b5-4e52-8388-72957ce5852d})
----------
The name of the plugin tempo ({809bc4ca-d1cd-4279-9e0d-7324537ccb5a})</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/tempo/plugininfo.h" line="210"/>
<source>Tempo connection</source>
<extracomment>The name of the ThingClass ({878eae0a-6217-4b36-bd46-72c911e52e73})</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/tempo/plugininfo.h" line="213"/>
<location filename="../../../build-nymea-plugins-Desktop-Debug/tempo/plugininfo.h" line="216"/>
<location filename="../../../build-nymea-plugins-Desktop-Debug/tempo/plugininfo.h" line="219"/>
<location filename="../../../build-nymea-plugins-Desktop-Debug/tempo/plugininfo.h" line="222"/>
<source>This month time spent</source>
<extracomment>The name of the ParamType (ThingClass: team, EventType: monthTimeSpent, ID: {f5ec7b30-3074-41e9-b1fc-62b6307ddbe1})
----------
The name of the StateType ({f5ec7b30-3074-41e9-b1fc-62b6307ddbe1}) of ThingClass team
----------
The name of the ParamType (ThingClass: account, EventType: monthTimeSpent, ID: {81bec4e8-9fd3-43d1-b339-2a7fdd83e8cb})
----------
The name of the StateType ({81bec4e8-9fd3-43d1-b339-2a7fdd83e8cb}) of ThingClass account</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/tempo/plugininfo.h" line="225"/>
<location filename="../../../build-nymea-plugins-Desktop-Debug/tempo/plugininfo.h" line="228"/>
<source>This month time spent changed</source>
<extracomment>The name of the EventType ({f5ec7b30-3074-41e9-b1fc-62b6307ddbe1}) of ThingClass team
----------
The name of the EventType ({81bec4e8-9fd3-43d1-b339-2a7fdd83e8cb}) of ThingClass account</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/tempo/plugininfo.h" line="231"/>
<location filename="../../../build-nymea-plugins-Desktop-Debug/tempo/plugininfo.h" line="234"/>
<location filename="../../../build-nymea-plugins-Desktop-Debug/tempo/plugininfo.h" line="237"/>
<location filename="../../../build-nymea-plugins-Desktop-Debug/tempo/plugininfo.h" line="240"/>
<source>Total time spent</source>
<extracomment>The name of the ParamType (ThingClass: team, EventType: totalTimeSpent, ID: {a694682e-3c2a-4146-aa56-9e75fd82bcab})
----------
The name of the StateType ({a694682e-3c2a-4146-aa56-9e75fd82bcab}) of ThingClass team
----------
The name of the ParamType (ThingClass: account, EventType: totalTimeSpent, ID: {1ac39002-56a1-4911-aa68-9d14e142edae})
----------
The name of the StateType ({1ac39002-56a1-4911-aa68-9d14e142edae}) of ThingClass account</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/tempo/plugininfo.h" line="243"/>
<location filename="../../../build-nymea-plugins-Desktop-Debug/tempo/plugininfo.h" line="246"/>
<source>Total time spent changed</source>
<extracomment>The name of the EventType ({a694682e-3c2a-4146-aa56-9e75fd82bcab}) of ThingClass team
----------
The name of the EventType ({1ac39002-56a1-4911-aa68-9d14e142edae}) of ThingClass account</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
</TS>