Private
Public Access
1
0

Merge branch 'feature/cleanup' into develop

This commit is contained in:
Sander Roosendaal
2017-01-16 17:36:31 +01:00
54 changed files with 470 additions and 3111 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -1,110 +0,0 @@
TimeStamp (sec), activityIdx, lapIdx, pointIdx, ElapsedTime (sec), Horizontal (meters), Stroke500mPace (sec/500m), Cadence (stokes/min), HRCur (bpm), Power (watts), Calories (kCal), Speed (m/sec), StrokeCount, StrokeDistance (meters), DriveLength (meters), DriveTime (ms), StrokeRecoveryTime (ms), WorkPerStroke (joules), AverageDriveForce (lbs), PeakDriveForce (lbs), DragFactor
1461004356.11047, 0, 0, 0, 1.34, 5.8, 124.45, 0, 112, 184, 0, 4.033, 1, 2.67, 1.18, 660, 430, 0, 148.4, 228.3, 0
1461004357.37363, 0, 0, 1, 2.64, 11.9, 107.86, 52, 114, 283, 0, 4.657, 2, 5.4, 1.18, 530, 570, 0, 134.4, 209.7, 110
1461004358.86966, 0, 0, 2, 4.11, 19.5, 102.66, 46, 118, 328, 1, 4.894, 3, 6.36, 1.33, 520, 730, 0, 137.8, 226.2, 110
1461004360.40359, 0, 0, 3, 5.69, 28, 95.64, 40, 120, 406, 1, 5.254, 4, 7.8, 1.45, 540, 830, 0, 139, 219.9, 111
1461004361.99023, 0, 0, 4, 7.26, 36.7, 92.38, 38, 125, 451, 2, 5.441, 5, 8.59, 1.48, 530, 820, 0, 141, 223.7, 110
1461004363.64047, 0, 0, 5, 8.89, 45.7, 90.84, 38, 132, 475, 3, 5.534, 6, 8.66, 1.48, 540, 890, 0, 132.9, 245.3, 110
1461004365.29068, 0, 0, 6, 10.55, 54.7, 91.31, 37, 135, 467, 4, 5.505, 7, 8.86, 1.42, 510, 930, 0, 138.6, 222.8, 110
1461004367.00093, 0, 0, 7, 12.24, 64, 92.1, 36, 141, 455, 5, 5.458, 8, 9.21, 1.48, 540, 930, 0, 134.8, 226.9, 110
1461004368.65075, 0, 0, 8, 13.93, 73.1, 92.54, 36, 144, 455, 5, 5.432, 9, 9.14, 1.48, 550, 930, 0, 131.2, 234.4, 110
1461004370.51044, 0, 0, 9, 15.79, 82.9, 93.32, 36, 152, 438, 6, 5.386, 10, 9.07, 1.45, 550, 1110, 0, 124.9, 211.2, 110
1461004372.4003, 0, 0, 10, 17.68, 92.6, 95.87, 32, 156, 403, 7, 5.242, 11, 9.79, 1.42, 550, 1150, 0, 123.9, 209.1, 110
1461004374.35136, 0, 0, 11, 19.63, 102.5, 98.2, 31, 160, 375, 8, 5.117, 12, 9.86, 1.45, 570, 1160, 0, 121.1, 210.9, 110
1461004376.27099, 0, 0, 12, 21.55, 112.1, 99.61, 31, 164, 359, 9, 5.044, 13, 9.79, 1.45, 590, 1150, 0, 118.8, 219.1, 110
1461004378.07035, 0, 0, 13, 23.34, 121.2, 100.17, 31, 166, 353, 10, 5.016, 14, 9.79, 1.48, 590, 1150, 0, 122.6, 208.4, 110
1461004380.05067, 0, 0, 14, 25.32, 131.2, 99.45, 32, 168, 361, 10, 5.052, 15, 9.51, 1.45, 580, 1090, 0, 126.1, 218, 110
1461004382.00372, 0, 0, 15, 27.24, 140.9, 98.98, 32, 169, 366, 11, 5.076, 16, 9.51, 1.45, 580, 1130, 0, 119.5, 204.9, 110
1461004383.89027, 0, 0, 16, 29.16, 150.5, 99.84, 31, 171, 357, 12, 5.032, 17, 9.58, 1.42, 570, 1140, 0, 120.1, 213.3, 110
1461004385.68965, 0, 0, 17, 30.96, 159.6, 100.5, 31, 172, 350, 13, 4.999, 18, 9.79, 1.51, 610, 1140, 0, 117.3, 210.8, 110
1461004387.79028, 0, 0, 18, 33.05, 169.9, 100.34, 32, 173, 352, 14, 5.007, 19, 9.44, 1.45, 580, 1190, 0, 119.7, 198.6, 110
1461004389.74033, 0, 0, 19, 35.02, 179.8, 100.57, 30, 174, 349, 15, 4.996, 20, 9.87, 1.45, 580, 1180, 0, 126.1, 212.6, 110
1461004391.6934, 0, 0, 20, 36.97, 189.6, 100.2, 30, 174, 353, 15, 5.014, 21, 9.86, 1.45, 580, 1160, 0, 124.7, 204.5, 110
1461004393.70408, 0, 0, 21, 38.96, 199.5, 100.1, 31, 175, 354, 16, 5.019, 22, 9.78, 1.45, 580, 1190, 0, 120.2, 202.3, 110
1461004395.67953, 0, 0, 22, 40.9, 209.2, 100.99, 30, 175, 345, 17, 4.975, 23, 9.85, 1.45, 590, 1160, 0, 119.7, 197.6, 110
1461004397.41974, 0, 1, 0, 42.69, 218.2, 101.05, 31, 176, 344, 18, 4.972, 24, 9.71, 1.45, 600, 1160, 0, 121.1, 202.9, 110
1461004399.46009, 0, 1, 1, 44.72, 228.3, 100.13, 32, 176, 354, 19, 5.018, 25, 9.51, 1.45, 580, 1130, 0, 121.8, 207.3, 110
1461004401.3506, 0, 1, 2, 46.62, 237.8, 100.06, 31, 176, 355, 19, 5.021, 26, 9.64, 1.45, 580, 1110, 0, 116.5, 207.4, 110
1461004403.29953, 0, 1, 3, 48.42, 246.9, 100.65, 31, 176, 348, 20, 4.992, 27, 9.57, 1.48, 600, 1110, 0, 123, 211.5, 110
1461004405.22022, 0, 1, 4, 50.47, 257.2, 99.69, 32, 177, 359, 21, 5.04, 28, 9.58, 1.45, 580, 1130, 0, 124.3, 208.8, 110
1461004407.19972, 0, 1, 5, 52.39, 266.9, 99.47, 31, 177, 361, 22, 5.051, 29, 9.65, 1.45, 580, 1150, 0, 120.4, 207.4, 110
1461004408.9101, 0, 1, 6, 54.17, 276, 99.87, 31, 177, 357, 23, 5.031, 30, 9.72, 1.45, 580, 1150, 0, 126.3, 210.5, 110
1461004411.00986, 0, 1, 7, 56.23, 286.3, 99.22, 32, 177, 364, 23, 5.064, 31, 9.51, 1.42, 560, 1170, 0, 125.6, 215.2, 110
1461004412.77946, 0, 1, 8, 58.04, 295.5, 99.56, 31, 177, 360, 24, 5.047, 32, 9.86, 1.45, 580, 1170, 0, 121.8, 214.4, 110
1461004414.82028, 0, 1, 9, 60.1, 305.8, 99.94, 31, 178, 356, 25, 5.027, 33, 9.71, 1.45, 600, 1160, 0, 120.6, 210.2, 110
1461004416.8312, 0, 1, 10, 62.04, 315.5, 99.94, 31, 178, 356, 26, 5.028, 34, 9.78, 1.45, 580, 1160, 0, 124, 222.3, 110
1461004418.72014, 0, 1, 11, 63.99, 325.3, 99.87, 31, 178, 357, 27, 5.031, 35, 9.78, 1.45, 580, 1140, 0, 123.7, 208.6, 110
1461004420.67038, 0, 1, 12, 65.93, 335.1, 99.71, 31, 178, 358, 28, 5.039, 36, 9.79, 1.48, 590, 1140, 0, 121.8, 218.6, 109
1461004422.49949, 0, 1, 13, 67.76, 344.4, 99.81, 31, 179, 357, 28, 5.034, 37, 9.71, 1.45, 590, 1140, 0, 121.9, 212.9, 110
1461004424.56946, 0, 1, 14, 69.81, 354.6, 99.91, 31, 179, 356, 29, 5.029, 38, 9.79, 1.42, 570, 1170, 0, 122.3, 208.7, 110
1461004426.45946, 0, 1, 15, 71.72, 364.2, 100.48, 31, 179, 350, 30, 5, 39, 9.78, 1.45, 580, 1120, 0, 122.7, 209, 110
1461004428.22943, 0, 1, 16, 73.51, 373.3, 99.94, 32, 179, 356, 31, 5.028, 40, 9.57, 1.45, 580, 1120, 0, 123.5, 229.9, 110
1461004430.30156, 0, 1, 17, 75.51, 383.3, 99.42, 32, 179, 362, 32, 5.054, 41, 9.5, 1.42, 560, 1120, 0, 124.8, 208.4, 109
1461004432.16011, 0, 1, 18, 77.41, 393, 99.26, 32, 179, 363, 32, 5.062, 42, 9.63, 1.45, 580, 1130, 0, 122.9, 226.6, 110
1461004434.10936, 0, 1, 19, 79.34, 402.7, 99.5, 32, 179, 361, 33, 5.05, 43, 9.56, 1.42, 560, 1150, 0, 124.9, 218.3, 110
1461004436.0894, 0, 2, 0, 81.3, 412.5, 99.64, 31, 179, 359, 34, 5.043, 44, 9.71, 1.42, 570, 1180, 0, 122.6, 212.9, 109
1461004437.86025, 0, 2, 1, 83.1, 421.6, 100.31, 31, 180, 352, 35, 5.009, 45, 9.84, 1.45, 600, 1180, 0, 120.4, 215.3, 110
1461004439.89998, 0, 2, 2, 85.14, 431.8, 100.11, 31, 180, 354, 36, 5.019, 46, 9.78, 1.48, 590, 1110, 0, 123.2, 213.3, 110
1461004441.81943, 0, 2, 3, 87.07, 441.6, 99.56, 32, 180, 360, 37, 5.047, 47, 9.51, 1.42, 570, 1160, 0, 123.4, 209.6, 110
1461004443.77011, 0, 2, 4, 89.02, 451.3, 99.72, 31, 180, 358, 37, 5.038, 48, 9.79, 1.45, 590, 1160, 0, 121.4, 212.2, 110
1461004445.59931, 0, 2, 5, 90.83, 460.6, 100.05, 31, 180, 355, 38, 5.022, 49, 9.71, 1.42, 570, 1160, 0, 126.1, 214.7, 110
1461004447.6099, 0, 2, 6, 92.85, 470.7, 99.57, 31, 180, 360, 39, 5.046, 50, 9.78, 1.45, 580, 1120, 0, 126, 216.2, 110
1461004449.55928, 0, 2, 7, 94.78, 480.5, 99.2, 32, 181, 364, 40, 5.065, 51, 9.63, 1.45, 590, 1150, 0, 122, 229.7, 109
1461004451.4801, 0, 2, 8, 96.72, 490.3, 99.47, 31, 181, 361, 41, 5.051, 52, 9.7, 1.42, 560, 1170, 0, 124.9, 221.2, 110
1461004453.33954, 0, 2, 9, 98.57, 499.5, 100.01, 31, 181, 355, 42, 5.024, 53, 9.85, 1.45, 580, 1170, 0, 119.2, 209, 109
1461004455.4099, 0, 2, 10, 100.66, 509.9, 100.76, 31, 181, 347, 42, 4.986, 54, 9.77, 1.45, 590, 1180, 0, 121.6, 224.3, 110
1461004457.36002, 0, 2, 11, 102.6, 519.6, 100.59, 31, 181, 349, 43, 4.995, 55, 9.78, 1.42, 580, 1160, 0, 123.1, 212.1, 109
1461004459.31064, 0, 2, 12, 104.58, 529.5, 100.3, 31, 181, 352, 44, 5.009, 56, 9.77, 1.45, 580, 1190, 0, 122.6, 216.3, 109
1461004461.16922, 0, 2, 13, 106.42, 538.7, 100.5, 30, 181, 350, 45, 4.999, 57, 9.92, 1.45, 580, 1190, 0, 120.4, 213.9, 110
1461004463.26918, 0, 2, 14, 108.49, 549, 100.79, 31, 181, 347, 46, 4.985, 58, 9.71, 1.45, 580, 1150, 0, 121.7, 216.6, 110
1461004465.10101, 0, 2, 15, 110.36, 558.4, 100.66, 31, 181, 348, 46, 4.991, 59, 9.63, 1.42, 570, 1110, 0, 120.4, 218.1, 109
1461004467.01999, 0, 2, 16, 112.26, 567.8, 100.59, 32, 181, 349, 47, 4.995, 60, 9.41, 1.42, 570, 1120, 0, 121.8, 227.8, 109
1461004468.90954, 0, 2, 17, 114.14, 577.3, 100.48, 31, 181, 350, 48, 5, 61, 9.55, 1.45, 600, 1090, 0, 117.8, 218, 109
1461004470.80092, 0, 2, 18, 116.06, 586.9, 100.11, 32, 181, 354, 49, 5.019, 62, 9.49, 1.48, 610, 1110, 0, 115, 209.3, 110
1461004472.72009, 0, 2, 19, 117.97, 596.5, 100.44, 32, 181, 351, 50, 5.002, 63, 9.49, 1.45, 580, 1130, 0, 120.6, 209.1, 110
1461004474.60979, 0, 2, 20, 119.87, 606, 100.28, 31, 182, 352, 50, 5.01, 64, 9.57, 1.42, 570, 1120, 0, 120, 217.9, 109
1461004476.4385, 0, 3, 0, 121.65, 614.9, 100.89, 32, 181, 346, 51, 4.98, 65, 9.41, 1.39, 570, 1120, 0, 118.6, 211.9, 110
1461004478.47986, 0, 3, 1, 123.73, 625.1, 101.01, 31, 182, 345, 52, 4.974, 66, 9.71, 1.48, 600, 1130, 0, 115.3, 219.1, 110
1461004480.27929, 0, 3, 2, 125.52, 634.1, 101.24, 31, 182, 342, 53, 4.962, 67, 9.57, 1.45, 590, 1130, 0, 121.4, 219, 110
1461004482.37966, 0, 3, 3, 127.6, 644.4, 100.67, 32, 181, 348, 54, 4.991, 68, 9.42, 1.42, 570, 1190, 0, 118.7, 218.1, 109
1461004484.29991, 0, 3, 4, 129.56, 654.1, 101.34, 30, 181, 341, 54, 4.958, 69, 9.85, 1.45, 590, 1140, 0, 121.6, 220.8, 110
1461004486.16014, 0, 3, 5, 131.41, 663.4, 100.79, 31, 181, 347, 55, 4.985, 70, 9.57, 1.42, 570, 1090, 0, 122.9, 210.4, 110
1461004488.07993, 0, 3, 6, 133.33, 673, 100.32, 32, 181, 352, 56, 5.008, 71, 9.35, 1.42, 570, 1140, 0, 118.5, 206.8, 109
1461004490.03001, 0, 3, 7, 135.27, 682.6, 100.98, 31, 181, 345, 57, 4.975, 72, 9.56, 1.42, 580, 1150, 0, 116.9, 218.1, 109
1461004491.97913, 0, 3, 8, 137.22, 692.3, 101.5, 31, 181, 340, 58, 4.95, 73, 9.63, 1.45, 590, 1150, 0, 118.7, 221.9, 110
1461004493.77931, 0, 3, 9, 138.99, 701.1, 101.41, 31, 181, 341, 58, 4.954, 74, 9.57, 1.42, 580, 1150, 0, 119, 205.9, 110
1461004495.78987, 0, 3, 10, 141.04, 711.2, 101.29, 31, 181, 342, 59, 4.96, 75, 9.64, 1.48, 600, 1100, 0, 116.2, 222.9, 110
1461004497.64987, 0, 3, 11, 142.92, 720.6, 101.08, 32, 181, 344, 60, 4.971, 76, 9.36, 1.42, 590, 1090, 0, 120.4, 210.2, 110
1461004499.56987, 0, 3, 12, 144.81, 730, 100.32, 32, 182, 352, 61, 5.008, 77, 9.35, 1.42, 570, 1130, 0, 119.2, 218.4, 109
1461004501.45977, 0, 3, 13, 146.71, 739.5, 100.77, 32, 181, 347, 61, 4.986, 78, 9.49, 1.42, 570, 1110, 0, 119.5, 210.1, 109
1461004503.31974, 0, 3, 14, 148.59, 748.9, 100.91, 32, 182, 346, 62, 4.979, 79, 9.41, 1.42, 590, 1100, 0, 117.6, 217.4, 109
1461004505.20973, 0, 3, 15, 150.44, 758.1, 100.85, 32, 181, 346, 63, 4.982, 80, 9.34, 1.42, 590, 1090, 0, 114.9, 212.3, 109
1461004507.04, 0, 3, 16, 152.26, 767.2, 100.96, 32, 181, 345, 64, 4.976, 81, 9.27, 1.42, 580, 1030, 0, 118.6, 222, 109
1461004508.78319, 0, 3, 17, 154, 775.9, 100.29, 33, 182, 352, 65, 5.01, 82, 9.06, 1.42, 570, 1030, 0, 116.3, 208.4, 109
1461004510.72907, 0, 3, 18, 155.96, 785.7, 100.51, 32, 182, 350, 65, 4.999, 83, 9.42, 1.48, 610, 1050, 0, 114.2, 220.8, 110
1461004512.58978, 0, 3, 19, 157.79, 794.9, 100.32, 33, 182, 352, 66, 5.008, 84, 9.07, 1.39, 560, 1070, 0, 116.1, 211.2, 109
1461004514.42009, 0, 3, 20, 159.65, 804, 101.03, 32, 182, 344, 67, 4.973, 85, 9.27, 1.45, 590, 1040, 0, 112.5, 199.5, 109
1461004516.18978, 0, 4, 0, 161.46, 813, 101.62, 33, 182, 338, 68, 4.944, 86, 8.98, 1.39, 580, 1050, 0, 111.6, 200.2, 109
1461004518.07996, 0, 4, 1, 163.33, 822.2, 101.73, 33, 181, 337, 68, 4.938, 87, 8.99, 1.39, 560, 1100, 0, 114.6, 206.4, 109
1461004519.90999, 0, 4, 2, 165.15, 831.2, 102.27, 32, 181, 332, 69, 4.912, 88, 9.2, 1.39, 570, 1050, 0, 115.6, 204.9, 110
1461004521.74184, 0, 4, 3, 166.98, 840.2, 101.9, 32, 182, 336, 70, 4.93, 89, 9.14, 1.45, 590, 1010, 0, 110, 200, 110
1461004523.59979, 0, 4, 4, 168.81, 849.2, 101.98, 34, 182, 335, 71, 4.926, 90, 8.71, 1.36, 550, 1070, 0, 114.6, 206, 110
1461004525.36992, 0, 4, 5, 170.62, 858, 102.27, 32, 181, 332, 71, 4.912, 91, 9.07, 1.39, 570, 1040, 0, 111.7, 185.3, 109
1461004527.19977, 0, 4, 6, 172.43, 866.9, 102.47, 33, 181, 330, 72, 4.902, 92, 8.92, 1.39, 570, 1040, 0, 114, 195, 109
1461004529.06276, 0, 4, 7, 174.32, 876.2, 102.31, 33, 181, 332, 73, 4.91, 93, 8.99, 1.42, 580, 1080, 0, 110.1, 199.7, 110
1461004530.82905, 0, 4, 8, 176.02, 884.6, 102.69, 32, 181, 328, 74, 4.892, 94, 9.14, 1.42, 580, 1080, 0, 113.9, 216.3, 110
1461004532.75083, 0, 4, 9, 177.99, 894.1, 102.44, 33, 181, 330, 74, 4.904, 95, 8.99, 1.42, 580, 1060, 0, 108.7, 196, 109
1461004534.39983, 0, 4, 10, 179.66, 902.4, 102.85, 32, 181, 326, 75, 4.884, 96, 9.05, 1.42, 580, 1060, 0, 114.9, 199.6, 109
1461004536.32286, 0, 4, 11, 181.59, 911.9, 101.55, 34, 182, 339, 76, 4.947, 97, 8.85, 1.45, 590, 1000, 0, 110.6, 199, 110
1461004538.06289, 0, 4, 12, 183.32, 920.5, 101.39, 34, 181, 341, 76, 4.955, 98, 8.78, 1.39, 580, 970, 0, 109.7, 195.2, 110
1461004539.89296, 0, 4, 13, 185.11, 929.4, 101.35, 34, 181, 341, 77, 4.957, 99, 8.71, 1.42, 570, 1010, 0, 112.5, 207.2, 109
1461004541.6899, 0, 4, 14, 186.8, 937.8, 101.24, 33, 181, 342, 78, 4.962, 100, 8.92, 1.42, 570, 1010, 0, 112.9, 206.9, 109
1461004543.39974, 0, 4, 15, 188.64, 946.9, 101.37, 34, 181, 341, 79, 4.956, 101, 8.85, 1.39, 560, 940, 0, 112.1, 200.7, 110
1461004545.11348, 0, 4, 16, 190.34, 955.4, 101, 35, 181, 345, 79, 4.975, 102, 8.42, 1.36, 560, 970, 0, 113.8, 187.5, 110
1461004546.85166, 0, 4, 17, 192.1, 964.1, 100.78, 35, 180, 347, 80, 4.985, 103, 8.57, 1.36, 550, 1010, 0, 109.9, 194.7, 110
1461004548.65002, 0, 4, 18, 193.91, 973, 101.83, 34, 181, 336, 81, 4.934, 104, 8.78, 1.39, 580, 1010, 0, 107.6, 188.8, 110
1461004550.35928, 0, 4, 19, 195.6, 981.3, 102.6, 34, 181, 329, 81, 4.896, 105, 8.71, 1.36, 560, 1010, 0, 108.9, 183.6, 110
1461004552.30971, 0, 4, 20, 197.53, 990.5, 103.54, 33, 181, 320, 82, 4.852, 106, 8.86, 1.36, 560, 1040, 0, 102.4, 187.2, 110
1461004554.19972, 0, 4, 21, 199.44, 999.5, 105.35, 33, 180, 304, 83, 4.768, 107, 8.7, 1.33, 590, 1110, 0, 100, 181.7, 109
1461004554.35294, 0, 4, 22, 199.56, 1000, 105.35, 33, 180, 304, 83, 4.768, 107, 8.7, 1.33, 590, 1110, 0, 100, 181.7, 109
1461004556.03066, 0, 6, 0, 199.57, 1000, 105.35, 33, 180, 304, 83, 4.768, 107, 8.7, 1.33, 590, 1110, 0, 100, 181.7, 109
1 TimeStamp (sec) activityIdx lapIdx pointIdx ElapsedTime (sec) Horizontal (meters) Stroke500mPace (sec/500m) Cadence (stokes/min) HRCur (bpm) Power (watts) Calories (kCal) Speed (m/sec) StrokeCount StrokeDistance (meters) DriveLength (meters) DriveTime (ms) StrokeRecoveryTime (ms) WorkPerStroke (joules) AverageDriveForce (lbs) PeakDriveForce (lbs) DragFactor
2 1461004356.11047 0 0 0 1.34 5.8 124.45 0 112 184 0 4.033 1 2.67 1.18 660 430 0 148.4 228.3 0
3 1461004357.37363 0 0 1 2.64 11.9 107.86 52 114 283 0 4.657 2 5.4 1.18 530 570 0 134.4 209.7 110
4 1461004358.86966 0 0 2 4.11 19.5 102.66 46 118 328 1 4.894 3 6.36 1.33 520 730 0 137.8 226.2 110
5 1461004360.40359 0 0 3 5.69 28 95.64 40 120 406 1 5.254 4 7.8 1.45 540 830 0 139 219.9 111
6 1461004361.99023 0 0 4 7.26 36.7 92.38 38 125 451 2 5.441 5 8.59 1.48 530 820 0 141 223.7 110
7 1461004363.64047 0 0 5 8.89 45.7 90.84 38 132 475 3 5.534 6 8.66 1.48 540 890 0 132.9 245.3 110
8 1461004365.29068 0 0 6 10.55 54.7 91.31 37 135 467 4 5.505 7 8.86 1.42 510 930 0 138.6 222.8 110
9 1461004367.00093 0 0 7 12.24 64 92.1 36 141 455 5 5.458 8 9.21 1.48 540 930 0 134.8 226.9 110
10 1461004368.65075 0 0 8 13.93 73.1 92.54 36 144 455 5 5.432 9 9.14 1.48 550 930 0 131.2 234.4 110
11 1461004370.51044 0 0 9 15.79 82.9 93.32 36 152 438 6 5.386 10 9.07 1.45 550 1110 0 124.9 211.2 110
12 1461004372.4003 0 0 10 17.68 92.6 95.87 32 156 403 7 5.242 11 9.79 1.42 550 1150 0 123.9 209.1 110
13 1461004374.35136 0 0 11 19.63 102.5 98.2 31 160 375 8 5.117 12 9.86 1.45 570 1160 0 121.1 210.9 110
14 1461004376.27099 0 0 12 21.55 112.1 99.61 31 164 359 9 5.044 13 9.79 1.45 590 1150 0 118.8 219.1 110
15 1461004378.07035 0 0 13 23.34 121.2 100.17 31 166 353 10 5.016 14 9.79 1.48 590 1150 0 122.6 208.4 110
16 1461004380.05067 0 0 14 25.32 131.2 99.45 32 168 361 10 5.052 15 9.51 1.45 580 1090 0 126.1 218 110
17 1461004382.00372 0 0 15 27.24 140.9 98.98 32 169 366 11 5.076 16 9.51 1.45 580 1130 0 119.5 204.9 110
18 1461004383.89027 0 0 16 29.16 150.5 99.84 31 171 357 12 5.032 17 9.58 1.42 570 1140 0 120.1 213.3 110
19 1461004385.68965 0 0 17 30.96 159.6 100.5 31 172 350 13 4.999 18 9.79 1.51 610 1140 0 117.3 210.8 110
20 1461004387.79028 0 0 18 33.05 169.9 100.34 32 173 352 14 5.007 19 9.44 1.45 580 1190 0 119.7 198.6 110
21 1461004389.74033 0 0 19 35.02 179.8 100.57 30 174 349 15 4.996 20 9.87 1.45 580 1180 0 126.1 212.6 110
22 1461004391.6934 0 0 20 36.97 189.6 100.2 30 174 353 15 5.014 21 9.86 1.45 580 1160 0 124.7 204.5 110
23 1461004393.70408 0 0 21 38.96 199.5 100.1 31 175 354 16 5.019 22 9.78 1.45 580 1190 0 120.2 202.3 110
24 1461004395.67953 0 0 22 40.9 209.2 100.99 30 175 345 17 4.975 23 9.85 1.45 590 1160 0 119.7 197.6 110
25 1461004397.41974 0 1 0 42.69 218.2 101.05 31 176 344 18 4.972 24 9.71 1.45 600 1160 0 121.1 202.9 110
26 1461004399.46009 0 1 1 44.72 228.3 100.13 32 176 354 19 5.018 25 9.51 1.45 580 1130 0 121.8 207.3 110
27 1461004401.3506 0 1 2 46.62 237.8 100.06 31 176 355 19 5.021 26 9.64 1.45 580 1110 0 116.5 207.4 110
28 1461004403.29953 0 1 3 48.42 246.9 100.65 31 176 348 20 4.992 27 9.57 1.48 600 1110 0 123 211.5 110
29 1461004405.22022 0 1 4 50.47 257.2 99.69 32 177 359 21 5.04 28 9.58 1.45 580 1130 0 124.3 208.8 110
30 1461004407.19972 0 1 5 52.39 266.9 99.47 31 177 361 22 5.051 29 9.65 1.45 580 1150 0 120.4 207.4 110
31 1461004408.9101 0 1 6 54.17 276 99.87 31 177 357 23 5.031 30 9.72 1.45 580 1150 0 126.3 210.5 110
32 1461004411.00986 0 1 7 56.23 286.3 99.22 32 177 364 23 5.064 31 9.51 1.42 560 1170 0 125.6 215.2 110
33 1461004412.77946 0 1 8 58.04 295.5 99.56 31 177 360 24 5.047 32 9.86 1.45 580 1170 0 121.8 214.4 110
34 1461004414.82028 0 1 9 60.1 305.8 99.94 31 178 356 25 5.027 33 9.71 1.45 600 1160 0 120.6 210.2 110
35 1461004416.8312 0 1 10 62.04 315.5 99.94 31 178 356 26 5.028 34 9.78 1.45 580 1160 0 124 222.3 110
36 1461004418.72014 0 1 11 63.99 325.3 99.87 31 178 357 27 5.031 35 9.78 1.45 580 1140 0 123.7 208.6 110
37 1461004420.67038 0 1 12 65.93 335.1 99.71 31 178 358 28 5.039 36 9.79 1.48 590 1140 0 121.8 218.6 109
38 1461004422.49949 0 1 13 67.76 344.4 99.81 31 179 357 28 5.034 37 9.71 1.45 590 1140 0 121.9 212.9 110
39 1461004424.56946 0 1 14 69.81 354.6 99.91 31 179 356 29 5.029 38 9.79 1.42 570 1170 0 122.3 208.7 110
40 1461004426.45946 0 1 15 71.72 364.2 100.48 31 179 350 30 5 39 9.78 1.45 580 1120 0 122.7 209 110
41 1461004428.22943 0 1 16 73.51 373.3 99.94 32 179 356 31 5.028 40 9.57 1.45 580 1120 0 123.5 229.9 110
42 1461004430.30156 0 1 17 75.51 383.3 99.42 32 179 362 32 5.054 41 9.5 1.42 560 1120 0 124.8 208.4 109
43 1461004432.16011 0 1 18 77.41 393 99.26 32 179 363 32 5.062 42 9.63 1.45 580 1130 0 122.9 226.6 110
44 1461004434.10936 0 1 19 79.34 402.7 99.5 32 179 361 33 5.05 43 9.56 1.42 560 1150 0 124.9 218.3 110
45 1461004436.0894 0 2 0 81.3 412.5 99.64 31 179 359 34 5.043 44 9.71 1.42 570 1180 0 122.6 212.9 109
46 1461004437.86025 0 2 1 83.1 421.6 100.31 31 180 352 35 5.009 45 9.84 1.45 600 1180 0 120.4 215.3 110
47 1461004439.89998 0 2 2 85.14 431.8 100.11 31 180 354 36 5.019 46 9.78 1.48 590 1110 0 123.2 213.3 110
48 1461004441.81943 0 2 3 87.07 441.6 99.56 32 180 360 37 5.047 47 9.51 1.42 570 1160 0 123.4 209.6 110
49 1461004443.77011 0 2 4 89.02 451.3 99.72 31 180 358 37 5.038 48 9.79 1.45 590 1160 0 121.4 212.2 110
50 1461004445.59931 0 2 5 90.83 460.6 100.05 31 180 355 38 5.022 49 9.71 1.42 570 1160 0 126.1 214.7 110
51 1461004447.6099 0 2 6 92.85 470.7 99.57 31 180 360 39 5.046 50 9.78 1.45 580 1120 0 126 216.2 110
52 1461004449.55928 0 2 7 94.78 480.5 99.2 32 181 364 40 5.065 51 9.63 1.45 590 1150 0 122 229.7 109
53 1461004451.4801 0 2 8 96.72 490.3 99.47 31 181 361 41 5.051 52 9.7 1.42 560 1170 0 124.9 221.2 110
54 1461004453.33954 0 2 9 98.57 499.5 100.01 31 181 355 42 5.024 53 9.85 1.45 580 1170 0 119.2 209 109
55 1461004455.4099 0 2 10 100.66 509.9 100.76 31 181 347 42 4.986 54 9.77 1.45 590 1180 0 121.6 224.3 110
56 1461004457.36002 0 2 11 102.6 519.6 100.59 31 181 349 43 4.995 55 9.78 1.42 580 1160 0 123.1 212.1 109
57 1461004459.31064 0 2 12 104.58 529.5 100.3 31 181 352 44 5.009 56 9.77 1.45 580 1190 0 122.6 216.3 109
58 1461004461.16922 0 2 13 106.42 538.7 100.5 30 181 350 45 4.999 57 9.92 1.45 580 1190 0 120.4 213.9 110
59 1461004463.26918 0 2 14 108.49 549 100.79 31 181 347 46 4.985 58 9.71 1.45 580 1150 0 121.7 216.6 110
60 1461004465.10101 0 2 15 110.36 558.4 100.66 31 181 348 46 4.991 59 9.63 1.42 570 1110 0 120.4 218.1 109
61 1461004467.01999 0 2 16 112.26 567.8 100.59 32 181 349 47 4.995 60 9.41 1.42 570 1120 0 121.8 227.8 109
62 1461004468.90954 0 2 17 114.14 577.3 100.48 31 181 350 48 5 61 9.55 1.45 600 1090 0 117.8 218 109
63 1461004470.80092 0 2 18 116.06 586.9 100.11 32 181 354 49 5.019 62 9.49 1.48 610 1110 0 115 209.3 110
64 1461004472.72009 0 2 19 117.97 596.5 100.44 32 181 351 50 5.002 63 9.49 1.45 580 1130 0 120.6 209.1 110
65 1461004474.60979 0 2 20 119.87 606 100.28 31 182 352 50 5.01 64 9.57 1.42 570 1120 0 120 217.9 109
66 1461004476.4385 0 3 0 121.65 614.9 100.89 32 181 346 51 4.98 65 9.41 1.39 570 1120 0 118.6 211.9 110
67 1461004478.47986 0 3 1 123.73 625.1 101.01 31 182 345 52 4.974 66 9.71 1.48 600 1130 0 115.3 219.1 110
68 1461004480.27929 0 3 2 125.52 634.1 101.24 31 182 342 53 4.962 67 9.57 1.45 590 1130 0 121.4 219 110
69 1461004482.37966 0 3 3 127.6 644.4 100.67 32 181 348 54 4.991 68 9.42 1.42 570 1190 0 118.7 218.1 109
70 1461004484.29991 0 3 4 129.56 654.1 101.34 30 181 341 54 4.958 69 9.85 1.45 590 1140 0 121.6 220.8 110
71 1461004486.16014 0 3 5 131.41 663.4 100.79 31 181 347 55 4.985 70 9.57 1.42 570 1090 0 122.9 210.4 110
72 1461004488.07993 0 3 6 133.33 673 100.32 32 181 352 56 5.008 71 9.35 1.42 570 1140 0 118.5 206.8 109
73 1461004490.03001 0 3 7 135.27 682.6 100.98 31 181 345 57 4.975 72 9.56 1.42 580 1150 0 116.9 218.1 109
74 1461004491.97913 0 3 8 137.22 692.3 101.5 31 181 340 58 4.95 73 9.63 1.45 590 1150 0 118.7 221.9 110
75 1461004493.77931 0 3 9 138.99 701.1 101.41 31 181 341 58 4.954 74 9.57 1.42 580 1150 0 119 205.9 110
76 1461004495.78987 0 3 10 141.04 711.2 101.29 31 181 342 59 4.96 75 9.64 1.48 600 1100 0 116.2 222.9 110
77 1461004497.64987 0 3 11 142.92 720.6 101.08 32 181 344 60 4.971 76 9.36 1.42 590 1090 0 120.4 210.2 110
78 1461004499.56987 0 3 12 144.81 730 100.32 32 182 352 61 5.008 77 9.35 1.42 570 1130 0 119.2 218.4 109
79 1461004501.45977 0 3 13 146.71 739.5 100.77 32 181 347 61 4.986 78 9.49 1.42 570 1110 0 119.5 210.1 109
80 1461004503.31974 0 3 14 148.59 748.9 100.91 32 182 346 62 4.979 79 9.41 1.42 590 1100 0 117.6 217.4 109
81 1461004505.20973 0 3 15 150.44 758.1 100.85 32 181 346 63 4.982 80 9.34 1.42 590 1090 0 114.9 212.3 109
82 1461004507.04 0 3 16 152.26 767.2 100.96 32 181 345 64 4.976 81 9.27 1.42 580 1030 0 118.6 222 109
83 1461004508.78319 0 3 17 154 775.9 100.29 33 182 352 65 5.01 82 9.06 1.42 570 1030 0 116.3 208.4 109
84 1461004510.72907 0 3 18 155.96 785.7 100.51 32 182 350 65 4.999 83 9.42 1.48 610 1050 0 114.2 220.8 110
85 1461004512.58978 0 3 19 157.79 794.9 100.32 33 182 352 66 5.008 84 9.07 1.39 560 1070 0 116.1 211.2 109
86 1461004514.42009 0 3 20 159.65 804 101.03 32 182 344 67 4.973 85 9.27 1.45 590 1040 0 112.5 199.5 109
87 1461004516.18978 0 4 0 161.46 813 101.62 33 182 338 68 4.944 86 8.98 1.39 580 1050 0 111.6 200.2 109
88 1461004518.07996 0 4 1 163.33 822.2 101.73 33 181 337 68 4.938 87 8.99 1.39 560 1100 0 114.6 206.4 109
89 1461004519.90999 0 4 2 165.15 831.2 102.27 32 181 332 69 4.912 88 9.2 1.39 570 1050 0 115.6 204.9 110
90 1461004521.74184 0 4 3 166.98 840.2 101.9 32 182 336 70 4.93 89 9.14 1.45 590 1010 0 110 200 110
91 1461004523.59979 0 4 4 168.81 849.2 101.98 34 182 335 71 4.926 90 8.71 1.36 550 1070 0 114.6 206 110
92 1461004525.36992 0 4 5 170.62 858 102.27 32 181 332 71 4.912 91 9.07 1.39 570 1040 0 111.7 185.3 109
93 1461004527.19977 0 4 6 172.43 866.9 102.47 33 181 330 72 4.902 92 8.92 1.39 570 1040 0 114 195 109
94 1461004529.06276 0 4 7 174.32 876.2 102.31 33 181 332 73 4.91 93 8.99 1.42 580 1080 0 110.1 199.7 110
95 1461004530.82905 0 4 8 176.02 884.6 102.69 32 181 328 74 4.892 94 9.14 1.42 580 1080 0 113.9 216.3 110
96 1461004532.75083 0 4 9 177.99 894.1 102.44 33 181 330 74 4.904 95 8.99 1.42 580 1060 0 108.7 196 109
97 1461004534.39983 0 4 10 179.66 902.4 102.85 32 181 326 75 4.884 96 9.05 1.42 580 1060 0 114.9 199.6 109
98 1461004536.32286 0 4 11 181.59 911.9 101.55 34 182 339 76 4.947 97 8.85 1.45 590 1000 0 110.6 199 110
99 1461004538.06289 0 4 12 183.32 920.5 101.39 34 181 341 76 4.955 98 8.78 1.39 580 970 0 109.7 195.2 110
100 1461004539.89296 0 4 13 185.11 929.4 101.35 34 181 341 77 4.957 99 8.71 1.42 570 1010 0 112.5 207.2 109
101 1461004541.6899 0 4 14 186.8 937.8 101.24 33 181 342 78 4.962 100 8.92 1.42 570 1010 0 112.9 206.9 109
102 1461004543.39974 0 4 15 188.64 946.9 101.37 34 181 341 79 4.956 101 8.85 1.39 560 940 0 112.1 200.7 110
103 1461004545.11348 0 4 16 190.34 955.4 101 35 181 345 79 4.975 102 8.42 1.36 560 970 0 113.8 187.5 110
104 1461004546.85166 0 4 17 192.1 964.1 100.78 35 180 347 80 4.985 103 8.57 1.36 550 1010 0 109.9 194.7 110
105 1461004548.65002 0 4 18 193.91 973 101.83 34 181 336 81 4.934 104 8.78 1.39 580 1010 0 107.6 188.8 110
106 1461004550.35928 0 4 19 195.6 981.3 102.6 34 181 329 81 4.896 105 8.71 1.36 560 1010 0 108.9 183.6 110
107 1461004552.30971 0 4 20 197.53 990.5 103.54 33 181 320 82 4.852 106 8.86 1.36 560 1040 0 102.4 187.2 110
108 1461004554.19972 0 4 21 199.44 999.5 105.35 33 180 304 83 4.768 107 8.7 1.33 590 1110 0 100 181.7 109
109 1461004554.35294 0 4 22 199.56 1000 105.35 33 180 304 83 4.768 107 8.7 1.33 590 1110 0 100 181.7 109
110 1461004556.03066 0 6 0 199.57 1000 105.35 33 180 304 83 4.768 107 8.7 1.33 590 1110 0 100 181.7 109

