452 changes: 169 additions & 283 deletions src/Charts/Overview.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -225,322 +225,208 @@ OverviewWindow::setConfiguration(QString config)
// default column widths - max 10 columns;
// note the sizing is such that each card is the equivalent of a full screen
// so we can embed charts etc without compromising what they can display
//
// we drop back here is the config is an old and unsupported format from pre v3.5

defaultsetup: // I know, but its easier than lots of nested if clauses above
defaultsetup:

if (config == "") {

if (scope == OverviewScope::ANALYSIS) {

// column 0
ChartSpaceItem *add;
add = new PMCOverviewItem(space, "coggan_tss");
space->addItem(1,0,1,9, add);

add = new MetaOverviewItem(space, tr("Sport"), "Sport");
space->addItem(2,0,1,5, add);

add = new MetaOverviewItem(space, tr("Workout Code"), "Workout Code");
space->addItem(3,0,1,5, add);

add = new MetricOverviewItem(space, tr("Duration"), "workout_time");
space->addItem(4,0,1,9, add);

add = new MetaOverviewItem(space, tr("Notes"), "Notes");
space->addItem(5,0,1,13, add);

// column 1
add = new MetricOverviewItem(space, tr("HRV rMSSD"), "rMSSD");
space->addItem(1,1,1,9, add);

add = new MetricOverviewItem(space, tr("Heartrate"), "average_hr");
space->addItem(2,1,1,5, add);

add = new ZoneOverviewItem(space, tr("Heartrate Zones"), RideFile::hr, false);
space->addItem(3,1,1,11, add);

add = new MetricOverviewItem(space, tr("Climbing"), "elevation_gain");
space->addItem(4,1,1,5, add);

add = new MetricOverviewItem(space, tr("Cadence"), "average_cad");
space->addItem(5,1,1,5, add);

add = new MetricOverviewItem(space, tr("Work"), "total_work");
space->addItem(6,1,1,5, add);

// column 2
add = new RPEOverviewItem(space, tr("RPE"));
space->addItem(1,2,1,9, add);

add = new MetricOverviewItem(space, tr("Stress"), "coggan_tss");
space->addItem(2,2,1,5, add);

add = new ZoneOverviewItem(space, tr("Fatigue Zones"), RideFile::wbal, false);
space->addItem(3,2,1,11, add);

add = new IntervalOverviewItem(space, tr("Intervals"), "elapsed_time", "average_power", "workout_time");
space->addItem(4,2,1,17, add);

// column 3
add = new MetricOverviewItem(space, tr("Power"), "average_power");
space->addItem(1,3,1,9, add);

add = new MetricOverviewItem(space, tr("IsoPower"), "coggan_np");
space->addItem(2,3,1,5, add);

add = new ZoneOverviewItem(space, tr("Power Zones"), RideFile::watts, false);
space->addItem(3,3,1,11, add);

add = new MetricOverviewItem(space, tr("Peak Power Index"), "peak_power_index");
space->addItem(4,3,1,8, add);

add = new MetricOverviewItem(space, tr("Variability"), "coggam_variability_index");
space->addItem(5,3,1,8, add);

// column 4
add = new MetricOverviewItem(space, tr("Distance"), "total_distance");
space->addItem(1,4,1,9, add);

add = new MetricOverviewItem(space, tr("Speed"), "average_speed");
space->addItem(2,4,1,5, add);

add = new ZoneOverviewItem(space, tr("Pace Zones"), RideFile::kph, false);
space->addItem(3,4,1,11, add);

add = new RouteOverviewItem(space, tr("Route"));
space->addItem(4,4,1,17, add);

// to make life simpler we place a .gchart export into the resources
// this is so we can design them and export rather than doing anything
// special with the contents.
//
// so we open the json doc and extract the config element
//
QString source;
if (scope == OverviewScope::ANALYSIS) source = ":charts/overview-analysis.gchart";
if (scope == OverviewScope::TRENDS) source = ":charts/overview-trends.gchart";

QFile file(source);
if (file.open(QIODevice::ReadOnly)) {
config = file.readAll();
file.close();
}

if (scope == OverviewScope::TRENDS) {
QJsonDocument chart = QJsonDocument::fromJson(config.toUtf8());
if (chart.isEmpty() || chart.isNull()) {

ChartSpaceItem *add;

// column 0
add = new KPIOverviewItem(space, tr("Distance"), 0, 10000, "{ round(sum(metrics(Distance))); }", "km", false);
space->addItem(0,0,1,8, add);
badconfig:
fprintf(stderr, "bad config: %s\n", source.toStdString().c_str());
return;
}

add = new TopNOverviewItem(space, tr("Going Long"), "total_distance");
space->addItem(1,0,1,25, add);
// root is "CHART"
QJsonObject gchartroot = chart.object();
if (!gchartroot.contains("CHART")) goto badconfig;
QJsonObject ochart = gchartroot["CHART"].toObject();

add = new KPIOverviewItem(space, tr("Weekly Hours"), 0, 15*3600, "{ weeks <- (daterange(stop)-daterange(start))/7; sum(metrics(Duration))/weeks; }", tr("hh:mm:ss"), true);
space->addItem(2,0,1,7, add);
// CHART PROPERTIES
if (!ochart.contains("PROPERTIES")) goto badconfig;
QJsonObject properties = ochart["PROPERTIES"].toObject();

// column 1
add = new KPIOverviewItem(space, tr("Peak Power Index"), 0, 150, "{ round(sort(descend, metrics(Power_Index))[0]); }", "%", false);
space->addItem(0,1,1,8, add);
// set from the config property
if (!properties.contains("config")) goto badconfig;
config = properties["config"].toString();
}

add = new MetricOverviewItem(space, tr("Max Power"), "max_power");
space->addItem(1,1,1,7, add);
//
// But by default we parse and apply (dropping back to default setup on error)
//
// parse
QJsonDocument doc = QJsonDocument::fromJson(config.toUtf8());
if (doc.isEmpty() || doc.isNull()) {
config="";
return;
}

add = new MetricOverviewItem(space, tr("Average Power"), "average_power");
space->addItem(2,1,1,7, add);
// parsed so lets work through it and setup the overview
QJsonObject root = doc.object();

add = new ZoneOverviewItem(space, tr("Power Zones"), RideFile::watts, false);
space->addItem(3,1,1,9, add);
// check version
QString version = root["version"].toString();
if (version != "2.0") {
config="";
goto defaultsetup;
}

add = new MetricOverviewItem(space, tr("Total TSS"), "coggan_tss");
space->addItem(4,1,1,7, add);
// cards
QJsonArray CHARTS = root["CHARTS"].toArray();
foreach(const QJsonValue val, CHARTS) {

// column 2
add = new KPIOverviewItem(space, tr("Total Hours"), 0, 0, "{ sum(metrics(Duration)); }", "hh:mm:ss", true);
space->addItem(0,2,1,8, add);
// convert so we can inspect
QJsonObject obj = val.toObject();

add = new TopNOverviewItem(space, tr("Going Hard"), "skiba_wprime_exp");
space->addItem(1,2,1,25, add);
// get the basics
QString name = obj["name"].toString();
QString datafilter = Utils::jsonunprotect(obj["datafilter"].toString());
int column = obj["column"].toInt();
int order = obj["order"].toInt();
int span = obj.contains("span") ? obj["span"].toInt() : 1;
int deep = obj["deep"].toInt();
int type = obj["type"].toInt();

add = new MetricOverviewItem(space, tr("Total W' Work"), "skiba_wprime_exp");
space->addItem(2,2,1,7, add);

// column 3
add = new KPIOverviewItem(space, tr("W' Ratio"), 0, 100, "{ round((sum(metrics(W'_Work)) / sum(metrics(Work))) * 100); }", "%", false);
space->addItem(0,3,1,8, add);
// lets create the cards
ChartSpaceItem *add=NULL;
switch(type) {

add = new KPIOverviewItem(space, tr("Peak CP Estimate "), 0, 360, "{ round(max(estimates(cp3,cp))); }", "watts", false);
space->addItem(1,3,1,7, add);
case OverviewItemType::RPE :
{
add = new RPEOverviewItem(space, name);
add->datafilter = datafilter;
space->addItem(order,column,span,deep, add);
}
break;

add = new KPIOverviewItem(space, tr("Peak W' Estimate "), 0, 25, "{ round(max(estimates(cp3,w')/1000)*10)/10; }", "kJ", false);
space->addItem(2,3,1,7, add);
case OverviewItemType::TOPN :
{
QString symbol=obj["symbol"].toString();
add = new TopNOverviewItem(space, name,symbol);
add->datafilter = datafilter;
space->addItem(order,column,span,deep, add);
}
break;

case OverviewItemType::DONUT :
{
QString symbol=obj["symbol"].toString();
QString meta=obj["meta"].toString();
add = new DonutOverviewItem(space, name,symbol,meta);
add->datafilter = datafilter;
space->addItem(order,column,span,deep, add);
}
break;

add = new ZoneOverviewItem(space, tr("Fatigue Zones"), RideFile::wbal, false);
space->addItem(3,3,1,9, add);
case OverviewItemType::METRIC :
{
QString symbol=obj["symbol"].toString();
add = new MetricOverviewItem(space, name,symbol);
add->datafilter = datafilter;
space->addItem(order,column,span,deep, add);
}
break;

add = new MetricOverviewItem(space, tr("Total Work"), "total_work");
space->addItem(4,3,1,7, add);
case OverviewItemType::META :
{
QString symbol=obj["symbol"].toString();
add = new MetaOverviewItem(space, name,symbol);
add->datafilter = datafilter;
space->addItem(order,column,span,deep, add);
}
break;

// column 4
add = new MetricOverviewItem(space, tr("Intensity Factor"), "coggan_if");
space->addItem(0,4,1,8, add);
case OverviewItemType::PMC :
{
QString symbol=obj["symbol"].toString();
add = new PMCOverviewItem(space, symbol); // doesn't have a title
add->datafilter = datafilter;
space->addItem(order,column,span,deep, add);
}
break;

add = new TopNOverviewItem(space, tr("Going Deep"), "skiba_wprime_low");
space->addItem(1,4,1,25, add);
case OverviewItemType::ZONE :
{
RideFile::SeriesType series = static_cast<RideFile::SeriesType>(obj["series"].toInt());
bool polarized = obj["polarized"].toInt();
add = new ZoneOverviewItem(space, name, series, polarized);
add->datafilter = datafilter;
space->addItem(order,column,span,deep, add);

add = new KPIOverviewItem(space, tr("IF > 0.85"), 0, 0, "{ count(metrics(IF)[x>0.85]); }", "activities", false);
space->addItem(2,4,1,7, add);
}
break;

}
case OverviewItemType::ROUTE :
{
add = new RouteOverviewItem(space, name); // doesn't have a title
add->datafilter = datafilter;
space->addItem(order,column,span,deep, add);
}
break;

} else {
case OverviewItemType::INTERVAL :
case OverviewItemType::ACTIVITIES:
{
QString xsymbol=obj["xsymbol"].toString();
QString ysymbol=obj["ysymbol"].toString();
QString zsymbol=obj["zsymbol"].toString();

//
// But by default we parse and apply (dropping back to default setup on error)
//
// parse
QJsonDocument doc = QJsonDocument::fromJson(config.toUtf8());
if (doc.isEmpty() || doc.isNull()) {
config="";
goto defaultsetup;
}
add = new IntervalOverviewItem(space, name, xsymbol, ysymbol, zsymbol); // doesn't have a title
add->datafilter = datafilter;
space->addItem(order,column,span,deep, add);
}
break;

// parsed so lets work through it and setup the overview
QJsonObject root = doc.object();
case OverviewItemType::DATATABLE:
{
QString program=Utils::jsonunprotect(obj["program"].toString());
add = new DataOverviewItem(space, name, program);
add->datafilter = datafilter;
space->addItem(order,column,span,deep, add);
}
break;

// check version
QString version = root["version"].toString();
if (version != "2.0") {
config="";
goto defaultsetup;
}
case OverviewItemType::KPI :
{
QString program=Utils::jsonunprotect(obj["program"].toString());
double start=obj["start"].toDouble();
double stop =obj["stop"].toDouble();
QString units =obj["units"].toString();
bool istime =obj["istime"].toInt();
add = new KPIOverviewItem(space, name, start, stop, program, units, istime);
add->datafilter = datafilter;
space->addItem(order,column,span,deep, add);
}
break;

// cards
QJsonArray CHARTS = root["CHARTS"].toArray();
foreach(const QJsonValue val, CHARTS) {

// convert so we can inspect
QJsonObject obj = val.toObject();

// get the basics
QString name = obj["name"].toString();
QString datafilter = Utils::jsonunprotect(obj["datafilter"].toString());
int column = obj["column"].toInt();
int order = obj["order"].toInt();
int span = obj.contains("span") ? obj["span"].toInt() : 1;
int deep = obj["deep"].toInt();
int type = obj["type"].toInt();


// lets create the cards
ChartSpaceItem *add=NULL;
switch(type) {

case OverviewItemType::RPE :
{
add = new RPEOverviewItem(space, name);
add->datafilter = datafilter;
space->addItem(order,column,span,deep, add);
}
break;

case OverviewItemType::TOPN :
{
QString symbol=obj["symbol"].toString();
add = new TopNOverviewItem(space, name,symbol);
add->datafilter = datafilter;
space->addItem(order,column,span,deep, add);
}
break;

case OverviewItemType::DONUT :
{
QString symbol=obj["symbol"].toString();
QString meta=obj["meta"].toString();
add = new DonutOverviewItem(space, name,symbol,meta);
add->datafilter = datafilter;
space->addItem(order,column,span,deep, add);
}
break;

case OverviewItemType::METRIC :
{
QString symbol=obj["symbol"].toString();
add = new MetricOverviewItem(space, name,symbol);
add->datafilter = datafilter;
space->addItem(order,column,span,deep, add);
}
break;

case OverviewItemType::META :
{
QString symbol=obj["symbol"].toString();
add = new MetaOverviewItem(space, name,symbol);
add->datafilter = datafilter;
space->addItem(order,column,span,deep, add);
}
break;

case OverviewItemType::PMC :
{
QString symbol=obj["symbol"].toString();
add = new PMCOverviewItem(space, symbol); // doesn't have a title
add->datafilter = datafilter;
space->addItem(order,column,span,deep, add);
}
break;

case OverviewItemType::ZONE :
{
RideFile::SeriesType series = static_cast<RideFile::SeriesType>(obj["series"].toInt());
bool polarized = obj["polarized"].toInt();
add = new ZoneOverviewItem(space, name, series, polarized);
add->datafilter = datafilter;
space->addItem(order,column,span,deep, add);

}
break;

case OverviewItemType::ROUTE :
{
add = new RouteOverviewItem(space, name); // doesn't have a title
add->datafilter = datafilter;
space->addItem(order,column,span,deep, add);
}
break;

case OverviewItemType::INTERVAL :
case OverviewItemType::ACTIVITIES:
{
QString xsymbol=obj["xsymbol"].toString();
QString ysymbol=obj["ysymbol"].toString();
QString zsymbol=obj["zsymbol"].toString();

add = new IntervalOverviewItem(space, name, xsymbol, ysymbol, zsymbol); // doesn't have a title
add->datafilter = datafilter;
space->addItem(order,column,span,deep, add);
}
break;

case OverviewItemType::DATATABLE:
{
QString program=Utils::jsonunprotect(obj["program"].toString());
add = new DataOverviewItem(space, name, program);
add->datafilter = datafilter;
space->addItem(order,column,span,deep, add);
}
break;

case OverviewItemType::KPI :
{
QString program=Utils::jsonunprotect(obj["program"].toString());
double start=obj["start"].toDouble();
double stop =obj["stop"].toDouble();
QString units =obj["units"].toString();
bool istime =obj["istime"].toInt();
add = new KPIOverviewItem(space, name, start, stop, program, units, istime);
add->datafilter = datafilter;
space->addItem(order,column,span,deep, add);
}
break;

case OverviewItemType::USERCHART :
{
QString settings=Utils::jsonunprotect(obj["settings"].toString());
add = new UserChartOverviewItem(space, name, settings);
add->datafilter = datafilter;
space->addItem(order,column,span,deep, add);
}
break;
case OverviewItemType::USERCHART :
{
QString settings=Utils::jsonunprotect(obj["settings"].toString());
add = new UserChartOverviewItem(space, name, settings);
add->datafilter = datafilter;
space->addItem(order,column,span,deep, add);
}
break;
}
}

Expand Down
2 changes: 2 additions & 0 deletions src/Resources/application.qrc
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<RCC>
<qresource prefix="/">
<file>charts/overview-analysis.gchart</file>
<file>charts/overview-trends.gchart</file>
<file>webservice/httpserver.ini</file>
<file>sidebar/apps.png</file>
<file>sidebar/assess.png</file>
Expand Down
17 changes: 17 additions & 0 deletions src/Resources/charts/overview-analysis.gchart
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"CHART":{
"VERSION":"1",
"VIEW":"analysis",
"TYPE":"42",
"PROPERTIES":{
"title":"Overview ",
"subtitle":" ",
"widthFactor":"2.5",
"heightFactor":"2.08333",
"style":"0",
"resizable":"0",
"config":"{\n \"version\":\"2.0\",\n \"CHARTS\":[\n { \"type\":101,\"span\":1,\"deep\":5,\"column\":0,\"order\":3,\"symbol\":\"activity_date\",\"datafilter\":\" \",\"name\":\"Date\" },\n { \"type\":102,\"span\":1,\"deep\":5,\"column\":0,\"order\":8,\"symbol\":\"Sport\",\"datafilter\":\" \",\"name\":\"Sport\" },\n { \"type\":102,\"span\":1,\"deep\":5,\"column\":0,\"order\":13,\"symbol\":\"Workout Code\",\"datafilter\":\" \",\"name\":\"Workout Code\" },\n { \"type\":102,\"span\":1,\"deep\":32,\"column\":0,\"order\":35,\"symbol\":\"Notes\",\"datafilter\":\" \",\"name\":\"Notes\" },\n { \"type\":104,\"span\":2,\"deep\":25,\"column\":0,\"order\":68,\"xsymbol\":\"elapsed_time\",\"ysymbol\":\"average_power\",\"zsymbol\":\"workout_time\",\"datafilter\":\" \",\"name\":\"Intervals Average Power\" },\n { \"type\":104,\"span\":2,\"deep\":24,\"column\":0,\"order\":70,\"xsymbol\":\"elapsed_time\",\"ysymbol\":\"power_index\",\"zsymbol\":\"workout_time\",\"datafilter\":\" \",\"name\":\"Intervals Power Index\" },\n { \"type\":112,\"span\":1,\"deep\":12,\"column\":1,\"order\":29,\"program\":\"{\\n\\n# column names, if using metrics then best\\n# to use name() to get correct name for locale\\n# otherwise it won't translate to other languages\\nnames { \\n metricname(Duration,\\n Time_Moving,\\n Distance,\\n Work,\\n W'_Work,\\n Elevation_Gain);\\n}\\n\\n# column units, if using metrics then best\\n# to use unit() function to get correct string\\n# for locale and metric\\\/imperial\\nunits { \\n metricunit(Duration,\\n Time_Moving,\\n Distance,\\n Work,\\n W'_Work,\\n Elevation_Gain);\\n}\\n\\n# values to display as doubles or strings\\n# if using metrics always best to use asstring()\\n# to convert correctly with dp, metric\\\/imperial\\n# or specific formats eg. rowing pace xx\\\/500m\\nvalues { \\n asstring(Duration,\\n Time_Moving,\\n Distance,\\n Work,\\n W'_Work,\\n Elevation_Gain); \\n} \\n\\n}\\n \",\"datafilter\":\" \",\"name\":\"Totals\" },\n { \"type\":112,\"span\":1,\"deep\":12,\"column\":1,\"order\":54,\"program\":\"{\\n\\n# column names, if using metrics then best\\n# to use name() to get correct name for locale\\n# otherwise it won't translate to other languages\\nnames { \\n metricname(Athlete_Weight,\\n Average_Speed,\\n Average_Power,\\n Average_Heart_Rate,\\n Average_Cadence);\\n}\\n\\n# column units, if using metrics then best\\n# to use unit() function to get correct string\\n# for locale and metric\\\/imperial\\nunits { \\n metricunit(Athlete_Weight,\\n Average_Speed,\\n Average_Power,\\n Average_Heart_Rate,\\n Average_Cadence);\\n}\\n\\n# values to display as doubles or strings\\n# if using metrics always best to use asstring()\\n# to convert correctly with dp, metric\\\/imperial\\n# or specific formats eg. rowing pace xx\\\/500m\\nvalues { \\n asstring(Athlete_Weight,\\n Average_Speed,\\n Average_Power,\\n Average_Heart_Rate,\\n Average_Cadence); \\n} \\n\\n}\\n \",\"datafilter\":\" \",\"name\":\"Averages\" },\n { \"type\":112,\"span\":1,\"deep\":11,\"column\":1,\"order\":55,\"program\":\"{\\n\\n# column names, if using metrics then best\\n# to use name() to get correct name for locale\\n# otherwise it won't translate to other languages\\nnames { \\n metricname(Max_Speed,\\n Max_Power,\\n Max_Heartrate,\\n Max_Cadence,\\n Max_W'_Expended);\\n}\\n\\n# column units, if using metrics then best\\n# to use unit() function to get correct string\\n# for locale and metric\\\/imperial\\nunits { \\n metricunit(Max_Speed,\\n Max_Power,\\n Max_Heartrate,\\n Max_Cadence,\\n Max_W'_Expended);\\n}\\n\\n# values to display as doubles or strings\\n# if using metrics always best to use asstring()\\n# to convert correctly with dp, metric\\\/imperial\\n# or specific formats eg. rowing pace xx\\\/500m\\nvalues { \\n asstring(Max_Speed,\\n Max_Power,\\n Max_Heartrate,\\n Max_Cadence,\\n Max_W'_Expended); \\n} \\n\\n}\\n \",\"datafilter\":\" \",\"name\":\"Maximum\" },\n { \"type\":112,\"span\":1,\"deep\":12,\"column\":1,\"order\":125,\"program\":\"{\\n\\n# column names, if using metrics then best\\n# to use name() to get correct name for locale\\n# otherwise it won't translate to other languages\\nnames { \\n metricname(IsoPower,\\n L4_Sustained_Time,\\n SDANN_HRV,\\n SDNNIDX_HRV,\\n SDNN_HRV,\\n VI);\\n}\\n\\n# column units, if using metrics then best\\n# to use unit() function to get correct string\\n# for locale and metric\\\/imperial\\nunits { \\n metricunit(IsoPower,\\n L4_Sustained_Time,\\n SDANN_HRV,\\n SDNNIDX_HRV,\\n SDNN_HRV,\\n VI);\\n}\\n\\n# values to display as doubles or strings\\n# if using metrics always best to use asstring()\\n# to convert correctly with dp, metric\\\/imperial\\n# or specific formats eg. rowing pace xx\\\/500m\\nvalues { \\n asstring(IsoPower,\\n L4_Sustained_Time,\\n SDANN_HRV,\\n SDNNIDX_HRV,\\n SDNN_HRV,\\n VI);\\n} \\n\\n}\\n \",\"datafilter\":\" \",\"name\":\"Metrics\" },\n { \"type\":100,\"span\":1,\"deep\":7,\"column\":2,\"order\":201,\"datafilter\":\" \",\"name\":\"RPE\" },\n { \"type\":113,\"span\":4,\"deep\":17,\"column\":2,\"order\":213,\"settings\":\"{ \\\"title\\\": \\\" \\\",\\n\\\"description\\\": \\\"A description of the chart, mostly useful when the chart is uploaded to the cloud to let others know what this chart is useful for etc. \\\",\\n\\\"type\\\": 1,\\n\\\"animate\\\": false,\\n\\\"legendpos\\\": 2,\\n\\\"stack\\\": false,\\n\\\"orientation\\\": 2,\\n\\\"scale\\\": 2.5,\\n\\\"SERIES\\\": [\\n{ \\\"name\\\": \\\"Power \\\", \\\"group\\\": \\\" \\\", \\\"xname\\\": \\\"Elapsed Time \\\", \\\"yname\\\": \\\"watts \\\", \\\"program\\\": \\\"{:sl:n finalise {:sl:n # we just fetch samples at end:sl:n xx <- samples(SECS);:sl:n yy <- samples(POWER);:sl:n }:sl:n:sl:n x { xx; }:sl:n y { yy; }:sl:n}:sl:n \\\", \\\"line\\\": 1, \\\"symbol\\\": 0, \\\"size\\\": 3, \\\"color\\\": \\\"#010112\\\", \\\"opacity\\\": 100, \\\"legend\\\": true, \\\"opengl\\\": true, \\\"datalabels\\\": false, \\\"aggregate\\\": 0, \\\"fill\\\": true},\\n{ \\\"name\\\": \\\"W'balance \\\", \\\"group\\\": \\\" \\\", \\\"xname\\\": \\\"Elapsed Time \\\", \\\"yname\\\": \\\"kJ \\\", \\\"program\\\": \\\"{:sl:n finalise {:sl:n # we just fetch samples at end:sl:n xx <- samples(SECS);:sl:n yy <- samples(WBAL):sl:\\\/1000;:sl:n:sl:n :sl:n }:sl:n:sl:n x { xx; }:sl:n y { yy; }:sl:n}:sl:n \\\", \\\"line\\\": 1, \\\"symbol\\\": 0, \\\"size\\\": 3, \\\"color\\\": \\\"#010157\\\", \\\"opacity\\\": 75, \\\"legend\\\": true, \\\"opengl\\\": true, \\\"datalabels\\\": false, \\\"aggregate\\\": 0, \\\"fill\\\": false} ]\\n,\\n\\\"AXES\\\": [\\n{ \\\"name\\\": \\\"Elapsed Time \\\", \\\"type\\\": 2, \\\"orientation\\\": 1, \\\"align\\\": 1, \\\"minx\\\": 0, \\\"maxx\\\": 0, \\\"miny\\\": 0, \\\"maxy\\\": 0, \\\"smooth\\\": 7, \\\"groupby\\\": 0, \\\"visible\\\": true, \\\"fixed\\\": false, \\\"log\\\": false, \\\"minorgrid\\\": false, \\\"majorgrid\\\": true, \\\"labelcolor\\\": \\\"#55aaff\\\", \\\"axiscolor\\\": \\\"#55aaff\\\"},\\n{ \\\"name\\\": \\\"watts \\\", \\\"type\\\": 0, \\\"orientation\\\": 2, \\\"align\\\": 1, \\\"minx\\\": 0, \\\"maxx\\\": 0, \\\"miny\\\": 0, \\\"maxy\\\": 1000, \\\"smooth\\\": 0, \\\"groupby\\\": 0, \\\"visible\\\": true, \\\"fixed\\\": false, \\\"log\\\": false, \\\"minorgrid\\\": false, \\\"majorgrid\\\": true, \\\"labelcolor\\\": \\\"#010112\\\", \\\"axiscolor\\\": \\\"#010112\\\"},\\n{ \\\"name\\\": \\\"kJ \\\", \\\"type\\\": 0, \\\"orientation\\\": 2, \\\"align\\\": 1, \\\"minx\\\": 0, \\\"maxx\\\": 0, \\\"miny\\\": 0, \\\"maxy\\\": 18, \\\"smooth\\\": 0, \\\"groupby\\\": 0, \\\"visible\\\": true, \\\"fixed\\\": false, \\\"log\\\": false, \\\"minorgrid\\\": false, \\\"majorgrid\\\": true, \\\"labelcolor\\\": \\\"#010157\\\", \\\"axiscolor\\\": \\\"#010157\\\"} ]\\n} \",\"datafilter\":\" \",\"name\":\"Power and W'balance\" },\n { \"type\":112,\"span\":2,\"deep\":12,\"column\":2,\"order\":214,\"program\":\"{\\n\\n# column names, if using metrics then best\\n# to use metricname() to get correct name for locale\\n# otherwise it won't translate to other languages\\nnames { \\n c(\\\"Name\\\",\\\"Description\\\",\\\"Low\\\",\\\"High\\\",\\\"Time\\\",\\\"%\\\");\\n}\\n\\n# column units, if using metrics then best\\n# to use metricunit() function to get correct string\\n# for locale and metric\\\/imperial\\nunits {\\n\\n c(\\\"\\\",\\n \\\"\\\",\\n zones(power,units),\\n zones(power,units), \\\"\\\", \\\"\\\");\\n}\\n\\n# values to display as doubles or strings\\n# if using metrics always best to use asstring()\\n# to convert correctly with dp, metric\\\/imperial\\n# or specific formats eg. rowing pace xx\\\/500m\\nvalues { \\n\\n c( zones(power,name),\\n zones(power,description),\\n zones(power,low),\\n zones(power,high),\\n zones(power,time),\\n zones(power,percent));\\n} \\n\\n}\\n \",\"datafilter\":\" \",\"name\":\"Power Zones\" },\n { \"type\":103,\"span\":1,\"deep\":11,\"column\":2,\"order\":1720,\"series\":50,\"polarized\":0,\"datafilter\":\" \",\"name\":\"Fatigue Zones\" },\n { \"type\":112,\"span\":4,\"deep\":50,\"column\":2,\"order\":1760,\"program\":\"{\\n\\n# column names, if using metrics then best\\n# to use metricname() to get correct name for locale\\n# otherwise it won't translate to other languages\\nnames { \\n metricname(type, name,\\n Average_Power,\\n Distance,\\n Duration,\\n Fatigue_Index,\\n Power_Index,\\n W'_Work,\\n Work);\\n}\\n\\n# column units, if using metrics then best\\n# to use metricunit() function to get correct string\\n# for locale and metric\\\/imperial\\nunits { \\n metricunit(type, name,\\n Average_Power,\\n Distance,\\n Duration,\\n Fatigue_Index,\\n Power_Index,\\n W'_Work,\\n Work);\\n}\\n\\n# values to display as doubles or strings\\n# if using metrics always best to use asstring()\\n# to convert correctly with dp, metric\\\/imperial\\n# or specific formats eg. rowing pace xx\\\/500m\\nvalues { \\n c(intervalstrings(type),\\n intervalstrings(name),\\n intervalstrings(Average_Power),\\n intervalstrings(Distance),\\n intervalstrings(Duration),\\n intervalstrings(Fatigue_Index),\\n intervalstrings(Power_Index),\\n intervalstrings(W'_Work),\\n intervalstrings(Work));\\n}\\n\\n}\\n \",\"datafilter\":\" \",\"name\":\"Intervals\" },\n { \"type\":107,\"span\":1,\"deep\":7,\"column\":3,\"order\":453,\"program\":\"{ round(config(weight)*10)\\\/10; } \",\"units\":\"kg\",\"istime\":0,\"start\":99.3,\"stop\":85,\"datafilter\":\" \",\"name\":\"Weight\" },\n { \"type\":112,\"span\":2,\"deep\":11,\"column\":3,\"order\":457,\"program\":\"{\\n\\n# column names, if using metrics then best\\n# to use name() to get correct name for locale\\n# otherwise it won't translate to other languages\\nnames { \\n c(\\\"Name\\\",\\\"Description\\\",\\\"Low\\\",\\n \\\"High\\\", \\\"Time\\\", \\\"%\\\");\\n}\\n\\n# column units, if using metrics then best\\n# to use unit() function to get correct string\\n# for locale and metric\\\/imperial\\nunits {\\n\\n c(\\\"\\\",\\n \\\"\\\",\\n zones(fatigue,units),\\n zones(fatigue,units),\\\"\\\", \\\"\\\");\\n}\\n\\n# values to display as doubles or strings\\n# if using metrics always best to use asstring()\\n# to convert correctly with dp, metric\\\/imperial\\n# or specific formats eg. rowing pace xx\\\/500m\\nvalues { \\n\\n c( zones(fatigue,name),\\n zones(fatigue,description),\\n zones(fatigue,low),\\n zones(fatigue,high),\\n zones(fatigue,time),\\n zones(fatigue,percent));\\n} \\n\\n}\\n \",\"datafilter\":\" \",\"name\":\"Fatigue Zones\" },\n { \"type\":107,\"span\":1,\"deep\":7,\"column\":4,\"order\":17,\"program\":\"{ cp<-config(cp);\\n tabove <- count(samples(POWER)[x>cp]);\\n round((tabove \\\/ Duration) * 100.0);\\n} \",\"units\":\"%\",\"istime\":0,\"start\":0,\"stop\":100,\"datafilter\":\" \",\"name\":\"Time above CP\" },\n { \"type\":103,\"span\":1,\"deep\":12,\"column\":4,\"order\":18,\"series\":10,\"polarized\":0,\"datafilter\":\" \",\"name\":\"Power Zones\" },\n { \"type\":107,\"span\":1,\"deep\":7,\"column\":5,\"order\":1487,\"program\":\"{\\n est <- round((estimate(cp3,cp) +\\n estimate(cp2,cp) +\\n estimate(ext,cp))\\\/3);\\n} \",\"units\":\"watts\",\"istime\":0,\"start\":0,\"stop\":360,\"datafilter\":\" \",\"name\":\"CP Estimate\" },\n { \"type\":107,\"span\":1,\"deep\":9,\"column\":5,\"order\":1497,\"program\":\"{\\n est <- round((estimate(cp3,w') +\\n estimate(cp2,w') +\\n estimate(ext,w'))\\\/3000);\\n} \",\"units\":\"kJ\",\"istime\":0,\"start\":5,\"stop\":45,\"datafilter\":\" \",\"name\":\"W' Estimate\" },\n { \"type\":101,\"span\":1,\"deep\":7,\"column\":5,\"order\":1498,\"symbol\":\"coggan_tss\",\"datafilter\":\" \",\"name\":\"TSS\" },\n { \"type\":101,\"span\":1,\"deep\":6,\"column\":5,\"order\":1522,\"symbol\":\"coggan_np\",\"datafilter\":\" \",\"name\":\"NP\" }\n ]\n}\n ",
"__LAST__":"1"
}
}
}
17 changes: 17 additions & 0 deletions src/Resources/charts/overview-trends.gchart
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"CHART":{
"VERSION":"1",
"VIEW":"home",
"TYPE":"47",
"PROPERTIES":{
"title":"Overview ",
"subtitle":" ",
"widthFactor":"2",
"heightFactor":"2",
"style":"0",
"resizable":"0",
"config":"{\n \"version\":\"2.0\",\n \"CHARTS\":[\n { \"type\":108,\"span\":1,\"deep\":15,\"column\":0,\"order\":110,\"symbol\":\"total_distance\",\"datafilter\":\"filter:isRide \",\"name\":\"Going Long\" },\n { \"type\":108,\"span\":1,\"deep\":16,\"column\":0,\"order\":168,\"symbol\":\"skiba_wprime_low\",\"datafilter\":\" \",\"name\":\"Going Deep\" },\n { \"type\":108,\"span\":1,\"deep\":17,\"column\":0,\"order\":179,\"symbol\":\"average_speed\",\"datafilter\":\"filter:isRide \",\"name\":\"Going Fast\" },\n { \"type\":113,\"span\":4,\"deep\":32,\"column\":0,\"order\":193,\"settings\":\"{ \\\"title\\\": \\\" \\\",\\n\\\"description\\\": \\\"A description of the chart, mostly useful when the chart is uploaded to the cloud to let others know what this chart is useful for etc. \\\",\\n\\\"type\\\": 1,\\n\\\"animate\\\": false,\\n\\\"legendpos\\\": 2,\\n\\\"stack\\\": false,\\n\\\"orientation\\\": 1,\\n\\\"scale\\\": 2.5,\\n\\\"SERIES\\\": [\\n{ \\\"name\\\": \\\"CTL \\\", \\\"group\\\": \\\" \\\", \\\"xname\\\": \\\"Date \\\", \\\"yname\\\": \\\"TSS \\\", \\\"program\\\": \\\"{:sl:n finalise {:sl:n # we just fetch metrics at the end:sl:n xx <- pmc(BikeStress, date);:sl:n yy <- pmc(BikeStress, lts);:sl:n }:sl:n:sl:n x { xx; }:sl:n y { yy; }:sl:n}:sl:n \\\", \\\"line\\\": 1, \\\"symbol\\\": 0, \\\"size\\\": 2, \\\"color\\\": \\\"#010129\\\", \\\"opacity\\\": 100, \\\"legend\\\": true, \\\"opengl\\\": true, \\\"datalabels\\\": false, \\\"aggregate\\\": 1, \\\"fill\\\": false},\\n{ \\\"name\\\": \\\"ATL \\\", \\\"group\\\": \\\" \\\", \\\"xname\\\": \\\"Date \\\", \\\"yname\\\": \\\"TSS \\\", \\\"program\\\": \\\"{:sl:n finalise {:sl:n # we just fetch metrics at the end:sl:n xx <- pmc(BikeStress, date);:sl:n yy <- pmc(BikeStress, sts);:sl:n }:sl:n:sl:n x { xx; }:sl:n y { yy; }:sl:n}:sl:n \\\", \\\"line\\\": 1, \\\"symbol\\\": 0, \\\"size\\\": 2, \\\"color\\\": \\\"#010128\\\", \\\"opacity\\\": 100, \\\"legend\\\": true, \\\"opengl\\\": true, \\\"datalabels\\\": false, \\\"aggregate\\\": 1, \\\"fill\\\": false},\\n{ \\\"name\\\": \\\"TSB \\\", \\\"group\\\": \\\" \\\", \\\"xname\\\": \\\"Date \\\", \\\"yname\\\": \\\"TSS \\\", \\\"program\\\": \\\"{:sl:n finalise {:sl:n # we just fetch metrics at the end:sl:n xx <- pmc(BikeStress, date);:sl:n yy <- pmc(BikeStress, sb);:sl:n }:sl:n:sl:n x { xx; }:sl:n y { yy; }:sl:n}:sl:n \\\", \\\"line\\\": 1, \\\"symbol\\\": 0, \\\"size\\\": 2, \\\"color\\\": \\\"#01012a\\\", \\\"opacity\\\": 100, \\\"legend\\\": true, \\\"opengl\\\": true, \\\"datalabels\\\": false, \\\"aggregate\\\": 1, \\\"fill\\\": false} ]\\n,\\n\\\"AXES\\\": [\\n{ \\\"name\\\": \\\"Date \\\", \\\"type\\\": 1, \\\"orientation\\\": 1, \\\"align\\\": 1, \\\"minx\\\": 0, \\\"maxx\\\": 0, \\\"miny\\\": 0, \\\"maxy\\\": 0, \\\"smooth\\\": 0, \\\"groupby\\\": 1, \\\"visible\\\": true, \\\"fixed\\\": false, \\\"log\\\": false, \\\"minorgrid\\\": false, \\\"majorgrid\\\": true, \\\"labelcolor\\\": \\\"#55aaff\\\", \\\"axiscolor\\\": \\\"#55aaff\\\"},\\n{ \\\"name\\\": \\\"TSS \\\", \\\"type\\\": 0, \\\"orientation\\\": 2, \\\"align\\\": 1, \\\"minx\\\": 0, \\\"maxx\\\": 0, \\\"miny\\\": 0, \\\"maxy\\\": 0, \\\"smooth\\\": 0, \\\"groupby\\\": 0, \\\"visible\\\": true, \\\"fixed\\\": false, \\\"log\\\": false, \\\"minorgrid\\\": false, \\\"majorgrid\\\": true, \\\"labelcolor\\\": \\\"#010129\\\", \\\"axiscolor\\\": \\\"#010129\\\"} ]\\n} \",\"datafilter\":\" \",\"name\":\"PMC\" },\n { \"type\":113,\"span\":6,\"deep\":18,\"column\":0,\"order\":195,\"settings\":\"{ \\\"title\\\": \\\" \\\",\\n\\\"description\\\": \\\"A description of the chart, mostly useful when the chart is uploaded to the cloud to let others know what this chart is useful for etc. \\\",\\n\\\"type\\\": 3,\\n\\\"animate\\\": false,\\n\\\"legendpos\\\": 2,\\n\\\"stack\\\": false,\\n\\\"orientation\\\": 1,\\n\\\"scale\\\": 2.5,\\n\\\"SERIES\\\": [\\n{ \\\"name\\\": \\\"TSS \\\", \\\"group\\\": \\\" \\\", \\\"xname\\\": \\\"Date \\\", \\\"yname\\\": \\\"TSS \\\", \\\"program\\\": \\\"{:sl:n:sl:n finalise {:sl:n # we just fetch metrics at the end:sl:n xx <- metrics(date);:sl:n yy <- round(metrics(BikeStress));:sl:n }:sl:n:sl:n x { xx; }:sl:n y { yy; }:sl:n}:sl:n \\\", \\\"line\\\": 1, \\\"symbol\\\": 0, \\\"size\\\": 1, \\\"color\\\": \\\"#010126\\\", \\\"opacity\\\": 100, \\\"legend\\\": true, \\\"opengl\\\": true, \\\"datalabels\\\": true, \\\"aggregate\\\": 0, \\\"fill\\\": false} ]\\n,\\n\\\"AXES\\\": [\\n{ \\\"name\\\": \\\"Date \\\", \\\"type\\\": 1, \\\"orientation\\\": 1, \\\"align\\\": 1, \\\"minx\\\": 0, \\\"maxx\\\": 0, \\\"miny\\\": 0, \\\"maxy\\\": 0, \\\"smooth\\\": 0, \\\"groupby\\\": 3, \\\"visible\\\": true, \\\"fixed\\\": false, \\\"log\\\": false, \\\"minorgrid\\\": false, \\\"majorgrid\\\": true, \\\"labelcolor\\\": \\\"#55aaff\\\", \\\"axiscolor\\\": \\\"#55aaff\\\"},\\n{ \\\"name\\\": \\\"TSS \\\", \\\"type\\\": 0, \\\"orientation\\\": 2, \\\"align\\\": 1, \\\"minx\\\": 0, \\\"maxx\\\": 0, \\\"miny\\\": 0, \\\"maxy\\\": 0, \\\"smooth\\\": 0, \\\"groupby\\\": 0, \\\"visible\\\": true, \\\"fixed\\\": false, \\\"log\\\": false, \\\"minorgrid\\\": false, \\\"majorgrid\\\": true, \\\"labelcolor\\\": \\\"#010126\\\", \\\"axiscolor\\\": \\\"#010126\\\"} ]\\n} \",\"datafilter\":\" \",\"name\":\"Monthly Load\" },\n { \"type\":107,\"span\":1,\"deep\":8,\"column\":1,\"order\":1449,\"program\":\"{\\n end <- daterange(stop) > Today ? Today : daterange(stop);\\n days <- end - daterange(start);\\n round(sum(metrics(Distance))\\\/(days\\\/7)); \\n\\n} \",\"units\":\"km\",\"istime\":0,\"start\":0,\"stop\":300,\"datafilter\":\"filter: \",\"name\":\"Weekly Distance\" },\n { \"type\":110,\"span\":2,\"deep\":24,\"column\":1,\"order\":1469,\"xsymbol\":\"activity_date\",\"ysymbol\":\"average_power\",\"zsymbol\":\"coggan_tss\",\"datafilter\":\"search: \",\"name\":\"Activities\" },\n { \"type\":112,\"span\":2,\"deep\":16,\"column\":1,\"order\":1470,\"program\":\"{\\n\\n# column names, if using metrics then best\\n# to use metricname() to get correct name for locale\\n# otherwise it won't translate to other languages\\nnames { \\n c(\\\"Name\\\",\\\"Description\\\",\\\"Low\\\",\\\"High\\\",\\\"Time\\\",\\\"%\\\");\\n}\\n\\n# column units, if using metrics then best\\n# to use metricunit() function to get correct string\\n# for locale and metric\\\/imperial\\nunits {\\n\\n c(\\\"\\\",\\n \\\"\\\",\\n zones(power,units),\\n zones(power,units), \\\"\\\", \\\"\\\");\\n}\\n\\n# values to display as doubles or strings\\n# if using metrics always best to use asstring()\\n# to convert correctly with dp, metric\\\/imperial\\n# or specific formats eg. rowing pace xx\\\/500m\\nvalues { \\n\\n c( zones(power,name),\\n zones(power,description),\\n zones(power,low),\\n zones(power,high),\\n zones(power,time),\\n zones(power,percent));\\n} \\n\\n}\\n \",\"datafilter\":\"filter: \",\"name\":\"Power Zones\" },\n { \"type\":107,\"span\":1,\"deep\":8,\"column\":2,\"order\":11,\"program\":\"{ weeks <- (daterange(stop)-daterange(start))\\\/7; round(10*sum(metrics(Duration)\\\/3600)\\\/weeks)\\\/10; } \",\"units\":\"hours\",\"istime\":0,\"start\":0,\"stop\":15,\"datafilter\":\"search: \",\"name\":\"Weekly Hours\" },\n { \"type\":101,\"span\":1,\"deep\":8,\"column\":3,\"order\":115,\"symbol\":\"coggan_tss\",\"datafilter\":\" \",\"name\":\"Total TSS\" },\n { \"type\":103,\"span\":1,\"deep\":15,\"column\":3,\"order\":153,\"series\":10,\"polarized\":0,\"datafilter\":\" \",\"name\":\"Power Zones\" },\n { \"type\":109,\"span\":1,\"deep\":13,\"column\":3,\"order\":154,\"symbol\":\"workout_time\",\"meta\":\"Workout Code\",\"datafilter\":\"search: \",\"name\":\"Workout\" },\n { \"type\":101,\"span\":1,\"deep\":5,\"column\":3,\"order\":265,\"symbol\":\"workout_time\",\"datafilter\":\"search: \",\"name\":\"Workout Time\" },\n { \"type\":107,\"span\":1,\"deep\":5,\"column\":3,\"order\":357,\"program\":\"{ round(max(pmc(BikeStress, lts))); } \",\"units\":\"stress\",\"istime\":0,\"start\":0,\"stop\":125,\"datafilter\":\"search: \",\"name\":\"Peak CTL\" },\n { \"type\":107,\"span\":1,\"deep\":8,\"column\":4,\"order\":807,\"program\":\"{ \\nround(max(banister(BikeScore, Power_Index,cp)));\\n} \",\"units\":\"watts\",\"istime\":0,\"start\":0,\"stop\":360,\"datafilter\":\"search: \",\"name\":\"Peak CP Estimate \" },\n { \"type\":113,\"span\":2,\"deep\":24,\"column\":4,\"order\":825,\"settings\":\"{ \\\"title\\\": \\\" \\\",\\n\\\"description\\\": \\\" \\\",\\n\\\"type\\\": 2,\\n\\\"animate\\\": false,\\n\\\"legendpos\\\": 2,\\n\\\"stack\\\": false,\\n\\\"orientation\\\": 2,\\n\\\"scale\\\": 2.5,\\n\\\"SERIES\\\": [\\n{ \\\"name\\\": \\\"IF \\\", \\\"group\\\": \\\" \\\", \\\"xname\\\": \\\"TSB \\\", \\\"yname\\\": \\\"IF \\\", \\\"program\\\": \\\"{:sl:n finalise {:sl:n # we just fetch metrics at the end :sl:n dates <- metrics(date);:sl:n pmcdates <- pmc(BikeStress, date);:sl:n indexes <- match(dates, pmcdates);:sl:n xx <- pmc(BikeStress, sb)[indexes];:sl:n yy <- metrics(BikeIntensity);:sl:n ff <- filename();:sl:n }:sl:n:sl:n x { xx; }:sl:n y { yy; }:sl:n f { ff; }:sl:n}:sl:n \\\", \\\"line\\\": 0, \\\"symbol\\\": 1, \\\"size\\\": 20, \\\"color\\\": \\\"#ff3efc\\\", \\\"opacity\\\": 100, \\\"legend\\\": true, \\\"opengl\\\": false, \\\"datalabels\\\": false, \\\"aggregate\\\": 0, \\\"fill\\\": false},\\n{ \\\"name\\\": \\\"Tests \\\", \\\"group\\\": \\\" \\\", \\\"xname\\\": \\\"TSB \\\", \\\"yname\\\": \\\"IF \\\", \\\"program\\\": \\\"{:sl:n:sl:n calc {:sl:n dates <- metrics(date);:sl:n pmcdates <- pmc(BikeStress, date);:sl:n indexes <- match(dates, pmcdates);:sl:n xx <- pmc(BikeStress, sb)[indexes];:sl:n yy <- metrics(BikeIntensity);:sl:n ff <- filename();:sl:n }:sl:n:sl:n finalise {:sl:n # we just fetch metrics at the end :sl:n activities(:qu:tests(user,power) >0:qu:, calc());:sl:n }:sl:n:sl:n x { xx; }:sl:n y { yy; }:sl:n f { ff; }:sl:n}:sl:n \\\", \\\"line\\\": 0, \\\"symbol\\\": 1, \\\"size\\\": 20, \\\"color\\\": \\\"#010110\\\", \\\"opacity\\\": 100, \\\"legend\\\": true, \\\"opengl\\\": false, \\\"datalabels\\\": false, \\\"aggregate\\\": 0, \\\"fill\\\": false} ]\\n,\\n\\\"AXES\\\": [\\n{ \\\"name\\\": \\\"TSB \\\", \\\"type\\\": 0, \\\"orientation\\\": 1, \\\"align\\\": 1, \\\"minx\\\": -15, \\\"maxx\\\": 15, \\\"miny\\\": 0, \\\"maxy\\\": 0, \\\"smooth\\\": 0, \\\"groupby\\\": 0, \\\"visible\\\": true, \\\"fixed\\\": true, \\\"log\\\": false, \\\"minorgrid\\\": false, \\\"majorgrid\\\": true, \\\"labelcolor\\\": \\\"#55aaff\\\", \\\"axiscolor\\\": \\\"#55aaff\\\"},\\n{ \\\"name\\\": \\\"IF \\\", \\\"type\\\": 0, \\\"orientation\\\": 2, \\\"align\\\": 1, \\\"minx\\\": 0, \\\"maxx\\\": 0, \\\"miny\\\": 0.4, \\\"maxy\\\": 1.2, \\\"smooth\\\": 0, \\\"groupby\\\": 0, \\\"visible\\\": true, \\\"fixed\\\": true, \\\"log\\\": false, \\\"minorgrid\\\": false, \\\"majorgrid\\\": true, \\\"labelcolor\\\": \\\"#ff3efc\\\", \\\"axiscolor\\\": \\\"#ff3efc\\\"} ]\\n} \",\"datafilter\":\" \",\"name\":\"Scatter PMC \" },\n { \"type\":112,\"span\":2,\"deep\":16,\"column\":4,\"order\":826,\"program\":\"{\\n\\n# column names, if using metrics then best\\n# to use name() to get correct name for locale\\n# otherwise it won't translate to other languages\\nnames {\\n metricname(date,\\n Duration,\\n Distance,\\n Work,\\n W'_Work,\\n 30_min_Peak_Power);\\n}\\n\\n# column units, if using metrics then best\\n# to use unit() function to get correct string\\n# for locale and metric\\\/imperial\\nunits {\\n metricunit(date,\\n Duration,\\n Distance,\\n Work,\\n W'_Work,\\n 30_min_Peak_Power);\\n}\\n\\n# values to display as doubles or strings\\n# if using metrics always best to use asstring()\\n# to convert correctly with dp, metric\\\/imperial\\n# or specific formats eg. rowing pace xx\\\/500m\\nvalues { \\n c(metricstrings(date),\\n metricstrings(Duration),\\n metricstrings(Distance),\\n metricstrings(Work),\\n metricstrings(W'_Work),\\n metricstrings(30_min_Peak_Power)); \\n} \\n\\nf {\\n filename();\\n}\\n\\n} \",\"datafilter\":\"filter:tests(user,power) \",\"name\":\"Performance tests\" },\n { \"type\":112,\"span\":2,\"deep\":32,\"column\":4,\"order\":827,\"program\":\"{\\n\\n# column names, if using metrics then best\\n# to use metricname() to get correct name for locale\\n# otherwise it won't translate to other languages\\nnames {\\n c(\\\"Date\\\", \\\"Daily\\\", \\\"Chronic\\\", \\\"Acute\\\", \\\"Stress\\\");\\n}\\n\\n# column units, if using metrics then best\\n# to use metricunit() function to get correct string\\n# for locale and metric\\\/imperial\\nunits {\\n c(\\\"\\\",\\\"TSS\\\", \\\"Load\\\",\\\"Load\\\",\\\"Balance\\\");\\n}\\n\\n# values to display as doubles or strings\\n# if using metrics always best to use asstring()\\n# to convert correctly with dp, metric\\\/imperial\\n# or specific formats eg. rowing pace xx\\\/500m\\nvalues { \\n c(datestring(pmc(BikeStress,date)),\\n round(pmc(BikeStress,stress)),\\n round(pmc(BikeStress,lts)),\\n round(pmc(BikeStress,sts)),\\n round(pmc(BikeStress,sb)));\\n\\n} \\n\\n\\n} \",\"datafilter\":\"search: \",\"name\":\"PMC Data\" },\n { \"type\":107,\"span\":1,\"deep\":8,\"column\":5,\"order\":1,\"program\":\"{ round(max(estimates(cp3,w')\\\/1000)*10)\\\/10; } \",\"units\":\"kJ\",\"istime\":0,\"start\":0,\"stop\":25,\"datafilter\":\" \",\"name\":\"Peak W' Estimate \" }\n ]\n}\n ",
"__LAST__":"1"
}
}
}