View File

@@ -1,58 +0,0 @@
index, AverageDriveForce (lbs), Cadence (stokes/min), DriveLength (meters), DriveTime (ms), ElapsedTime (sec), HRCur (bpm), Horizontal (meters), PeakDriveForce (lbs), Power (watts), Stroke500mPace (sec/500m), StrokeDistance (meters), StrokeRecoveryTime (ms), lapIdx,TimeStamp (sec)
0,0.0,54,0.0,0.0,2.6,106,11.8,0.0,0.0,130.7,0.0,0.0,0.0,1461602536.6
1,0.0,54,0.0,0.0,2.6,108,11.8,0.0,0.0,108.7,0.0,0.0,0.0,1461602536.6
2,0.0,43,0.0,0.0,4.1,110,19.7,0.0,0.0,101.3,0.0,0.0,0.0,1461602538.1
3,0.0,40,0.0,0.0,5.7,116,28.7,0.0,0.0,93.3,0.0,0.0,0.0,1461602539.7
4,0.0,37,0.0,0.0,7.4,122,38.0,0.0,0.0,90.6,0.0,0.0,0.0,1461602541.4
5,0.0,36,0.0,0.0,9.0,126,47.1,0.0,0.0,90.0,0.0,0.0,0.0,1461602543.0
6,0.0,37,0.0,0.0,10.7,132,56.4,0.0,0.0,90.2,0.0,0.0,0.0,1461602544.7
7,0.0,36,0.0,0.0,12.4,138,65.7,0.0,0.0,90.6,0.0,0.0,0.0,1461602546.4
8,0.0,35,0.0,0.0,14.1,142,75.0,0.0,0.0,91.2,0.0,0.0,0.0,1461602548.1
9,0.0,37,0.0,0.0,15.5,148,83.2,0.0,0.0,90.6,0.0,0.0,0.0,1461602549.5
10,0.0,38,0.0,0.0,17.3,151,92.8,0.0,0.0,90.2,0.0,0.0,0.0,1461602551.3
11,0.0,37,0.0,0.0,18.9,155,101.9,0.0,0.0,90.4,0.0,0.0,0.0,1461602552.9
12,0.0,36,0.0,0.0,20.5,159,110.5,0.0,0.0,90.9,0.0,0.0,0.0,1461602554.5
13,0.0,36,0.0,0.0,22.2,161,120.3,0.0,0.0,90.8,0.0,0.0,0.0,1461602556.2
14,0.0,36,0.0,0.0,23.9,164,129.5,0.0,0.0,90.8,0.0,0.0,0.0,1461602557.9
15,0.0,36,0.0,0.0,25.6,166,138.7,0.0,0.0,91.3,0.0,0.0,0.0,1461602559.6
16,0.0,36,0.0,0.0,27.2,166,147.7,0.0,0.0,91.9,0.0,0.0,0.0,1461602561.2
17,0.0,36,0.0,0.0,28.9,168,156.9,0.0,0.0,92.0,0.0,0.0,0.0,1461602562.9
18,0.0,36,0.0,0.0,30.5,169,165.7,0.0,0.0,92.0,0.0,0.0,0.0,1461602564.5
19,0.0,37,0.0,0.0,32.2,170,174.7,0.0,0.0,91.4,0.0,0.0,0.0,1461602566.2
20,0.0,37,0.0,0.0,33.8,171,183.6,0.0,0.0,91.4,0.0,0.0,0.0,1461602567.8
21,0.0,37,0.0,0.0,35.4,171,192.6,0.0,0.0,91.4,0.0,0.0,0.0,1461602569.4
22,0.0,36,0.0,0.0,37.0,172,201.2,0.0,0.0,92.0,0.0,0.0,0.0,1461602571.0
23,0.0,36,0.0,0.0,38.7,173,210.6,0.0,0.0,92.4,0.0,0.0,0.0,1461602572.7
24,0.0,37,0.0,0.0,40.4,173,219.5,0.0,0.0,91.9,0.0,0.0,0.0,1461602574.4
25,0.0,37,0.0,0.0,42.0,174,228.2,0.0,0.0,92.4,0.0,0.0,0.0,1461602576.0
26,0.0,37,0.0,0.0,43.6,174,237.1,0.0,0.0,92.5,0.0,0.0,0.0,1461602577.6
27,0.0,36,0.0,0.0,45.2,174,245.9,0.0,0.0,93.0,0.0,0.0,0.0,1461602579.2
28,0.0,37,0.0,0.0,46.9,175,254.9,0.0,0.0,93.4,0.0,0.0,0.0,1461602580.9
29,0.0,36,0.0,0.0,48.5,175,263.8,0.0,0.0,92.9,0.0,0.0,0.0,1461602582.5
30,0.0,37,0.0,0.0,50.1,175,272.4,0.0,0.0,92.3,0.0,0.0,0.0,1461602584.1
31,0.0,38,0.0,0.0,51.6,176,280.6,0.0,0.0,92.5,0.0,0.0,0.0,1461602585.6
32,0.0,36,0.0,0.0,53.4,176,290.1,0.0,0.0,92.8,0.0,0.0,0.0,1461602587.4
33,0.0,36,0.0,0.0,55.1,176,299.1,0.0,0.0,93.5,0.0,0.0,0.0,1461602589.1
34,0.0,36,0.0,0.0,56.7,177,308.0,0.0,0.0,93.7,0.0,0.0,0.0,1461602590.7
35,0.0,36,0.0,0.0,58.4,177,316.9,0.0,0.0,94.1,0.0,0.0,0.0,1461602592.4
36,0.0,36,0.0,0.0,60.0,177,325.5,0.0,0.0,94.3,0.0,0.0,0.0,1461602594.0
37,0.0,37,0.0,0.0,61.7,177,334.4,0.0,0.0,94.9,0.0,0.0,0.0,1461602595.7
38,0.0,36,0.0,0.0,63.4,178,343.3,0.0,0.0,95.2,0.0,0.0,0.0,1461602597.4
39,0.0,35,0.0,0.0,65.1,178,352.3,0.0,0.0,95.5,0.0,0.0,0.0,1461602599.1
40,0.0,35,0.0,0.0,66.7,178,360.8,0.0,0.0,96.1,0.0,0.0,0.0,1461602600.7
41,0.0,35,0.0,0.0,68.6,178,370.3,0.0,0.0,96.3,0.0,0.0,0.0,1461602602.6
42,0.0,35,0.0,0.0,70.3,178,379.4,0.0,0.0,95.8,0.0,0.0,0.0,1461602604.3
43,0.0,35,0.0,0.0,72.0,178,388.5,0.0,0.0,95.8,0.0,0.0,0.0,1461602606.0
44,0.0,35,0.0,0.0,73.7,178,397.2,0.0,0.0,96.5,0.0,0.0,0.0,1461602607.7
45,0.0,35,0.0,0.0,75.5,178,406.1,0.0,0.0,97.1,0.0,0.0,0.0,1461602609.5
46,0.0,35,0.0,0.0,77.1,179,414.3,0.0,0.0,97.8,0.0,0.0,0.0,1461602611.1
47,0.0,34,0.0,0.0,78.8,179,423.3,0.0,0.0,98.4,0.0,0.0,0.0,1461602612.8
48,0.0,34,0.0,0.0,80.7,179,432.8,0.0,0.0,98.0,0.0,0.0,0.0,1461602614.7
49,0.0,34,0.0,0.0,82.5,178,442.1,0.0,0.0,96.8,0.0,0.0,0.0,1461602616.5
50,0.0,34,0.0,0.0,84.3,179,451.4,0.0,0.0,96.9,0.0,0.0,0.0,1461602618.3
51,0.0,34,0.0,0.0,86.0,178,460.7,0.0,0.0,97.1,0.0,0.0,0.0,1461602620.0
52,0.0,33,0.0,0.0,87.9,179,469.9,0.0,0.0,97.3,0.0,0.0,0.0,1461602621.9
53,0.0,34,0.0,0.0,89.6,178,479.1,0.0,0.0,97.8,0.0,0.0,0.0,1461602623.6
54,0.0,33,0.0,0.0,91.4,179,488.2,0.0,0.0,98.6,0.0,0.0,0.0,1461602625.4
55,0.0,33,0.0,0.0,93.3,179,497.5,0.0,0.0,99.3,0.0,0.0,0.0,1461602627.3
56,0.0,33,0.0,0.0,93.8,178,500.0,0.0,0.0,99.3,0.0,0.0,0.0,1461602627.8
1 index AverageDriveForce (lbs) Cadence (stokes/min) DriveLength (meters) DriveTime (ms) ElapsedTime (sec) HRCur (bpm) Horizontal (meters) PeakDriveForce (lbs) Power (watts) Stroke500mPace (sec/500m) StrokeDistance (meters) StrokeRecoveryTime (ms) lapIdx TimeStamp (sec)
2 0 0.0 54 0.0 0.0 2.6 106 11.8 0.0 0.0 130.7 0.0 0.0 0.0 1461602536.6
3 1 0.0 54 0.0 0.0 2.6 108 11.8 0.0 0.0 108.7 0.0 0.0 0.0 1461602536.6
4 2 0.0 43 0.0 0.0 4.1 110 19.7 0.0 0.0 101.3 0.0 0.0 0.0 1461602538.1
5 3 0.0 40 0.0 0.0 5.7 116 28.7 0.0 0.0 93.3 0.0 0.0 0.0 1461602539.7
6 4 0.0 37 0.0 0.0 7.4 122 38.0 0.0 0.0 90.6 0.0 0.0 0.0 1461602541.4
7 5 0.0 36 0.0 0.0 9.0 126 47.1 0.0 0.0 90.0 0.0 0.0 0.0 1461602543.0
8 6 0.0 37 0.0 0.0 10.7 132 56.4 0.0 0.0 90.2 0.0 0.0 0.0 1461602544.7
9 7 0.0 36 0.0 0.0 12.4 138 65.7 0.0 0.0 90.6 0.0 0.0 0.0 1461602546.4
10 8 0.0 35 0.0 0.0 14.1 142 75.0 0.0 0.0 91.2 0.0 0.0 0.0 1461602548.1
11 9 0.0 37 0.0 0.0 15.5 148 83.2 0.0 0.0 90.6 0.0 0.0 0.0 1461602549.5
12 10 0.0 38 0.0 0.0 17.3 151 92.8 0.0 0.0 90.2 0.0 0.0 0.0 1461602551.3
13 11 0.0 37 0.0 0.0 18.9 155 101.9 0.0 0.0 90.4 0.0 0.0 0.0 1461602552.9
14 12 0.0 36 0.0 0.0 20.5 159 110.5 0.0 0.0 90.9 0.0 0.0 0.0 1461602554.5
15 13 0.0 36 0.0 0.0 22.2 161 120.3 0.0 0.0 90.8 0.0 0.0 0.0 1461602556.2
16 14 0.0 36 0.0 0.0 23.9 164 129.5 0.0 0.0 90.8 0.0 0.0 0.0 1461602557.9
17 15 0.0 36 0.0 0.0 25.6 166 138.7 0.0 0.0 91.3 0.0 0.0 0.0 1461602559.6
18 16 0.0 36 0.0 0.0 27.2 166 147.7 0.0 0.0 91.9 0.0 0.0 0.0 1461602561.2
19 17 0.0 36 0.0 0.0 28.9 168 156.9 0.0 0.0 92.0 0.0 0.0 0.0 1461602562.9
20 18 0.0 36 0.0 0.0 30.5 169 165.7 0.0 0.0 92.0 0.0 0.0 0.0 1461602564.5
21 19 0.0 37 0.0 0.0 32.2 170 174.7 0.0 0.0 91.4 0.0 0.0 0.0 1461602566.2
22 20 0.0 37 0.0 0.0 33.8 171 183.6 0.0 0.0 91.4 0.0 0.0 0.0 1461602567.8
23 21 0.0 37 0.0 0.0 35.4 171 192.6 0.0 0.0 91.4 0.0 0.0 0.0 1461602569.4
24 22 0.0 36 0.0 0.0 37.0 172 201.2 0.0 0.0 92.0 0.0 0.0 0.0 1461602571.0
25 23 0.0 36 0.0 0.0 38.7 173 210.6 0.0 0.0 92.4 0.0 0.0 0.0 1461602572.7
26 24 0.0 37 0.0 0.0 40.4 173 219.5 0.0 0.0 91.9 0.0 0.0 0.0 1461602574.4
27 25 0.0 37 0.0 0.0 42.0 174 228.2 0.0 0.0 92.4 0.0 0.0 0.0 1461602576.0
28 26 0.0 37 0.0 0.0 43.6 174 237.1 0.0 0.0 92.5 0.0 0.0 0.0 1461602577.6
29 27 0.0 36 0.0 0.0 45.2 174 245.9 0.0 0.0 93.0 0.0 0.0 0.0 1461602579.2
30 28 0.0 37 0.0 0.0 46.9 175 254.9 0.0 0.0 93.4 0.0 0.0 0.0 1461602580.9
31 29 0.0 36 0.0 0.0 48.5 175 263.8 0.0 0.0 92.9 0.0 0.0 0.0 1461602582.5
32 30 0.0 37 0.0 0.0 50.1 175 272.4 0.0 0.0 92.3 0.0 0.0 0.0 1461602584.1
33 31 0.0 38 0.0 0.0 51.6 176 280.6 0.0 0.0 92.5 0.0 0.0 0.0 1461602585.6
34 32 0.0 36 0.0 0.0 53.4 176 290.1 0.0 0.0 92.8 0.0 0.0 0.0 1461602587.4
35 33 0.0 36 0.0 0.0 55.1 176 299.1 0.0 0.0 93.5 0.0 0.0 0.0 1461602589.1
36 34 0.0 36 0.0 0.0 56.7 177 308.0 0.0 0.0 93.7 0.0 0.0 0.0 1461602590.7
37 35 0.0 36 0.0 0.0 58.4 177 316.9 0.0 0.0 94.1 0.0 0.0 0.0 1461602592.4
38 36 0.0 36 0.0 0.0 60.0 177 325.5 0.0 0.0 94.3 0.0 0.0 0.0 1461602594.0
39 37 0.0 37 0.0 0.0 61.7 177 334.4 0.0 0.0 94.9 0.0 0.0 0.0 1461602595.7
40 38 0.0 36 0.0 0.0 63.4 178 343.3 0.0 0.0 95.2 0.0 0.0 0.0 1461602597.4
41 39 0.0 35 0.0 0.0 65.1 178 352.3 0.0 0.0 95.5 0.0 0.0 0.0 1461602599.1
42 40 0.0 35 0.0 0.0 66.7 178 360.8 0.0 0.0 96.1 0.0 0.0 0.0 1461602600.7
43 41 0.0 35 0.0 0.0 68.6 178 370.3 0.0 0.0 96.3 0.0 0.0 0.0 1461602602.6
44 42 0.0 35 0.0 0.0 70.3 178 379.4 0.0 0.0 95.8 0.0 0.0 0.0 1461602604.3
45 43 0.0 35 0.0 0.0 72.0 178 388.5 0.0 0.0 95.8 0.0 0.0 0.0 1461602606.0
46 44 0.0 35 0.0 0.0 73.7 178 397.2 0.0 0.0 96.5 0.0 0.0 0.0 1461602607.7
47 45 0.0 35 0.0 0.0 75.5 178 406.1 0.0 0.0 97.1 0.0 0.0 0.0 1461602609.5
48 46 0.0 35 0.0 0.0 77.1 179 414.3 0.0 0.0 97.8 0.0 0.0 0.0 1461602611.1
49 47 0.0 34 0.0 0.0 78.8 179 423.3 0.0 0.0 98.4 0.0 0.0 0.0 1461602612.8
50 48 0.0 34 0.0 0.0 80.7 179 432.8 0.0 0.0 98.0 0.0 0.0 0.0 1461602614.7
51 49 0.0 34 0.0 0.0 82.5 178 442.1 0.0 0.0 96.8 0.0 0.0 0.0 1461602616.5
52 50 0.0 34 0.0 0.0 84.3 179 451.4 0.0 0.0 96.9 0.0 0.0 0.0 1461602618.3
53 51 0.0 34 0.0 0.0 86.0 178 460.7 0.0 0.0 97.1 0.0 0.0 0.0 1461602620.0
54 52 0.0 33 0.0 0.0 87.9 179 469.9 0.0 0.0 97.3 0.0 0.0 0.0 1461602621.9
55 53 0.0 34 0.0 0.0 89.6 178 479.1 0.0 0.0 97.8 0.0 0.0 0.0 1461602623.6
56 54 0.0 33 0.0 0.0 91.4 179 488.2 0.0 0.0 98.6 0.0 0.0 0.0 1461602625.4
57 55 0.0 33 0.0 0.0 93.3 179 497.5 0.0 0.0 99.3 0.0 0.0 0.0 1461602627.3
58 56 0.0 33 0.0 0.0 93.8 178 500.0 0.0 0.0 99.3 0.0 0.0 0.0 1461602627.8

View File

@@ -1,372 +0,0 @@
<map version="1.0.1">
<!-- To view this file, download free mind mapping software FreeMind from http://freemind.sourceforge.net -->
<node CREATED="1461605460487" ID="ID_889197617" MODIFIED="1461606620316" TEXT="Rowsandall.com">
<font BOLD="true" NAME="SansSerif" SIZE="12"/>
<node CREATED="1461605502608" ID="ID_1180462618" MODIFIED="1464031892658" POSITION="left" TEXT="Views">
<node CREATED="1461605473619" FOLDED="true" ID="ID_758526503" MODIFIED="1465560183674" TEXT="User">
<node CREATED="1461605477168" ID="ID_1749649289" MODIFIED="1461605479404" TEXT="Standard">
<node CREATED="1461605480370" ID="ID_738158750" MODIFIED="1462199805310" TEXT="login">
<icon BUILTIN="button_ok"/>
</node>
<node CREATED="1461605482538" ID="ID_1405710591" MODIFIED="1462199807598" TEXT="logout">
<icon BUILTIN="button_ok"/>
</node>
<node CREATED="1461605484671" ID="ID_1442326086" MODIFIED="1461605487813" TEXT="change password"/>
<node CREATED="1461605488216" ID="ID_1649042094" MODIFIED="1461605492132" TEXT="forgotten password"/>
</node>
<node CREATED="1461605833468" ID="ID_1862101264" MODIFIED="1461605836329" TEXT="Profile">
<node CREATED="1461605838740" ID="ID_1834978335" MODIFIED="1462199827098" TEXT="Profile Edit">
<icon BUILTIN="button_ok"/>
</node>
</node>
</node>
<node CREATED="1461605852258" FOLDED="true" ID="ID_1710060901" MODIFIED="1465560182848" TEXT="Workouts">
<node CREATED="1461607050049" ID="ID_480866837" MODIFIED="1462372232456" TEXT="Add Workout">
<icon BUILTIN="button_ok"/>
<node CREATED="1461607067010" ID="ID_661274255" MODIFIED="1462372158825" TEXT="Import from C2">
<arrowlink DESTINATION="ID_640810634" ENDARROW="Default" ENDINCLINATION="115;0;" ID="Arrow_ID_41303595" STARTARROW="None" STARTINCLINATION="115;0;"/>
<icon BUILTIN="button_ok"/>
</node>
<node CREATED="1461607694156" ID="ID_1130452500" MODIFIED="1462199832919" TEXT="File Upload">
<arrowlink DESTINATION="ID_640810634" ENDARROW="Default" ENDINCLINATION="293;0;" ID="Arrow_ID_758678018" STARTARROW="None" STARTINCLINATION="293;0;"/>
<icon BUILTIN="button_ok"/>
</node>
</node>
<node CREATED="1461605874513" ID="ID_1782808892" MODIFIED="1461607669961" TEXT="Workouts (list of workouts with check boxes and buttons)">
<font BOLD="true" NAME="SansSerif" SIZE="12"/>
<node CREATED="1461605893808" ID="ID_518138659" MODIFIED="1462372195888" TEXT="Upload to C2 (can be bulk, grayed out if already)">
<icon BUILTIN="button_ok"/>
</node>
<node CREATED="1461605938181" ID="ID_640810634" MODIFIED="1462372165831" TEXT="Edit Workout">
<linktarget COLOR="#b0b0b0" DESTINATION="ID_640810634" ENDARROW="Default" ENDINCLINATION="115;0;" ID="Arrow_ID_41303595" SOURCE="ID_661274255" STARTARROW="None" STARTINCLINATION="115;0;"/>
<linktarget COLOR="#b0b0b0" DESTINATION="ID_640810634" ENDARROW="Default" ENDINCLINATION="293;0;" ID="Arrow_ID_758678018" SOURCE="ID_1130452500" STARTARROW="None" STARTINCLINATION="293;0;"/>
<icon BUILTIN="button_ok"/>
<node CREATED="1461605945605" ID="ID_1416204687" MODIFIED="1461605948881" TEXT="Re-upload"/>
<node CREATED="1461606059232" ID="ID_1181900408" MODIFIED="1462372171747" TEXT="Delete">
<icon BUILTIN="button_ok"/>
</node>
<node CREATED="1461605951301" ID="ID_1968531900" MODIFIED="1461605954953" TEXT="Add wind"/>
<node CREATED="1461605965845" ID="ID_1834624609" MODIFIED="1462372174420" TEXT="Notes field">
<icon BUILTIN="button_ok"/>
</node>
<node CREATED="1461606989942" ID="ID_827883014" MODIFIED="1462372177224" TEXT="Type">
<icon BUILTIN="button_ok"/>
</node>
<node CREATED="1461607007953" ID="ID_1419094175" MODIFIED="1461607017030" TEXT="Workout Date"/>
<node CREATED="1461607011010" ID="ID_609096815" MODIFIED="1461607021325" TEXT="Workout Time"/>
<node CREATED="1462372184965" ID="ID_373832954" MODIFIED="1462372188425" TEXT="Add summary data"/>
<node CREATED="1461606385679" ID="ID_630481471" MODIFIED="1461606394314" TEXT="Create new image"/>
</node>
<node CREATED="1461606355535" ID="ID_1087026303" MODIFIED="1461607115821" TEXT="Images">
<node CREATED="1461606373437" ID="ID_103313544" MODIFIED="1462372204106" TEXT="Thumbnails of all images related to this workout">
<icon BUILTIN="button_ok"/>
<node CREATED="1461607873806" ID="ID_274993696" MODIFIED="1462372206775" TEXT="Image">
<icon BUILTIN="button_ok"/>
<node CREATED="1461607878860" ID="ID_1920400420" MODIFIED="1462372494580" TEXT="Download link "/>
<node CREATED="1462372495159" ID="ID_95260866" MODIFIED="1462372498010" TEXT="Copy link"/>
<node CREATED="1461607881802" ID="ID_98661252" MODIFIED="1461607883115" TEXT="Delete"/>
</node>
</node>
</node>
</node>
<node CREATED="1461607842284" ID="ID_1056962364" MODIFIED="1462372225114" TEXT="Dashboard (recent workouts and images)">
<icon BUILTIN="button_ok"/>
<node CREATED="1462477981731" ID="ID_1584782313" MODIFIED="1462477992939" TEXT="Click on workout and get all related graphs"/>
</node>
</node>
<node CREATED="1461606171232" FOLDED="true" ID="ID_622814831" MODIFIED="1465560179352" TEXT="Static Pages">
<node CREATED="1461606207102" ID="ID_578601651" MODIFIED="1465560156611" TEXT="Donate">
<icon BUILTIN="button_ok"/>
<node CREATED="1461606226184" ID="ID_1214482888" MODIFIED="1461606232099" TEXT="Help cover hosting costs!"/>
</node>
<node CREATED="1461606209720" ID="ID_915618144" MODIFIED="1465560159175" TEXT="About">
<icon BUILTIN="button_ok"/>
<node CREATED="1461606219407" ID="ID_1994937123" MODIFIED="1461606224396" TEXT="Bla bla bla about"/>
</node>
<node CREATED="1461606211560" ID="ID_1050734643" MODIFIED="1465560162149" TEXT="Contact">
<icon BUILTIN="button_ok"/>
<node CREATED="1461606214176" ID="ID_929787220" MODIFIED="1461606217618" TEXT="Email contact form"/>
</node>
</node>
<node CREATED="1461606535579" ID="ID_1450476512" MODIFIED="1461606537849" TEXT="Admin">
<node CREATED="1461606539053" ID="ID_283722711" MODIFIED="1465560176760" TEXT="Delete old workouts"/>
<node CREATED="1461606693893" ID="ID_1225613996" MODIFIED="1461606698106" TEXT="Remove user"/>
</node>
</node>
<node CREATED="1461605512814" FOLDED="true" ID="ID_367361758" MODIFIED="1464105541217" POSITION="right" TEXT="Data">
<node CREATED="1461605518069" ID="ID_931509269" MODIFIED="1461859560704" TEXT="Users">
<linktarget COLOR="#b0b0b0" DESTINATION="ID_931509269" ENDARROW="Default" ENDINCLINATION="200;0;" ID="Arrow_ID_793154745" SOURCE="ID_942353046" STARTARROW="None" STARTINCLINATION="200;0;"/>
<icon BUILTIN="button_ok"/>
<node CREATED="1461605521065" ID="ID_1638831444" MODIFIED="1461859556854" TEXT="Standard">
<linktarget COLOR="#b0b0b0" DESTINATION="ID_1638831444" ENDARROW="Default" ENDINCLINATION="191;0;" ID="Arrow_ID_304251916" SOURCE="ID_1797909649" STARTARROW="None" STARTINCLINATION="191;0;"/>
<icon BUILTIN="button_ok"/>
<node CREATED="1461605527357" ID="ID_64076292" MODIFIED="1461675033560" TEXT="username">
<icon BUILTIN="button_ok"/>
</node>
<node CREATED="1461605531205" ID="ID_1160148822" MODIFIED="1461675039348" TEXT="password">
<icon BUILTIN="button_ok"/>
</node>
<node CREATED="1461605534005" ID="ID_468992723" MODIFIED="1461675044250" TEXT="email address">
<icon BUILTIN="button_ok"/>
</node>
<node CREATED="1461605537685" ID="ID_658719592" MODIFIED="1461675047673" TEXT="First name">
<icon BUILTIN="button_ok"/>
</node>
<node CREATED="1461605541815" ID="ID_780111581" MODIFIED="1461675049604" TEXT="Last Name">
<icon BUILTIN="button_ok"/>
</node>
</node>
</node>
<node CREATED="1461651836946" ID="ID_1797909649" MODIFIED="1461859566661" TEXT="Rower">
<arrowlink DESTINATION="ID_1638831444" ENDARROW="Default" ENDINCLINATION="191;0;" ID="Arrow_ID_304251916" STARTARROW="None" STARTINCLINATION="191;0;"/>
<icon BUILTIN="button_ok"/>
<node CREATED="1461605578428" ID="ID_1864317482" MODIFIED="1461675054304" TEXT="HR Bands (with default settings)">
<icon BUILTIN="button_ok"/>
<node CREATED="1461605582890" ID="ID_1036263090" MODIFIED="1461652489462" TEXT="max">
<icon BUILTIN="button_ok"/>
</node>
<node CREATED="1461605584970" ID="ID_349101423" MODIFIED="1461674288201" TEXT="an">
<icon BUILTIN="button_ok"/>
</node>
<node CREATED="1461605606354" ID="ID_658122284" MODIFIED="1461674293973" TEXT="tr">
<icon BUILTIN="button_ok"/>
</node>
<node CREATED="1461605609690" ID="ID_64081034" MODIFIED="1461674300195" TEXT="at">
<icon BUILTIN="button_ok"/>
</node>
<node CREATED="1461605612128" ID="ID_1685146538" MODIFIED="1461674305654" TEXT="ut1">
<icon BUILTIN="button_ok"/>
</node>
<node CREATED="1461605614208" ID="ID_1935848031" MODIFIED="1461674312392" TEXT="ut2">
<icon BUILTIN="button_ok"/>
</node>
<node CREATED="1461605618128" ID="ID_425502762" MODIFIED="1461674318162" TEXT="rest">
<icon BUILTIN="button_ok"/>
</node>
</node>
<node CREATED="1461605640561" ID="ID_1222231283" MODIFIED="1461674599803" TEXT="Default weight class">
<icon BUILTIN="button_ok"/>
</node>
<node CREATED="1461605559179" ID="ID_707728856" MODIFIED="1461605560984" TEXT="Hidden">
<node CREATED="1461605561891" ID="ID_1160097125" MODIFIED="1461674977236" TEXT="C2 log access token">
<icon BUILTIN="button_ok"/>
<node CREATED="1461605710787" ID="ID_1360155429" MODIFIED="1461605730720" TEXT="obtained when user grants permission through his C2 identity"/>
</node>
<node CREATED="1461605570034" ID="ID_1771848783" MODIFIED="1461674983016" TEXT="Access token expiration date">
<icon BUILTIN="button_ok"/>
</node>
</node>
</node>
<node CREATED="1461605740385" ID="ID_39541443" MODIFIED="1461605747853" TEXT="Workouts">
<node CREATED="1461605749553" ID="ID_942353046" MODIFIED="1461652737920" TEXT="User">
<arrowlink DESTINATION="ID_931509269" ENDARROW="Default" ENDINCLINATION="200;0;" ID="Arrow_ID_793154745" STARTARROW="None" STARTINCLINATION="200;0;"/>
<icon BUILTIN="button_ok"/>
</node>
<node CREATED="1461606406059" ID="ID_454812442" MODIFIED="1461652703049" TEXT="Workout_name">
<icon BUILTIN="button_ok"/>
</node>
<node CREATED="1461606414638" ID="ID_928281830" MODIFIED="1461660580936" TEXT="Workout_date">
<icon BUILTIN="button_ok"/>
</node>
<node CREATED="1461606429486" ID="ID_484521014" MODIFIED="1461866244072" TEXT="Workout_starttime">
<icon BUILTIN="button_ok"/>
</node>
<node CREATED="1461606866528" ID="ID_1872216450" MODIFIED="1461866319482" TEXT="Workout_distance">
<icon BUILTIN="button_ok"/>
<node CREATED="1461866323170" ID="ID_936304102" MODIFIED="1461866324983" TEXT="meters"/>
</node>
<node CREATED="1461606878962" ID="ID_1128895938" MODIFIED="1461866381363" TEXT="Workout_duration">
<icon BUILTIN="button_ok"/>
<node CREATED="1461866327528" ID="ID_901906294" MODIFIED="1461866329945" TEXT="seconds"/>
</node>
<node CREATED="1461606908475" ID="ID_900580299" MODIFIED="1461866411448" TEXT="Workout_weight_class">
<icon BUILTIN="button_ok"/>
</node>
<node CREATED="1461606914708" FOLDED="true" ID="ID_931316189" MODIFIED="1461675065400" TEXT="Workout_type">
<icon BUILTIN="button_ok"/>
<node CREATED="1461606925676" ID="ID_1969400042" MODIFIED="1461606933440" TEXT="On-water"/>
<node CREATED="1461606927748" ID="ID_1209065588" MODIFIED="1461606941465" TEXT="Indoor Rower"/>
<node CREATED="1461606944797" ID="ID_610597647" MODIFIED="1461606949114" TEXT="Indoor Rower with Slides"/>
<node CREATED="1461606952525" ID="ID_1889795917" MODIFIED="1461606956547" TEXT="Dynamic Indoor Rower"/>
<node CREATED="1461606960376" ID="ID_294896523" MODIFIED="1461606962612" TEXT="Ski Erg"/>
<node CREATED="1461606963397" ID="ID_490247020" MODIFIED="1461606967058" TEXT="Paddle Adapter"/>
<node CREATED="1461606970534" ID="ID_1089182854" MODIFIED="1461606973571" TEXT="On-snow"/>
</node>
<node CREATED="1461605767159" ID="ID_156406" MODIFIED="1461866434741" TEXT="Filename (csv file)">
<icon BUILTIN="button_ok"/>
<node CREATED="1461606269644" ID="ID_196687111" MODIFIED="1461606286903" TEXT="I presume the data will be in painsled style csv files- not in the database"/>
</node>
<node CREATED="1461606317938" ID="ID_1830610935" MODIFIED="1463015586401" TEXT="Images">
<linktarget COLOR="#b0b0b0" DESTINATION="ID_1830610935" ENDARROW="Default" ENDINCLINATION="115;0;" ID="Arrow_ID_576769313" SOURCE="ID_1806450802" STARTARROW="None" STARTINCLINATION="115;0;"/>
<icon BUILTIN="button_ok"/>
</node>
<node CREATED="1461605787942" ID="ID_6975390" MODIFIED="1461866507607" TEXT="Uploaded to c2 yes/no">
<icon BUILTIN="button_ok"/>
</node>
<node CREATED="1461605980412" ID="ID_739576489" MODIFIED="1461866856190" TEXT="Notes">
<icon BUILTIN="button_ok"/>
</node>
</node>
<node CREATED="1461823229485" ID="ID_1676270847" MODIFIED="1461836884659" TEXT="Image">
<node CREATED="1461823234420" ID="ID_100561287" MODIFIED="1461866881645" TEXT="File name">
<icon BUILTIN="button_ok"/>
</node>
<node CREATED="1461823237869" ID="ID_366884875" MODIFIED="1461866884487" TEXT="Creation Date">
<icon BUILTIN="button_ok"/>
</node>
<node CREATED="1461823241228" ID="ID_1806450802" MODIFIED="1461866887134" TEXT="Workout">
<arrowlink DESTINATION="ID_1830610935" ENDARROW="Default" ENDINCLINATION="115;0;" ID="Arrow_ID_576769313" STARTARROW="None" STARTINCLINATION="115;0;"/>
<icon BUILTIN="button_ok"/>
</node>
</node>
</node>
<node CREATED="1461606622088" FOLDED="true" ID="ID_1185569412" MODIFIED="1464024669570" POSITION="right" TEXT="Design">
<node CREATED="1461606626928" ID="ID_500274274" MODIFIED="1461606634684" TEXT="Header">
<node CREATED="1461607250605" ID="ID_277736788" MODIFIED="1461607542769" TEXT="Logo">
<node CREATED="1461831818073" ID="ID_1883988129" MODIFIED="1461831820242" TEXT="Top Left"/>
</node>
<node CREATED="1461831845737" ID="ID_1769289061" MODIFIED="1461831849446" TEXT="Navigation Bar"/>
<node CREATED="1461832234311" ID="ID_890051640" MODIFIED="1461832238780" TEXT="Value Prop on Home Page"/>
<node CREATED="1461607537352" ID="ID_9873263" MODIFIED="1461607540890" TEXT="Dashboard"/>
<node CREATED="1461607651139" ID="ID_1816736051" MODIFIED="1461607653176" TEXT="Workouts"/>
<node CREATED="1461607681100" ID="ID_963617159" MODIFIED="1461607683905" TEXT="Add Workout"/>
<node CREATED="1461832024698" ID="ID_704022131" MODIFIED="1461835713046" TEXT="Contact ">
<node CREATED="1461832031127" ID="ID_1522003911" MODIFIED="1461832033220" TEXT="top right"/>
</node>
<node CREATED="1461606804240" ID="ID_1832303281" MODIFIED="1461607544061" TEXT="User"/>
</node>
<node CREATED="1461606635252" ID="ID_1658688353" MODIFIED="1461606636679" TEXT="Footer">
<node CREATED="1461607518944" ID="ID_1140796060" MODIFIED="1461607526372" TEXT="Links to static pages"/>
</node>
<node CREATED="1461606637239" ID="ID_371706656" MODIFIED="1461606641893" TEXT="Side menu?"/>
</node>
<node CREATED="1461823376524" ID="ID_1672286072" MODIFIED="1461823378932" POSITION="left" TEXT="Testing"/>
<node CREATED="1461825250754" FOLDED="true" ID="ID_478462481" MODIFIED="1468564897482" POSITION="left" TEXT="Functionality">
<node CREATED="1461859283282" ID="ID_671390350" MODIFIED="1461859648988" TEXT="Workouts take user data for making plot">
<icon BUILTIN="button_ok"/>
</node>
<node CREATED="1461859444717" ID="ID_231247258" MODIFIED="1461873064421" TEXT="File upload creates workout">
<icon BUILTIN="button_ok"/>
<node CREATED="1461859462821" ID="ID_614435280" MODIFIED="1461873050715" TEXT="check for duplicate start times">
<icon BUILTIN="button_ok"/>
</node>
<node CREATED="1461861313345" ID="ID_1214320980" MODIFIED="1461868259008" TEXT="add workout starttime, workout distance, workout duration">
<icon BUILTIN="button_ok"/>
</node>
</node>
<node CREATED="1461876950835" ID="ID_1233516192" MODIFIED="1461939989747" TEXT="add DurationField instead of TimeField to Workout">
<icon BUILTIN="button_ok"/>
</node>
<node CREATED="1461939997214" ID="ID_623587817" MODIFIED="1461940003606" TEXT="Delete workouts">
<icon BUILTIN="button_ok"/>
</node>
<node CREATED="1461859314613" ID="ID_1989736428" MODIFIED="1461956084613" TEXT="Plot info stored in Image class - linked to Workout - linked to user">
<icon BUILTIN="button_ok"/>
</node>
<node CREATED="1461859340521" ID="ID_1835485102" MODIFIED="1461956098273" TEXT="Workout summary data added to workout on import">
<icon BUILTIN="button_ok"/>
</node>
<node CREATED="1461859363717" ID="ID_1063073204" MODIFIED="1461956100660" TEXT="Import TCX files">
<icon BUILTIN="button_ok"/>
</node>
<node CREATED="1462037395055" ID="ID_426454632" MODIFIED="1462089744303" TEXT="restrict file import to csv and tcx">
<icon BUILTIN="button_ok"/>
</node>
<node CREATED="1461868236440" ID="ID_998738107" MODIFIED="1462092856141" TEXT="add tenths in rowingdata.py line 455">
<icon BUILTIN="button_ok"/>
</node>
<node CREATED="1462090268399" ID="ID_1789786780" MODIFIED="1463013907767" TEXT="add proper error handling">
<icon BUILTIN="button_ok"/>
<node CREATED="1462095525267" ID="ID_1485370247" MODIFIED="1462095529439" TEXT="Internal error handling"/>
<node CREATED="1462095529783" ID="ID_1494810517" MODIFIED="1462095534480" TEXT="Error handling related to C2"/>
</node>
<node CREATED="1462385130662" ID="ID_1736622825" MODIFIED="1468564862058" TEXT="create C2 authorization/renewal functionality">
<icon BUILTIN="button_ok"/>
</node>
<node CREATED="1461859380482" ID="ID_286348211" MODIFIED="1462095448230" TEXT="Connect to C2 and store token">
<icon BUILTIN="button_ok"/>
</node>
<node CREATED="1461859292062" ID="ID_891978726" MODIFIED="1462114645928" TEXT="Upload simple workout to C2">
<icon BUILTIN="button_ok"/>
</node>
<node CREATED="1462114661522" ID="ID_108970697" MODIFIED="1462126485430" TEXT="Store C2 workout ID in database for checking duplicates">
<icon BUILTIN="button_ok"/>
</node>
<node CREATED="1462114646664" ID="ID_1219356268" MODIFIED="1462258165015" TEXT="Upload stroke data to C2">
<icon BUILTIN="button_ok"/>
</node>
<node CREATED="1462201886153" ID="ID_235589940" MODIFIED="1468564866385" TEXT="Add average and max HR to Workout model">
<icon BUILTIN="button_ok"/>
</node>
<node CREATED="1462220317016" ID="ID_247996816" MODIFIED="1462259473708" TEXT="Graph choices">
<icon BUILTIN="button_ok"/>
<node CREATED="1462220324724" ID="ID_1722947819" MODIFIED="1462259470955" TEXT="Add pie chart">
<icon BUILTIN="button_ok"/>
</node>
</node>
<node CREATED="1461859296985" ID="ID_697148148" MODIFIED="1462279669306" TEXT="Import workouts from C2">
<icon BUILTIN="button_ok"/>
</node>
<node CREATED="1462279678160" ID="ID_748439178" MODIFIED="1462372114571" TEXT="Upload TCX data to C2">
<icon BUILTIN="button_ok"/>
</node>
<node CREATED="1461859495116" ID="ID_1813236024" MODIFIED="1461859511687" TEXT="Add HR bands to &quot;open&quot; rowingdata view (for anonymous users)">
<node CREATED="1464099527327" ID="ID_1377500887" MODIFIED="1464099533644" TEXT="made it easier to sign up"/>
</node>
<node CREATED="1461859515623" ID="ID_871304706" MODIFIED="1464024676014" TEXT="get sendmail working on rowsandall.com">
<icon BUILTIN="button_ok"/>
</node>
<node CREATED="1462451790489" ID="ID_1246677164" MODIFIED="1463146936088" TEXT="make password change integral part of the site">
<icon BUILTIN="button_ok"/>
</node>
<node CREATED="1463013921749" ID="ID_891957290" MODIFIED="1464773708543" TEXT="Make interactive plots possible">
<icon BUILTIN="button_ok"/>
<node CREATED="1463013931611" ID="ID_1645855977" MODIFIED="1463013936147" TEXT="use mpld3"/>
<node CREATED="1463146923529" ID="ID_111815361" MODIFIED="1463146928361" TEXT="change plotting to bokeh"/>
</node>
<node CREATED="1464033158984" ID="ID_1019464919" MODIFIED="1468564874119" TEXT="add label under graph thumbnails">
<icon BUILTIN="button_ok"/>
</node>
<node CREATED="1463056343673" ID="ID_1704885677" MODIFIED="1464773723535" TEXT="Make duration field prettier in C2 Import view">
<icon BUILTIN="button_ok"/>
</node>
<node CREATED="1463898326795" ID="ID_304310926" MODIFIED="1468564877186" TEXT="compare 2 workouts">
<icon BUILTIN="button_ok"/>
</node>
<node CREATED="1465541522068" ID="ID_24723428" MODIFIED="1465541524989" TEXT="add Teams"/>
</node>
<node CREATED="1462372120147" FOLDED="true" ID="ID_1790010779" MODIFIED="1468564903194" POSITION="left" TEXT="Bugs">
<node CREATED="1462372122865" ID="ID_791488808" MODIFIED="1463926727764" TEXT="Export to C2 - reimport - 2 hours difference ">
<icon BUILTIN="button_ok"/>
<node CREATED="1462372134058" ID="ID_1541966541" MODIFIED="1462372139127" TEXT="Check time zone information"/>
</node>
<node CREATED="1462392509362" ID="ID_1974493580" MODIFIED="1464773734760" TEXT="Total time not correct">
<icon BUILTIN="button_ok"/>
<node CREATED="1462393932924" ID="ID_1584420283" MODIFIED="1462393949048" TEXT="Could take max(&apos;Elapsed Time&apos;) but this doesn&apos;t work for intervals sessions"/>
<node CREATED="1462393949569" ID="ID_601203201" MODIFIED="1462393955852" TEXT="Perhaps detect if intervals or not"/>
<node CREATED="1462394984031" ID="ID_772280168" MODIFIED="1462395007935" TEXT="Interestingly, the old painsledtoc2 upload from cmd line gives a shorter duration"/>
</node>
<node CREATED="1462392520077" ID="ID_1005078711" MODIFIED="1462395039415" TEXT="Interval workout time not rendered correctly on C2">
<icon BUILTIN="button_ok"/>
</node>
<node CREATED="1462393593313" ID="ID_1412686474" MODIFIED="1462393610112" TEXT="If user is not registered to C2 and wants to upload data, responses are not working well"/>
<node CREATED="1462433524171" ID="ID_207392923" MODIFIED="1464773757964" TEXT="Workout duration from RowPro seems to be wrong">
<icon BUILTIN="button_ok"/>
<node CREATED="1462433646218" ID="ID_1865445212" MODIFIED="1462433655083" TEXT="Elapsed Time resets at intervals"/>
<node CREATED="1462434136715" ID="ID_862653660" MODIFIED="1462434152061" TEXT="There is no time stamp - need to solve this in rowingdata.py"/>
</node>
<node CREATED="1462986270815" ID="ID_355099188" MODIFIED="1468564882289" TEXT="TCX files without HR data">
<icon BUILTIN="button_ok"/>
</node>
<node CREATED="1463056227952" ID="ID_1543943630" MODIFIED="1463056234311" TEXT="TCX files from Painsled">
<node CREATED="1463056996127" ID="ID_1741508714" MODIFIED="1463057048432" TEXT="No lat/lon values present"/>
<node CREATED="1464027094358" ID="ID_109801653" MODIFIED="1464027099523" TEXT="can import through strava?"/>
</node>
</node>
<node CREATED="1462383105838" FOLDED="true" ID="ID_1770973494" MODIFIED="1464773783299" POSITION="right" TEXT="Other">
<node CREATED="1462383109198" ID="ID_1666350492" MODIFIED="1463015612121" TEXT="Migrate from Celery to RQ">
<icon BUILTIN="button_ok"/>
</node>
</node>
</node>
</map>

Binary file not shown.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,9 +0,0 @@
{% extends "base.html" %}
{% load staticfiles %}
{% block title %}Here's your result{% endblock %}
{% block content %}
Waiting for task with result {{ task }}
{% endblock %}

View File

@@ -1,25 +0,0 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html lang="en">
<head>
<title>{% block title %}{% endblock %}</title>
{% block meta %} {% endblock %}
</head>
<body>
<h1>My helpful rowing site</h1>
{% if user.is_authenticated %}
<p>Welcome, {{ user.first_name }}.</p>
<p><a href="/logout/">logout</a></p>
{% else %}
<p><a href="/login/">login</a> </p>
{% endif %}
<hr>
{% block content %}{% endblock %}
{% block footer %}
<hr>
<p>{{ versionstring }}</p>
<p>Thanks for visiting my site.</p>
{% endblock %}
</body>
</html>

View File

@@ -1,22 +0,0 @@
<html>
<head>
<title>Contact us</title>
</head>
<body>
<h1>Contact us</h1>
{% if form.errors %}
<p style="color: red;">
Please correct the error{{ form.errors|pluralize }} below.
</p>
{% endif %}
<form action="" method="post">
<table>
{{ form.as_table }}
</table>
{% csrf_token %}
<input type="submit" value="Submit">
</form>
</body>
</html>

View File

@@ -1,7 +0,0 @@
{% extends "base.html" %}
{% block title %}Current time{% endblock %}
{% block content %}
<p>It is now {{ current_date }}.</p>
{% endblock %}

View File

@@ -1,21 +0,0 @@
{% extends "base.html" %}
{% load staticfiles %}
{% block title %}File loading{% endblock %}
{% block content %}
<h1>File Upload</h1>
{% if form.errors %}
<p style="color: red;">
Please correct the error{{ form.errors|pluralize }} below.
</p>
{% endif %}
<form enctype="multipart/form-data" action="{{ formloc }}" method="post">
<table>
{{ form.as_table }}
</table>
{% csrf_token %}
<input type="submit" value="Submit">
{% endblock %}

View File

@@ -1,7 +0,0 @@
{% extends "base.html" %}
{% block title %}Future time{% endblock %}
{% block content %}
<p>In {{ offset }} hours, it will be {{ future_time }}.</p>
{% endblock %}

View File

@@ -1,10 +0,0 @@
{% extends "base.html" %}
{% load staticfiles %}
{% block title %}Here's your plot{% endblock %}
{% block content %}
<h1> Here's your plot</h1>
<p><image src="{% static imagename %}"/></p>
{% endblock %}

View File

@@ -1,37 +0,0 @@
{% extends "base.html" %}
{% load staticfiles %}
{% load rowerfilters %}
{% block title %}Workouts{% endblock %}
{% block content %}
<h1>My Workouts</h1>
{% if workouts %}
<table width="70%">
<tr>
<td> <strong>Date</strong> </td>
<td> <strong>Time </strong></td>
<td> <strong>Name </strong></td>
<td> <strong>Type </strong></td>
<td> <strong>Distance</strong> </td>
<td> <strong>Duration</strong> </td>
<td> <strong>Edit</strong></td>
</tr>
{% for workout in workouts %}
<tr>
<td> {{ workout.date }} </td>
<td> {{ workout.starttime }} </td>
<td> {{ workout.name }} </td>
<td> {{ workout.workouttype }} </td>
<td> {{ workout.distance }}m</td>
<td> {{ workout.duration |durationprint:"%H:%M:%S" }} </td>
<td> <a href="/rowers/workout/{{ workout.id }}/edit">E</td>
</tr>
{% endfor %}
</table>
{% else %}
<p> No workouts found </p>
{% endif %}
{% endblock %}

View File

@@ -1,23 +0,0 @@
<html>
<head>
<title>Login Form</title>
</head>
<body>
<h1>Login</h1>
{% if form.errors %}
<p style="color: red;">
Please correct the error{{ form.errors|pluralize }} below.
</p>
{% endif %}
<form enctype="multipart/form-data" action="{{ formloc }}" method="post">
<table>
{{ form.as_table }}
</table>
{% csrf_token %}
<input type="hidden" name="next" value="{{ next }}">
<input type="submit" value="Submit">
</form>
</body>
</html>

View File

@@ -1,26 +0,0 @@
{% extends "base.html" %}
{% block content %}
{% if form.errors %}
<p>Your username and password didn't match. Please try again.</p>
{% endif %}
<form method="post" action="{% url 'django.contrib.auth.views.login' %}">
{% csrf_token %}
<table>
<tr>
<td>{{ form.username.label_tag }}</td>
<td>{{ form.username }}</td>
</tr>
<tr>
<td>{{ form.password.label_tag }}</td>
<td>{{ form.password }}</td>
</tr>
</table>
<input type="submit" value="login" />
<input type="hidden" name="next" value="{{ next }}" />
</form>
{% endblock %}

View File

@@ -1,21 +0,0 @@
{% extends "base.html" %}
{% block title %}Change Rower {% endblock %}
{% block content %}
{% if form.errors %}
<p style="color: red;">
Please correct the error{{ form.errors|pluralize }} below.
</p>
{% endif %}
<h1>Edit your Parameters</h1>
<form enctype="multipart/form-data" action="" method="post">
<table>
{{ form.as_table }}
</table>
{% csrf_token %}
<input type="submit" value="Save">
{% endblock %}

View File

@@ -1,7 +0,0 @@
{% extends "base.html" %}
{% block title %}Rowingdata Version{% endblock %}
{% block content %}
<p>{{ versionstring }}.</p>
{% endblock %}

View File

@@ -1,11 +0,0 @@
{% extends "base.html" %}
{% load staticfiles %}
{% block title %}Waiting{% endblock %}
{% block meta %}<meta http-equiv="refresh" content="30">{% endblock %}
{% block content %}
<h1>Waiting for your image to be processed</h1>
<p> {{ message }} </p>
<p>Please do not close this page. Page should refresh automatically (or you can hit reload)</p>
{% endblock %}

View File

@@ -1,21 +0,0 @@
{% extends "base.html" %}
{% block title %}Change Workout {% endblock %}
{% block content %}
{% if form.errors %}
<p style="color: red;">
Please correct the error{{ form.errors|pluralize }} below.
</p>
{% endif %}
<h1>Edit Workout Data</h1>
<form enctype="multipart/form-data" action="" method="post">
<table>
{{ form.as_table }}
</table>
{% csrf_token %}
<input type="submit" value="Save">
{% endblock %}

View File

@@ -4,8 +4,9 @@ from django.contrib.auth.models import User
from .models import Rower, Workout,GraphImage,FavoriteChart,SiteAnnouncement
# Register your models here.
# Register your models here so you can use them in the Admin module
# Rower details directly under the User
class RowerInline(admin.StackedInline):
model = Rower
can_delete = False

View File

@@ -2,6 +2,6 @@ from __future__ import unicode_literals
from django.apps import AppConfig
# Store metadata for the app
class RowersConfig(AppConfig):
name = 'rowers'

View File

@@ -1,3 +1,8 @@
# The interactions with the Concept2 logbook API
# All C2 related functions should be defined here
# (There is still some stuff defined directly in views.py. Need to
# move that here.)
# Python
import oauth2 as oauth
import cgi
@@ -17,8 +22,7 @@ from django.contrib.auth import authenticate, login, logout
from django.contrib.auth.models import User
from django.contrib.auth.decorators import login_required
# Project
# from .models import Profile
from rowingdata import rowingdata
import pandas as pd
import numpy as np
@@ -27,9 +31,9 @@ import sys
import urllib
from requests import Request, Session
from rowsandall_app.settings import C2_CLIENT_ID, C2_REDIRECT_URI, C2_CLIENT_SECRET
# Custom error class - to raise a NoTokenError
class C2NoTokenError(Exception):
def __init__(self,value):
self.value=value
@@ -37,8 +41,8 @@ class C2NoTokenError(Exception):
def __str__(self):
return repr(self.value)
# Custom exception handler, returns a 401 HTTP message
# with exception details in the json data
def custom_exception_handler(exc,message):
response = {
@@ -56,7 +60,7 @@ def custom_exception_handler(exc,message):
return res
# Check if workout is owned by this user
def checkworkoutuser(user,workout):
try:
r = Rower.objects.get(user=user)
@@ -64,11 +68,12 @@ def checkworkoutuser(user,workout):
except Rower.DoesNotExist:
return(False)
# convert datetime object to seconds
def makeseconds(t):
seconds = t.hour*3600.+t.minute*60.+t.second+0.1*int(t.microsecond/1.e5)
return seconds
# convert our weight class code to Concept2 weight class code
def c2wc(weightclass):
if (weightclass=="lwt"):
res = "L"
@@ -77,7 +82,9 @@ def c2wc(weightclass):
return res
# Concept2 logbook sends over split data for each interval
# We use it here to generate a custom summary
# Some users complained about small differences
def summaryfromsplitdata(splitdata,data,filename,sep='|'):
totaldist = data['distance']
@@ -177,6 +184,8 @@ def summaryfromsplitdata(splitdata,data,filename,sep='|'):
return sums,sa,results
# Not used now. Could be used to add workout split data to Concept2
# logbook but needs to be reviewed.
def createc2workoutdata_as_splits(w):
filename = w.csvfilename
row = rowingdata(filename)
@@ -216,7 +225,6 @@ def createc2workoutdata_as_splits(w):
data = {
"type": w.workouttype,
# "date": str(w.date)+" "+str(w.starttime),
"date": w.startdatetime.isoformat(),
"distance": int(w.distance),
"time": int(10*makeseconds(durationstr)),
@@ -233,59 +241,8 @@ def createc2workoutdata_as_splits(w):
return data
def createc2workoutdata_grouped(w):
filename = w.csvfilename
row = rowingdata(filename)
# resize per minute
df = row.df.groupby(lambda x:x/10).mean()
averagehr = int(df[' HRCur (bpm)'].mean())
maxhr = int(df[' HRCur (bpm)'].max())
# adding diff, trying to see if this is valid
t = 10*df.ix[:,' ElapsedTime (sec)'].values
t[0] = t[1]
d = df.ix[:,' Horizontal (meters)'].values
d[0] = d[1]
p = 10*df.ix[:,' Stroke500mPace (sec/500m)'].values
t = t.astype(int)
d = d.astype(int)
p = p.astype(int)
spm = df[' Cadence (stokes/min)'].astype(int)
spm[0] = spm[1]
hr = df[' HRCur (bpm)'].astype(int)
stroke_data = []
for i in range(len(t)):
thisrecord = {"t":t[i],"d":d[i],"p":p[i],"spm":spm[i],"hr":hr[i]}
stroke_data.append(thisrecord)
try:
durationstr = datetime.strptime(str(w.duration),"%H:%M:%S.%f")
except ValueError:
durationstr = datetime.strptime(str(w.duration),"%H:%M:%S")
data = {
"type": w.workouttype,
# "date": str(w.date)+" "+str(w.starttime),
"date": w.startdatetime.isoformat(),
"distance": int(w.distance),
"time": int(10*makeseconds(durationstr)),
"weight_class": c2wc(w.weightcategory),
"timezone": "Etc/UTC",
"comments": w.notes,
"heart_rate": {
"average": averagehr,
"max": maxhr,
},
"stroke_data": stroke_data,
}
return data
# Create the Data object for the stroke data to be sent to Concept2 logbook
# API
def createc2workoutdata(w):
filename = w.csvfilename
row = rowingdata(filename)
@@ -318,7 +275,6 @@ def createc2workoutdata(w):
data = {
"type": w.workouttype,
# "date": str(w.date)+" "+str(w.starttime),
"date": w.startdatetime.isoformat(),
"timezone": "Etc/UTC",
"distance": int(w.distance),
@@ -335,6 +291,7 @@ def createc2workoutdata(w):
return data
# Refresh Concept2 authorization token
def do_refresh_token(refreshtoken):
scope = "results:write,user:read"
client_auth = requests.auth.HTTPBasicAuth(C2_CLIENT_ID, C2_CLIENT_SECRET)
@@ -347,10 +304,6 @@ def do_refresh_token(refreshtoken):
url = "https://log.concept2.com/oauth/access_token"
s = Session()
req = Request('POST',url, data=post_data, headers=headers)
# response = requests.post("https://log.concept2.com/oauth/access_token",
# data=post_data,
# data=post_data,
# headers=headers)
prepped = req.prepare()
prepped.body+="&scope="
@@ -374,13 +327,12 @@ def do_refresh_token(refreshtoken):
return [thetoken,expires_in,refresh_token]
# Exchange authorization code for authorization token
def get_token(code):
scope = "user:read,results:write"
client_auth = requests.auth.HTTPBasicAuth(C2_CLIENT_ID, C2_CLIENT_SECRET)
post_data = {"grant_type": "authorization_code",
"code": code,
# "scope": scope,
"redirect_uri": C2_REDIRECT_URI,
"client_secret": C2_CLIENT_SECRET,
"client_id":C2_CLIENT_ID,
@@ -396,9 +348,6 @@ def get_token(code):
response = s.send(prepped)
# response = requests.post("https://log.concept2.com/oauth/access_token",
# data=post_data,
# headers=headers)
token_json = response.json()
thetoken = token_json['access_token']
expires_in = token_json['expires_in']
@@ -406,6 +355,7 @@ def get_token(code):
return [thetoken,expires_in,refresh_token]
# Make URL for authorization and load it
def make_authorization_url(request):
# Generate a random string for the state parameter
# Save it for use later to prevent xsrf attacks
@@ -421,6 +371,7 @@ def make_authorization_url(request):
return HttpResponseRedirect(url)
# Get workout from C2 ID
def get_c2_workout(user,c2id):
r = Rower.objects.get(user=user)
if (r.c2token == '') or (r.c2token is None):
@@ -440,6 +391,7 @@ def get_c2_workout(user,c2id):
return s
# Get stroke data belonging to C2 ID
def get_c2_workout_strokes(user,c2id):
r = Rower.objects.get(user=user)
if (r.c2token == '') or (r.c2token is None):
@@ -459,6 +411,8 @@ def get_c2_workout_strokes(user,c2id):
return s
# Get list of C2 workouts. We load only the first page,
# assuming that users don't want to import their old workouts
def get_c2_workout_list(user):
r = Rower.objects.get(user=user)
if (r.c2token == '') or (r.c2token is None):
@@ -479,7 +433,8 @@ def get_c2_workout_list(user):
return s
# Get username, having access token.
# Handy for checking if the API access is working
def get_username(access_token):
authorizationstring = str('Bearer ' + access_token)
headers = {'Authorization': authorizationstring,
@@ -495,6 +450,8 @@ def get_username(access_token):
return me_json['data']['username']
# Get user id, having access token
# Handy for checking if the API access is working
def get_userid(access_token):
authorizationstring = str('Bearer ' + access_token)
headers = {'Authorization': authorizationstring,
@@ -510,6 +467,7 @@ def get_userid(access_token):
return me_json['data']['id']
# For debugging purposes
def process_callback(request):
# need error handling
@@ -521,6 +479,7 @@ def process_callback(request):
return HttpResponse("got a user name: %s" % username)
# Uploading workout
def workout_c2_upload(user,w):
response = 'trying C2 upload'
r = Rower.objects.get(user=user)
@@ -535,8 +494,6 @@ def workout_c2_upload(user,w):
if (checkworkoutuser(user,w)):
c2userid = get_userid(r.c2token)
data = createc2workoutdata(w)
# if (w.workouttype=='water'):
# data = createc2workoutdata_as_splits(w)
authorizationstring = str('Bearer ' + r.c2token)
headers = {'Authorization': authorizationstring,
'user-agent': 'sanderroosendaal',
@@ -554,6 +511,7 @@ def workout_c2_upload(user,w):
return response
# This is token refresh. Looks for tokens in our database, then refreshes
def rower_c2_token_refresh(user):
r = Rower.objects.get(user=user)
res = do_refresh_token(r.c2refreshtoken)

View File

@@ -4,6 +4,9 @@ import os
from celery import Celery
# Only used for testing with Celery on localhost. RQ is not available
# on Windows, so I use Celery on my notebook.
# set the default Django settings module for the 'celery' program.
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'rowsandall_app.settings')

View File

@@ -1,3 +1,5 @@
# All the data preparation, data cleaning and data mangling should
# be defined here
from rowers.models import Workout, User, Rower
from rowingdata import rowingdata as rrdata
@@ -37,11 +39,13 @@ database_url = 'mysql://{user}:{password}@{host}:{port}/{database_name}'.format(
port=port,
)
# Use SQLite local database when we're in debug mode
if settings.DEBUG or user=='':
# database_url = 'sqlite:///db.sqlite3'
database_url = 'sqlite:///'+database_name
# mapping the DB column names to the CSV file column names
columndict = {
'time':'TimeStamp (sec)',
'hr':' HRCur (bpm)',
@@ -63,6 +67,7 @@ from scipy.signal import savgol_filter
import datetime
# A string representation for time deltas
def niceformat(values):
out = []
for v in values:
@@ -71,6 +76,7 @@ def niceformat(values):
return out
# A nice printable format for time delta values
def strfdelta(tdelta):
try:
minutes,seconds = divmod(tdelta.seconds,60)
@@ -87,6 +93,7 @@ def strfdelta(tdelta):
return res
# A nice printable format for pace values
def nicepaceformat(values):
out = []
for v in values:
@@ -96,6 +103,7 @@ def nicepaceformat(values):
return out
# Convert seconds to a Time Delta value, replacing NaN with a 5:50 pace
def timedeltaconv(x):
if not np.isnan(x):
dt = datetime.timedelta(seconds=x)
@@ -105,6 +113,9 @@ def timedeltaconv(x):
return dt
# Create new workout from file and store it in the database
# This routine should be used everywhere in views.py and mailprocessing.pu
# Currently there is code duplication
def new_workout_from_file(r,f2,
workouttype='rower',
title='Workout',
@@ -263,6 +274,9 @@ def new_workout_from_file(r,f2,
return True
# Compare the data from the CSV file and the database
# Currently only calculates number of strokes. To be expanded with
# more elaborate testing if needed
def compare_data(id):
row = Workout.objects.get(id=id)
f1 = row.csvfilename
@@ -288,6 +302,8 @@ def compare_data(id):
ldb = l2
return l1==l2,ldb,lfile
# Repair data for workouts where the CSV file is lost (or the DB entries
# don't exist)
def repair_data(verbose=False):
ws = Workout.objects.all()
for w in ws:
@@ -319,6 +335,7 @@ def repair_data(verbose=False):
print str(sys.exc_info()[0])
pass
# A wrapper around the rowingdata class, with some error catching
def rdata(file,rower=rrower()):
try:
res = rrdata(file,rower=rower)
@@ -330,6 +347,7 @@ def rdata(file,rower=rrower()):
return res
# Remove all stroke data for workout ID from database
def delete_strokedata(id):
engine = create_engine(database_url, echo=False)
query = sa.text('DELETE FROM strokedata WHERE workoutid={id};'.format(
@@ -343,10 +361,12 @@ def delete_strokedata(id):
conn.close()
engine.dispose()
# Replace stroke data in DB with data from CSV file
def update_strokedata(id,df):
delete_strokedata(id)
rowdata = dataprep(df,id=id,bands=True,barchart=True,otwpower=True)
# Test that all data are of a numerical time
def testdata(time,distance,pace,spm):
t1 = np.issubdtype(time,np.number)
t2 = np.issubdtype(distance,np.number)
@@ -355,6 +375,8 @@ def testdata(time,distance,pace,spm):
return t1 and t2 and t3 and t4
# Get data from DB for one workout (fetches all data). If data
# is not in DB, read from CSV file (and create DB entry)
def getrowdata_db(id=0):
data = read_df_sql(id)
data['x_right'] = data['x_right']/1.0e6
@@ -369,12 +391,14 @@ def getrowdata_db(id=0):
return data,row
# Fetch a subset of the data from the DB
def getsmallrowdata_db(columns,ids=[]):
prepmultipledata(ids)
data = read_cols_df_sql(ids,columns)
return data
# Fetch both the workout and the workout stroke data (from CSV file)
def getrowdata(id=0):
# check if valid ID exists (workout exists)
@@ -395,7 +419,12 @@ def getrowdata(id=0):
return rowdata,row
# Checks if all rows for a list of workout IDs have entries in the
# stroke_data table. If this is not the case, it creates the stroke
# data
# In theory, this should never yield any work, but it's a good
# safety net for programming errors elsewhere in the app
# Also used heavily when I moved from CSV file only to CSV+Stroke data
def prepmultipledata(ids,verbose=False):
query = sa.text('SELECT DISTINCT workoutid FROM strokedata')
engine = create_engine(database_url, echo=False)
@@ -420,6 +449,8 @@ def prepmultipledata(ids,verbose=False):
data = dataprep(rowdata.df,id=id,bands=True,barchart=True,otwpower=True)
return res
# Read a set of columns for a set of workout ids, returns data as a
# pandas dataframe
def read_cols_df_sql(ids,columns):
columns = list(columns)+['distance','spm']
columns = [x for x in columns if x != 'None']
@@ -450,7 +481,7 @@ def read_cols_df_sql(ids,columns):
engine.dispose()
return df
# Read stroke data from the DB for a Workout ID. Returns a pandas dataframe
def read_df_sql(id):
engine = create_engine(database_url, echo=False)
@@ -460,10 +491,8 @@ def read_df_sql(id):
engine.dispose()
return df
# Get the necessary data from the strokedata table in the DB.
# For the flex plot
def smalldataprep(therows,xparam,yparam1,yparam2):
df = pd.DataFrame()
if yparam2 == 'None':
@@ -503,7 +532,10 @@ def smalldataprep(therows,xparam,yparam1,yparam2):
return df
# This is the main routine.
# it reindexes, sorts, filters, and smooths the data, then
# saves it to the stroke_data table in the database
# Takes a rowingdata object's DataFrame as input
def dataprep(rowdatadf,id=0,bands=True,barchart=True,otwpower=True,
empower=True):
rowdatadf.set_index([range(len(rowdatadf))],inplace=True)

View File

@@ -1,3 +1,5 @@
# This is Data prep used for testing purposes (no Django environment)
# Uses the debug SQLite database for stroke data
from rowingdata import rowingdata as rrdata
from rowingdata import rower as rrower

View File

@@ -27,7 +27,7 @@ from rowsandall_app.settings import C2_CLIENT_ID, C2_REDIRECT_URI, C2_CLIENT_SEC
from rowsandall_app.settings import SPORTTRACKS_CLIENT_ID, SPORTTRACKS_REDIRECT_URI, SPORTTRACKS_CLIENT_SECRET
import requests
import json
from rowsandall_app.rows import handle_uploaded_file
from rowers.rows import handle_uploaded_file
from rowers.tasks import handle_makeplot,handle_otwsetpower,handle_sendemailtcx
from scipy.signal import savgol_filter
@@ -60,6 +60,7 @@ import plots
from io import BytesIO
from scipy.special import lambertw
# used in shell to send a newsletter to all Rowers
def emailall(emailfile,subject):
rowers = Rower.objects.all()
for rower in rowers:

View File

@@ -1,6 +1,6 @@
from django import forms
from rowers.models import Workout
from rowsandall_app.rows import validate_file_extension,must_be_csv
from rowers.rows import validate_file_extension,must_be_csv
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User
from django.contrib.admin.widgets import AdminDateWidget
@@ -9,10 +9,12 @@ from django.utils import timezone,translation
import datetime
# login form
class LoginForm(forms.Form):
username = forms.CharField()
password = forms.CharField(widget=forms.PasswordInput())
# simple form for Contact page. Sends email to info@rowsandall.com
class EmailForm(forms.Form):
firstname = forms.CharField(max_length=255)
lastname = forms.CharField(max_length=255)
@@ -21,16 +23,21 @@ class EmailForm(forms.Form):
botcheck = forms.CharField(max_length=5)
message = forms.CharField()
# Upload the CrewNerd Summary CSV
class CNsummaryForm(forms.Form):
file = forms.FileField(required=True,validators=[must_be_csv])
# The little window to type '4x2000m/500m' to update the workout summary
class SummaryStringForm(forms.Form):
intervalstring = forms.CharField(max_length=255,label='Workout Description')
# Used for testing the POST API for StrokeData
class StrokeDataForm(forms.Form):
strokedata = forms.CharField(label='payload',
widget=forms.Textarea)
# The form used for uploading files
class DocumentsForm(forms.Form):
filetypechoices = (
('csv' , 'Painsled iOS CSV'),
@@ -50,9 +57,6 @@ class DocumentsForm(forms.Form):
workouttype = forms.ChoiceField(required=True,
choices=Workout.workouttypes,
initial='rower')
# fileformat = forms.ChoiceField(required=True,
# choices=filetypechoices,
# initial='csv')
notes = forms.CharField(required=False,
widget=forms.Textarea)
@@ -60,7 +64,8 @@ class DocumentsForm(forms.Form):
fields = ['title','file','workouttype','fileformat']
# The form to indicate additional actions to be performed immediately
# after a successful upload
class UploadOptionsForm(forms.Form):
plotchoices = (
('timeplot','Time Plot'),
@@ -76,6 +81,8 @@ class UploadOptionsForm(forms.Form):
class Meta:
fields = ['make_plot','plottype','upload_toc2']
# This form is used on the Analysis page to add a custom distance/time
# trial and predict the pace
class PredictedPieceForm(forms.Form):
unitchoices = (
('t','minutes'),
@@ -88,6 +95,7 @@ class PredictedPieceForm(forms.Form):
class Meta:
fields = ['value','pieceunit']
# On the Geeky side, to update stream information for river dwellers
class UpdateStreamForm(forms.Form):
unitchoices = (
('m','m/s'),
@@ -107,6 +115,7 @@ class UpdateStreamForm(forms.Form):
class Meta:
fields = ['dist1','dist2','stream1', 'stream2','streamunit']
# add wind information to your workout
class UpdateWindForm(forms.Form):
unitchoices = (
('m','m/s'),
@@ -134,8 +143,7 @@ class UpdateWindForm(forms.Form):
'windunit',
'winddirection1','winddirection2']
# Form to select a data range to show workouts from a certain time period
class DateRangeForm(forms.Form):
startdate = forms.DateField(initial=timezone.now()-datetime.timedelta(days=365),
widget=SelectDateWidget(years=range(1990,2050)),
@@ -147,6 +155,7 @@ class DateRangeForm(forms.Form):
class Meta:
fields = ['startdate','enddate']
# Form used to select workouts for the past N days
class DeltaDaysForm(forms.Form):
deltadays = forms.IntegerField(initial=0,required=False,label='')
@@ -191,13 +200,14 @@ class RegistrationFormUniqueEmail(RegistrationFormTermsOfService):
raise forms.ValidationError("This email address is already in use. Please supply a different email address.")
return self.cleaned_data['email']
# Time field supporting microseconds. Not used, I believe.
class MyTimeField(forms.TimeField):
def __init__(self, *args, **kwargs):
super(MyTimeField, self).__init__(*args, **kwargs)
supports_microseconds = True
# Form used to update interval stats
class IntervalUpdateForm(forms.Form):
def __init__(self, *args, **kwargs):

View File

@@ -1,3 +1,4 @@
# Processes emails sent to workouts@rowsandall.com
import time
from django.conf import settings
from rowers.tasks import handle_sendemail_unrecognized
@@ -21,7 +22,9 @@ from scipy.signal import savgol_filter
import zipfile
import os
import rowers.dataprep as dataprep
# Sends a confirmation with a link to the workout
def send_confirm(u,name,link):
fullemail = u.email
subject = 'Workout added: '+name
@@ -38,6 +41,7 @@ def send_confirm(u,name,link):
return 1
# Reads a "rowingdata" object, plus some error protections
def rdata(file,rower=rrower()):
try:
res = rrdata(file,rower=rower)
@@ -49,13 +53,18 @@ def rdata(file,rower=rrower()):
return res
# Some error protection around process attachments
def safeprocessattachments():
try:
return processattachments()
except:
return [0]
# This is duplicated in management/commands/processemail
# Need to double check the code there, update here, and only
# use the code here.
def processattachments():
# in res, we store the ids of the new workouts
res = []
attachments = MessageAttachment.objects.all()
for a in attachments:
@@ -73,7 +82,6 @@ def processattachments():
try:
wid = [make_new_workout_from_email(rr,a.document,name)]
res += wid
print wid
link = 'https://rowsandall.com/rowers/workout/'+str(wid[0])+'/edit'
dd = send_confirm(u,name,link)
except:
@@ -92,6 +100,7 @@ def processattachments():
# no attachments, so can be deleted
m.delete()
# Delete remaining messages (which should not have attachments)
mm = Message.objects.all()
for m in mm:
if m.attachments.exists()==False:
@@ -99,6 +108,7 @@ def processattachments():
return res
# As above, but with some print commands for debugging purposes
def processattachments_debug():
res = []
attachments = MessageAttachment.objects.all()
@@ -144,7 +154,9 @@ def processattachments_debug():
return res
# Process the attachment file, create new workout
# The code here is duplication of the code in views.py (workout_upload_view)
# Need to move the code to a subroutine used both in views.py and here
def make_new_workout_from_email(rr,f2,name,cntr=0):
workouttype = 'rower'
f2 = f2.name
@@ -305,6 +317,16 @@ def make_new_workout_from_email(rr,f2,name,cntr=0):
startdatetime=row.rowdatetime)
w.save()
print w.id
print row.df.info()
# put stroke data in database
res = dataprep.dataprep(row.df,id=w.id,
bands=True,barchart=True,
otwpower=True,empower=True)
return w.id

View File

@@ -18,6 +18,8 @@ from sqlite3 import OperationalError
from django.utils import timezone
import datetime
from rowers.rows import validate_file_extension
from rowsandall_app.settings import (
TWEET_ACCESS_TOKEN_KEY,
TWEET_ACCESS_TOKEN_SECRET,
@@ -47,12 +49,12 @@ database_url = 'mysql://{user}:{password}@{host}:{port}/{database_name}'.format(
if settings.DEBUG or user=='':
database_url = 'sqlite:///db.sqlite3'
# Create your models here.
# For future Team functionality
class Team(models.Model):
name = models.CharField(max_length=150)
notes = models.CharField(blank=True,max_length=200)
# Extension of User with rowing specific data
class Rower(models.Model):
weightcategories = (
('hwt','heavy-weight'),
@@ -95,6 +97,7 @@ class Rower(models.Model):
def __str__(self):
return self.user.username
# Saving a chart as a favorite chart
class FavoriteChart(models.Model):
y1params = (
('hr','Heart Rate'),
@@ -181,6 +184,7 @@ class FavoriteForm(ModelForm):
fields = ['xparam','yparam1','yparam2',
'plottype','workouttype','reststrokes']
# To generate favorite chart forms on the fly
class BaseFavoriteFormSet(BaseFormSet):
def clean(self):
if any(self.errors):
@@ -209,6 +213,7 @@ class BaseFavoriteFormSet(BaseFormSet):
if not yparam2:
yparam2 = 'None'
# Workout
class Workout(models.Model):
workouttypes = (
('water','On-water'),
@@ -273,7 +278,7 @@ def auto_delete_file_on_delete(sender, instance, **kwargs):
if os.path.isfile(instance.csvfilename+'.gz'):
os.remove(instance.csvfilename+'.gz')
# Delete stroke data from the database when a workout is deleted
@receiver(models.signals.post_delete,sender=Workout)
def auto_delete_strokedata_on_delete(sender, instance, **kwargs):
if instance.id:
@@ -289,6 +294,11 @@ def auto_delete_strokedata_on_delete(sender, instance, **kwargs):
conn.close()
engine.dispose()
# Model of StrokeData table
# the definition here is used only to enable easy Django migration
# when the StrokeData are expanded.
# No Django Instances of this model are managed. Strokedata table is
# accesssed directly with SQL commands
class StrokeData(models.Model):
class Meta:
db_table = 'strokedata'
@@ -330,6 +340,7 @@ class StrokeData(models.Model):
wash = models.FloatField(default=0,null=True)
peakforceangle = models.FloatField(default=0,null=True)
# A wrapper around the png files
class GraphImage(models.Model):
filename = models.CharField(default='',max_length=150,blank=True,null=True)
creationdatetime = models.DateTimeField()
@@ -338,7 +349,7 @@ class GraphImage(models.Model):
def __str__(self):
return self.filename
# delete related file object
# delete related file object when image is deleted
@receiver(models.signals.post_delete,sender=GraphImage)
def auto_delete_image_on_delete(sender,instance, **kwargs):
if instance.filename:
@@ -347,11 +358,11 @@ def auto_delete_image_on_delete(sender,instance, **kwargs):
else:
print "couldn't find the file "+instance.filename
# Date input utility
class DateInput(forms.DateInput):
input_type = 'date'
# Form to update Workout data
class WorkoutForm(ModelForm):
duration = forms.TimeInput(format='%H:%M:%S.%f')
class Meta:
@@ -368,16 +379,20 @@ class WorkoutForm(ModelForm):
if self.instance.workouttype != 'water':
del self.fields['boattype']
# Used for the rowing physics calculations
class AdvancedWorkoutForm(ModelForm):
class Meta:
model = Workout
fields = ['boattype','weightvalue']
# Simple form to set rower's Functional Threshold Power
class RowerPowerForm(ModelForm):
class Meta:
model = Rower
fields = ['ftp']
# Form to set rower's Heart Rate zones, including test routines
# to enable consistency
class RowerForm(ModelForm):
class Meta:
model = Rower
@@ -519,6 +534,8 @@ class RowerForm(ModelForm):
if an>=max:
raise forms.ValidationError("AN should be lower than Max")
# An announcement that goes to the right of the workouts list
# optionally sends a tweet to our twitter account
class SiteAnnouncement(models.Model):
created = models.DateField(default=timezone.now)
announcement = models.TextField(max_length=140)

View File

@@ -1,3 +1,5 @@
# Interactions with Rowsandall.com API. Not fully complete.
# Python
import oauth2 as oauth
import cgi

View File

@@ -1,3 +1,5 @@
# Defines permissions for objects (API related)
from rest_framework import permissions
from rowers.models import Rower

View File

@@ -3,6 +3,7 @@ import matplotlib.pyplot as plt
import numpy as np
# Make pace ticks for pace axis '2:00', '1:50', etc
def format_pace_tick(x,pos=None):
min=int(x/60)
sec=int(x-min*60.)
@@ -10,6 +11,7 @@ def format_pace_tick(x,pos=None):
template='%d:%s'
return template % (min,sec_str)
# Returns a pace string from a pace in seconds/500m, '1:45.4'
def format_pace(x,pos=None):
if isinf(x) or isnan(x):
x=0
@@ -24,6 +26,7 @@ def format_pace(x,pos=None):
return str1
# Returns a time string from a time in seconds
def format_time(x,pos=None):
@@ -37,11 +40,13 @@ def format_time(x,pos=None):
return str1
# Formatting the distance tick marks
def format_dist_tick(x,pos=None):
km = x/1000.
template='%6.3f'
return template % (km)
# Formatting the time tick marks (h:mm)
def format_time_tick(x,pos=None):
hour=int(x/3600)
min=int((x-hour*3600.)/60)
@@ -49,6 +54,11 @@ def format_time_tick(x,pos=None):
template='%d:%s'
return template % (hour,min_str)
# Utility to select reasonable y axis range
# Basically the data range plus some padding, but with ultimate
# you can set the slowest paces to fall off the axis.
# Useful for OTW rowing where you sometimes stops and pace runs out of
# the boundaries
def y_axis_range(ydata,miny=0,padding=.1,ultimate=[-1e9,1e9]):
# ydata must by a numpy array
@@ -86,6 +96,7 @@ def y_axis_range(ydata,miny=0,padding=.1,ultimate=[-1e9,1e9]):
return [yrangemin,yrangemax]
# Make a plot (this one is only used for testing)
def mkplot(row,title):
df = row.df

View File

@@ -71,13 +71,3 @@ def handle_uploaded_file(f):
return fname,fname2
# this might work on windows
#import magic
#def validate_mime_type(value):
# supported_types=['text/csv','application/vnd.garmin.tcx+xml']
# with magic.Magic(flags=magic.MAGIC_MIME_TYPE) as m:
# mime_type=m.id_buffer(value.file.read())
# value.file.seek(0)
# if mime_type not in supported_types:
# raise ValidationError(u'Unsupported file type.')

View File

@@ -1,3 +1,6 @@
# Serializers. Defines which fields from an object get to the JSON object
# Also optionally define POST, PATCH methods (create, update)
from rest_framework import serializers
from rowers.models import Workout,Rower,StrokeData,FavoriteChart
@@ -104,6 +107,7 @@ class WorkoutSerializer(serializers.ModelSerializer):
instance.save()
return instance
# This is just a fake one for URL registration purposes
class StrokeDataSerializer(serializers.Serializer):
workoutid = serializers.IntegerField
strokedata = serializers.JSONField

View File

@@ -1,3 +1,5 @@
# All the functionality to connect to SportTracks
# Python
import oauth2 as oauth
import cgi
@@ -31,6 +33,8 @@ from rowers.models import Rower,Workout
from rowsandall_app.settings import C2_CLIENT_ID, C2_REDIRECT_URI, C2_CLIENT_SECRET, STRAVA_CLIENT_ID, STRAVA_REDIRECT_URI, STRAVA_CLIENT_SECRET, SPORTTRACKS_CLIENT_SECRET, SPORTTRACKS_CLIENT_ID, SPORTTRACKS_REDIRECT_URI
# Custom exception handler, returns a 401 HTTP message
# with exception details in the json data
def custom_exception_handler(exc,message):
response = {
@@ -48,6 +52,7 @@ def custom_exception_handler(exc,message):
return res
# Refresh ST token using refresh token
def do_refresh_token(refreshtoken):
client_auth = requests.auth.HTTPBasicAuth(SPORTTRACKS_CLIENT_ID, SPORTTRACKS_CLIENT_SECRET)
post_data = {"grant_type": "refresh_token",
@@ -75,7 +80,7 @@ def do_refresh_token(refreshtoken):
return [thetoken,expires_in,refresh_token]
# Exchange ST access code for long-lived ST access token
def get_token(code):
client_auth = requests.auth.HTTPBasicAuth(SPORTTRACKS_CLIENT_ID, SPORTTRACKS_CLIENT_SECRET)
post_data = {"grant_type": "authorization_code",
@@ -100,6 +105,7 @@ def get_token(code):
return [thetoken,expires_in,refresh_token]
# Make authorization URL including random string
def make_authorization_url(request):
# Generate a random string for the state parameter
# Save it for use later to prevent xsrf attacks
@@ -118,7 +124,7 @@ def make_authorization_url(request):
return HttpResponseRedirect(url)
# This is token refresh. Looks for tokens in our database, then refreshes
def rower_sporttracks_token_refresh(user):
r = Rower.objects.get(user=user)
res = do_refresh_token(r.sporttracksrefreshtoken)
@@ -135,6 +141,7 @@ def rower_sporttracks_token_refresh(user):
r.save()
return r.sporttrackstoken
# Get list of workouts available on SportTracks
def get_sporttracks_workout_list(user):
r = Rower.objects.get(user=user)
if (r.sporttrackstoken == '') or (r.sporttrackstoken is None):
@@ -154,7 +161,7 @@ def get_sporttracks_workout_list(user):
return s
# Get workout summary data by SportTracks ID
def get_sporttracks_workout(user,sporttracksid):
r = Rower.objects.get(user=user)
if (r.sporttrackstoken == '') or (r.sporttrackstoken is None):
@@ -174,6 +181,7 @@ def get_sporttracks_workout(user,sporttracksid):
return s
# Create Workout Data for upload to SportTracks
def createsporttracksworkoutdata(w):
filename = w.csvfilename
try:
@@ -272,6 +280,8 @@ def createsporttracksworkoutdata(w):
return data
# Obtain SportTracks Workout ID from the response returned on successful
# upload
def getidfromresponse(response):
t = json.loads(response.text)
uri = t['uris'][0]

View File

@@ -1,3 +1,5 @@
# All the functionality needed to connect to Strava
# Python
import oauth2 as oauth
import cgi
@@ -30,6 +32,9 @@ import stravalib
from rowsandall_app.settings import C2_CLIENT_ID, C2_REDIRECT_URI, C2_CLIENT_SECRET, STRAVA_CLIENT_ID, STRAVA_REDIRECT_URI, STRAVA_CLIENT_SECRET
# Exponentially weighted moving average
# Used for data smoothing of the jagged data obtained by Strava
# See bitbucket issue 72
def ewmovingaverage(interval,window_size):
# Experimental code using Exponential Weighted moving average
@@ -45,45 +50,11 @@ def ewmovingaverage(interval,window_size):
return interval2
def geo_distance(lat1,lon1,lat2,lon2):
""" Approximate distance and bearing between two points
defined by lat1,lon1 and lat2,lon2
This is a slight underestimate but is close enough for our purposes,
We're never moving more than 10 meters between trackpoints
Bearing calculation fails if one of the points is a pole.
"""
# radius of earth in km
R = 6373.0
# pi
pi = math.pi
lat1 = math.radians(lat1)
lat2 = math.radians(lat2)
lon1 = math.radians(lon1)
lon2 = math.radians(lon2)
dlon = lon2 - lon1
dlat = lat2 - lat1
a = sin(dlat / 2)**2 + cos(lat1) * cos(lat2) * sin(dlon / 2)**2
c = 2 * atan2(sqrt(a), sqrt(1 - a))
distance = R * c
tc1 = atan2(sin(lon2-lon1)*cos(lat2),
cos(lat1)*sin(lat2)-sin(lat1)*cos(lat2)*cos(lon2-lon1))
tc1 = tc1 % (2*pi)
bearing = math.degrees(tc1)
return [distance,bearing]
from utils import geo_distance
# Custom exception handler, returns a 401 HTTP message
# with exception details in the json data
def custom_exception_handler(exc,message):
response = {
@@ -101,6 +72,7 @@ def custom_exception_handler(exc,message):
return res
# Exchange access code for long-lived access token
def get_token(code):
client_auth = requests.auth.HTTPBasicAuth(STRAVA_CLIENT_ID, STRAVA_CLIENT_SECRET)
post_data = {"grant_type": "authorization_code",
@@ -118,7 +90,7 @@ def get_token(code):
return [thetoken]
# Make authorization URL including random string
def make_authorization_url(request):
# Generate a random string for the state parameter
# Save it for use later to prevent xsrf attacks
@@ -134,6 +106,7 @@ def make_authorization_url(request):
return HttpResponseRedirect(url)
# Get list of workouts available on Strava
def get_strava_workout_list(user):
r = Rower.objects.get(user=user)
if (r.stravatoken == '') or (r.stravatoken is None):
@@ -150,6 +123,7 @@ def get_strava_workout_list(user):
return s
# Get a Strava workout summary data and stroke data by ID
def get_strava_workout(user,stravaid):
r = Rower.objects.get(user=user)
if (r.stravatoken == '') or (r.stravatoken is None):
@@ -236,6 +210,7 @@ def get_strava_workout(user,stravaid):
return [workoutsummary,df]
# Generate Workout data for Strava (a TCX file)
def createstravaworkoutdata(w):
filename = w.csvfilename
try:
@@ -247,6 +222,8 @@ def createstravaworkoutdata(w):
return tcxfilename
# Upload the TCX file to Strava and set the workout activity type
# to rowing on Strava
def handle_stravaexport(file,workoutname,stravatoken,description=''):
# w = Workout.objects.get(id=workoutid)
client = stravalib.Client(access_token=stravatoken)
@@ -264,12 +241,6 @@ def handle_stravaexport(file,workoutname,stravatoken,description=''):
errorlog.write(timestr+errorstring+"\r\n")
errorlog.write("stravastuff.py line 262\r\n")
# w.uploadedtostrava = res.id
# w.save()
# file.close()
return res.id

View File

@@ -21,12 +21,12 @@ from rowers.dataprepnodjango import update_strokedata
from django.core.mail import send_mail, BadHeaderError,EmailMessage
# testing task
@app.task
def add(x, y):
return x + y
# send email to me when an unrecognized file is uploaded
@app.task
def handle_sendemail_unrecognized(unrecognizedfile,useremail):
@@ -51,7 +51,7 @@ def handle_sendemail_unrecognized(unrecognizedfile,useremail):
os.remove(unrecognizedfile)
return 1
# Send email with TCX attachment
@app.task
def handle_sendemailtcx(first_name,last_name,email,tcxfile):
@@ -75,6 +75,7 @@ def handle_sendemailtcx(first_name,last_name,email,tcxfile):
os.remove(tcxfile)
return 1
# Send email with CSV attachment
@app.task
def handle_sendemailcsv(first_name,last_name,email,csvfile):
@@ -104,6 +105,7 @@ def handle_sendemailcsv(first_name,last_name,email,csvfile):
return 1
# Calculate wind and stream corrections for OTW rowing
@app.task
def handle_otwsetpower(f1,boattype,weightvalue,
first_name,last_name,email,workoutid,
@@ -129,7 +131,7 @@ def handle_otwsetpower(f1,boattype,weightvalue,
except KeyError:
rg = rowingdata.getrigging('static/rigging/1x.txt')
# do calculation
# do calculation, but do not overwrite NK Empower Power data
powermeasured = False
try:
w = rowdata.df['wash']
@@ -166,6 +168,7 @@ def handle_otwsetpower(f1,boattype,weightvalue,
return 1
# This function generates all the static (PNG image) plots
@app.task
def handle_makeplot(f1,f2,t,hrdata,plotnr,imagename):
hrmax = hrdata['hrmax']
@@ -222,5 +225,6 @@ def handle_makeplot(f1,f2,t,hrdata,plotnr,imagename):
return imagename
# Another simple task for debugging purposes
def add2(x,y):
return x+y

View File

@@ -1,3 +1,59 @@
# This is just a scratch pad to temporarily park code, just in case I need
# it later. Hardly used since i have proper versioning
#
def createc2workoutdata_grouped(w):
filename = w.csvfilename
row = rowingdata(filename)
# resize per minute
df = row.df.groupby(lambda x:x/10).mean()
averagehr = int(df[' HRCur (bpm)'].mean())
maxhr = int(df[' HRCur (bpm)'].max())
# adding diff, trying to see if this is valid
t = 10*df.ix[:,' ElapsedTime (sec)'].values
t[0] = t[1]
d = df.ix[:,' Horizontal (meters)'].values
d[0] = d[1]
p = 10*df.ix[:,' Stroke500mPace (sec/500m)'].values
t = t.astype(int)
d = d.astype(int)
p = p.astype(int)
spm = df[' Cadence (stokes/min)'].astype(int)
spm[0] = spm[1]
hr = df[' HRCur (bpm)'].astype(int)
stroke_data = []
for i in range(len(t)):
thisrecord = {"t":t[i],"d":d[i],"p":p[i],"spm":spm[i],"hr":hr[i]}
stroke_data.append(thisrecord)
try:
durationstr = datetime.strptime(str(w.duration),"%H:%M:%S.%f")
except ValueError:
durationstr = datetime.strptime(str(w.duration),"%H:%M:%S")
data = {
"type": w.workouttype,
"date": w.startdatetime.isoformat(),
"distance": int(w.distance),
"time": int(10*makeseconds(durationstr)),
"weight_class": c2wc(w.weightcategory),
"timezone": "Etc/UTC",
"comments": w.notes,
"heart_rate": {
"average": averagehr,
"max": maxhr,
},
"stroke_data": stroke_data,
}
return data
@login_required()
def workout_edit_view(request,id=0):
if request.method == 'POST':

View File

@@ -8,7 +8,7 @@ import rowers.interactiveplots as iplots
import datetime
from rowingdata import rowingdata as rdata
from rowingdata import rower as rrower
from rowsandall_app.rows import handle_uploaded_file
from rowers.rows import handle_uploaded_file
from django.core.files.uploadedfile import SimpleUploadedFile
from time import strftime,strptime,mktime,time,daylight
import os
@@ -1037,8 +1037,7 @@ class subroutinetests(TestCase):
jsond = json.dumps(data)
data = c2stuff.createc2workoutdata_as_splits(w)
jsond = json.dumps(data)
data = c2stuff.createc2workoutdata_as_grouped(w)
jsond = json.dumps(data)
class PlotTests(TestCase):

View File

@@ -100,7 +100,6 @@ urlpatterns = [
url(r'^api-docs$', views.schema_view),
url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')),
url(r'^api/workouts/(?P<id>\d+)/strokedata$',views.strokedatajson),
url(r'^testbokeh$',views.testbokeh),
url(r'^500/$', TemplateView.as_view(template_name='500.html'),name='500'),
url(r'^404/$', TemplateView.as_view(template_name='404.html'),name='404'),
url(r'^400/$', TemplateView.as_view(template_name='400.html'),name='400'),
@@ -113,10 +112,6 @@ urlpatterns = [
url(r'^list-workouts/(?P<startdatestring>\w+.*)/(?P<enddatestring>\w+.*)$',views.workouts_view),
url(r'^list-workouts/$',views.workouts_view),
url(r'^list-graphs/$',views.graphs_view),
url(r'^dashboard/c/(?P<message>\w+.*)/$',views.dashboard_view),
url(r'^dashboard/s/(?P<successmessage>\w+.*)/$',views.dashboard_view),
url(r'^dashboard/c/(?P<message>\w+.*)/s/(?P<successmessage>\w+.*)$',views.dashboard_view),
url(r'^dashboard/$',views.dashboard_view),
url(r'^(?P<theuser>\d+)/ote-bests/(?P<startdatestring>\w+.*)/(?P<enddatestring>\w+.*)$',views.rankings_view),
url(r'^(?P<theuser>\d+)/ote-bests/(?P<deltadays>\d+)$',views.rankings_view),
url(r'^ote-bests/(?P<startdatestring>\w+.*)/(?P<enddatestring>\w+.*)$',views.rankings_view),
@@ -194,7 +189,6 @@ urlpatterns = [
url(r'^workout/sporttracksimport/$',views.workout_sporttracksimport_view),
url(r'^workout/sporttracksimport/(\d+)/$',views.workout_getsporttracksworkout_view),
url(r'^workout/(\d+)/deleteconfirm$',views.workout_delete_confirm_view),
url(r'^workout/(\d+)/c2upload/$',views.list_c2_upload_view),
url(r'^workout/(\d+)/c2uploadw/$',views.workout_c2_upload_view),
url(r'^workout/(\d+)/stravauploadw/$',views.workout_strava_upload_view),
url(r'^workout/(\d+)/recalcsummary/$',views.workout_recalcsummary_view),
@@ -232,7 +226,6 @@ urlpatterns = [
if settings.DEBUG:
urlpatterns += [
url(r'^workout/uploadd/$',views.workout_upload_view_debug),
url(r'^testreverse/$',views.test_reverse_view),
url(r'^c2listug/$',views.c2listdebug_view),
]

42
rowers/utils.py Normal file
View File

@@ -0,0 +1,42 @@
import math
def geo_distance(lat1,lon1,lat2,lon2):
""" Approximate distance and bearing between two points
defined by lat1,lon1 and lat2,lon2
This is a slight underestimate but is close enough for our purposes,
We're never moving more than 10 meters between trackpoints
Bearing calculation fails if one of the points is a pole.
(Hey, from the North pole you can walk South, East, North and end up
on the same spot!)
"""
# radius of our earth in km --> should be moved to settings if
# rowing takes off on other planets
R = 6373.0
# pi
pi = math.pi
lat1 = math.radians(lat1)
lat2 = math.radians(lat2)
lon1 = math.radians(lon1)
lon2 = math.radians(lon2)
dlon = lon2 - lon1
dlat = lat2 - lat1
a = sin(dlat / 2)**2 + cos(lat1) * cos(lat2) * sin(dlon / 2)**2
c = 2 * atan2(sqrt(a), sqrt(1 - a))
distance = R * c
tc1 = atan2(sin(lon2-lon1)*cos(lat2),
cos(lat1)*sin(lat2)-sin(lat1)*cos(lat2)*cos(lon2-lon1))
tc1 = tc1 % (2*pi)
bearing = math.degrees(tc1)
return [distance,bearing]

File diff suppressed because it is too large Load Diff

View File

@@ -8,9 +8,8 @@ from rowers.models import Rower, Workout
from rowsandall_app.settings import FORECAST_IO_KEY
# Get weather data from the DarkSky API
def get_weather_data(long,lat,unixtime):
# url = "https://api.forecast.io/forecast/"+FORECAST_IO_KEY+"/"
url = "https://api.darksky.net/forecast/"+FORECAST_IO_KEY+"/"
url += str(long)+","+str(lat)+","+str(unixtime)
@@ -21,6 +20,7 @@ def get_weather_data(long,lat,unixtime):
else:
return 0
# Get wind data (and translate from knots to m/s)
def get_wind_data(lat,long,unixtime):
data = get_weather_data(lat,long,unixtime)
if data:
@@ -39,6 +39,7 @@ def get_wind_data(lat,long,unixtime):
try:
temperature = data['currently']['temperature']
# Temp is given in Fahrenheit, so convert to Celsius for Europeans
temperaturec = (temperature-32.)*(5./9.)
temperaturec = int(10*temperaturec)/10.
except KeyError:
@@ -54,7 +55,8 @@ def get_wind_data(lat,long,unixtime):
windbearing = 0
message = 'Not able to get weather data'
# apply Hellman's coefficient for neutral air above human inhabitated areas
# apply Hellman's coefficient for neutral air above human
# inhabitated areas
windspeed = windspeed*(0.1)**0.34
windspeed = 0.01*int(100*windspeed)

View File

@@ -1,33 +1,5 @@
from django import forms
from rowers.models import Workout
from rows import validate_file_extension
class ContactForm(forms.Form):
subject = forms.CharField()
email = forms.EmailField(required=False)
message = forms.CharField(widget=forms.Textarea)
class DocumentsForm(forms.Form):
filetypechoices = (
('tcx' , 'TCX'),
('csv' , 'Painsled CSV')
)
title = forms.CharField(required=False)
file = forms.FileField(required=True,
validators=[validate_file_extension])
workouttype = forms.ChoiceField(required=True,
choices=Workout.workouttypes,
initial='rower')
fileformat = forms.ChoiceField(required=True,
choices=filetypechoices,
initial='csv')
notes = forms.CharField(required=False,
widget=forms.Textarea)
class Meta:
fields = ['title','file','workouttype','fileformat']

View File

@@ -1,7 +1,3 @@
# -*- coding: utf-8 -*-
from django.db import models
from rows import validate_file_extension
class Document(models.Model):
docfile = models.FileField(upload_to='documents/%Y/%m/%d',
validators=[validate_file_extension])

View File

@@ -1,3 +0,0 @@
def addrc(x,y,z):
return int(x)+int(y)

View File

@@ -1,121 +0,0 @@
"""
Django settings for rowsandall_app project.
Generated by 'django-admin startproject' using Django 1.9.5.
For more information on this file, see
https://docs.djangoproject.com/en/1.9/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/1.9/ref/settings/
"""
import os
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/1.9/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = '&cg#y8h-8s#00ayk#gu)+l43j1j9^9r&qf$3!$x#ov@1houiph'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = []
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
MIDDLEWARE_CLASSES = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'rowsandall_app.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR,'templates')],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'rowsandall_app.wsgi.application'
# Database
# https://docs.djangoproject.com/en/1.9/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
# Password validation
# https://docs.djangoproject.com/en/1.9/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
# Internationalization
# https://docs.djangoproject.com/en/1.9/topics/i18n/
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'Europe/Prague'
USE_I18N = True
USE_L10N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.9/howto/static-files/
STATIC_URL = '/static/'

View File

@@ -1,3 +0,0 @@
graph = {{ my_data|safe }};
mpld3.draw_figure("fig01", graph);

View File

@@ -17,9 +17,7 @@ from django.conf.urls import url,include
from django.conf import settings
from django.conf.urls.static import static
from django.contrib import admin
from rowsandall_app.views import rootview,version,rowingdata,showStaticImage,\
add,wait,nrowingdata,waitforplot,showplot,interactiveplot
from rowsandall_app.views import uploadfile
from rowsandall_app.views import rootview
from django.contrib.auth import views as auth_views
from rowers import views as rowersviews
@@ -28,8 +26,6 @@ urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')),
url(r'^$',rootview),
url(r'^version/$',version),
url(r'^addresult/(.+.*)/$',wait),
url(r'^login/',auth_views.login, name='login'),
url(r'^logout/',auth_views.logout_then_login,name='logout'),
url(r'^password_change_done/$',auth_views.password_change_done,name='password_change_done'),
@@ -42,12 +38,10 @@ urlpatterns = [
url(r'^password_reset_complete/$',auth_views.password_reset_complete,name='password_reset_complete'),
url(r'^rowers/',include('rowers.urls')),
url(r'^cvkbrno/',include('cvkbrno.urls')),
url(r'^add/(\d+)/(\d+)/$',add),
url(r'^call\_back',rowersviews.rower_process_callback),
url(r'^stravacall\_back',rowersviews.rower_process_stravacallback),
url(r'^sporttracks\_callback',rowersviews.rower_process_sporttrackscallback),
url(r'^twitter\_callback',rowersviews.rower_process_twittercallback),
url(r'^interactiveplot',interactiveplot),
url(r'^i18n/', include('django.conf.urls.i18n')),
]

View File

@@ -1,258 +1,11 @@
from django.http import HttpResponse,Http404,HttpResponseRedirect
from django.template import Template, Context
from django.template.loader import get_template
from django.shortcuts import render, redirect, render_to_response
from django.conf import settings
from rowsandall_app.forms import ContactForm,DocumentsForm
from django.core.mail import send_mail
from django.core.urlresolvers import reverse
from matplotlib.pyplot import figure, axes, pie, title
from matplotlib.backends.backend_agg import FigureCanvasAgg
from matplotlib.ticker import MultipleLocator,FuncFormatter,NullFormatter
import matplotlib.pyplot as plt
from rowingdata import rower as rrower
from rowingdata import main as rmain
from rowingdata import rowingdata as rdata
from rowingdata import TCXParser
import StringIO
from django.contrib.auth.decorators import login_required
import django_rq
queue = django_rq.get_queue('default')
import datetime
import pandas as pd
import os
import numpy as np
import json
import mpld3
from rctasks import addrc
from rows import *
from rowers.tasks import add as addtask
from rowers.tasks import handle_makeplot
from celery.result import AsyncResult
from celery.exceptions import TimeoutError
from rowers.models import Rower,User,Workout,GraphImage
def showStaticImage(request,imagename):
return render(request, 'image_page.html', {'imagename': imagename})
def rootview(request):
magicsentence = rmain()
return render(request, 'frontpage.html', {'versionstring': magicsentence})
def version(request):
magicsentence = rmain()
return render(request, 'base.html', {'versionstring': magicsentence})
def rowingdata(request,formloc):
if request.method == 'POST':
form = DocumentsForm(request.POST,request.FILES)
if form.is_valid():
f = request.FILES['file']
t = request.POST['title']
res = handle_uploaded_file(f)
f1 = res[0]
f2 = res[1]
row = rdata(f2)
fig1 = row.get_timeplot_erg(t)
canvas = FigureCanvasAgg(fig1)
# response = HttpResponse(content_type='image/png')
# canvas.print_png(response)
imagename = f1+'.png'
plt.savefig('static/plots/'+imagename,format='png')
plt.close(fig1)
response = render(request,'image_page.html',{'imagename':'plots/'+imagename})
else:
response = HttpResponse("invalid form")
return response
else:
form = DocumentsForm()
return render(request, 'document_form.html',
{'form':form, 'formloc': formloc})
@login_required()
def nrowingdata(request,formloc):
if request.method == 'POST':
form = DocumentsForm(request.POST,request.FILES)
if form.is_valid():
f = request.FILES['file']
res = handle_uploaded_file(f)
t = request.POST['title']
fileformat = request.POST['fileformat']
workouttype = request.POST['workouttype']
notes = request.POST['notes']
f1 = res[0] # file name
f2 = res[1] # file name incl media directory
# handle TCX
if (fileformat == 'tcx'):
row = TCXParser(f2)
f_to_be_deleted = f2
# should delete file
f2 = f2+'.csv'
row.write_csv(f2)
os.remove(f_to_be_deleted)
imagename = f1+'.png'
fullpathimagename = 'static/plots/'+imagename
u = request.user
r = Rower.objects.get(user=request.user)
hrdata = {
'hrmax':r.max,
'hrut2':r.ut2,
'hrut1':r.ut1,
'hrat':r.at,
'hrtr':r.tr,
'hran':r.an,
}
# make plot - asynchronous task
res = handle_makeplot.delay(f1,f2,t,hrdata)
# make workout and put in database
rr = rrower(hrmax=r.max,hrut2=r.ut2,
hrut1=r.ut1,hrat=r.at,
hrtr=r.tr,hran=r.an)
row = rdata(f2,rower=rr)
totaldist = row.df['cum_dist'].max()
totaltime = row.df['TimeStamp (sec)'].max()-row.df['TimeStamp (sec)'].min()
totaltime = totaltime+row.df.ix[0,' ElapsedTime (sec)']
hours = int(totaltime/3600.)
minutes = int((totaltime - 3600.*hours)/60.)
seconds = int(totaltime - 3600.*hours - 60.*minutes)
duration = "%s:%s:%s" % (hours,minutes,seconds)
workoutdate = row.rowdatetime.strftime('%Y-%m-%d')
workoutstarttime = row.rowdatetime.strftime('%H:%M:%S')
# check for duplicate start times
r = Rower.objects.get(user=request.user)
ws = Workout.objects.filter(starttime=workoutstarttime,
user=r)
if (len(ws) != 0):
print "Warning: This workout probably already exists in the database"
w = Workout(user=r,name=t,date=workoutdate,workouttype=workouttype,
duration=duration,distance=totaldist,
weightcategory=r.weightcategory,
starttime=workoutstarttime,
csvfilename=f2,notes=notes)
w.save()
i = GraphImage(workout=w,creationdatetime=datetime.datetime.now(),
filename=fullpathimagename)
i.save()
url = reverse(waitforplot,args=[str(res.id)])
return HttpResponseRedirect(url)
else:
response = HttpResponse("invalid form")
return response
else:
form = DocumentsForm()
return render(request, 'document_form.html',
{'form':form, 'formloc': formloc})
def uploadfile(request,formloc):
if request.method == 'POST':
form = DocumentsForm(request.POST, request.FILES)
if form.is_valid():
result = handle_uploaded_file(request.FILES['file'])
return HttpResponse("succes! "+result)
else:
return HttpResponse("Invalid Form")
else:
form = DocumentsForm()
return render(request, 'document_form.html',
{'form': form, 'formloc':formloc})
@login_required()
def add(request,x,y):
if settings.DEBUG:
task = addtask.apply_async((int(x),int(y)),countdown=10)
task_id = AsyncResult(task)
print task.id
else:
task = queue.enqueue(addrc,x,y,0)
task_id = task.id
url = reverse(wait,args=[str(task_id)])
return HttpResponseRedirect(url)
def wait(request,task_id=""):
if settings.DEBUG:
res = AsyncResult(task_id)
else:
res = queue.fetch_job(task_id)
if (res.status == 'SUCCESS') or (res.status == 'finished'):
return HttpResponse("Task complete. Result: "+str(res.result))
else:
m = "Task status: "+str(res.status)
return render(request,'waiting_page.html',{'message':m})
return HttpResponse("Task status: "+str(res.status)+". <p>Hit reload to check again")
def waitforplot(request,task_id=""):
res = AsyncResult(task_id)
if (res.status == 'SUCCESS'):
url = reverse(showplot,args=[str(res.result)])
return HttpResponseRedirect(url)
else:
m = "Task status: "+str(res.status)
return render(request,'waiting_page.html',{'message':m})
def showplot(request,imagename=""):
return render(request,'image_page.html',{'imagename':'plots/'+imagename})
def contact(request):
if request.method == 'POST':
form = ContactForm(request.POST)
if form.is_valid():
cd = form.cleaned_data
send_mail(
cd['subject'],
cd['message'],
cd.get('email', 'noreply@example.com'),
['siteowner@example.com'],
)
return HttpResponseRedirect('/contact/thanks/')
else:
form = ContactForm()
return render(request, 'contact_form.html', {'form': form})
def interactiveplot(request):
x = np.linspace(-10,10,num=1200)
y = x**2-5*x+4
fig = plt.figure(figsize=(6,5))
graph = plt.plot(x,y)
g = mpld3.fig_to_html(fig)
# js_data = json.dumps(mpld3.fig_to_dict(fig))
plt.close()
# return render(request,'interactiveplot.html',{"my_data":js_data})
return HttpResponse(g)