half way through cleaning up and commenting the code
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -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,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,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 "open" 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('Elapsed Time') but this doesn'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
@@ -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 %}
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -1,7 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Current time{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<p>It is now {{ current_date }}.</p>
|
||||
{% endblock %}
|
||||
@@ -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 %}
|
||||
@@ -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 %}
|
||||
@@ -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 %}
|
||||
|
||||
@@ -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 %}
|
||||
@@ -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>
|
||||
@@ -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 %}
|
||||
@@ -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 %}
|
||||
@@ -1,7 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Rowingdata Version{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<p>{{ versionstring }}.</p>
|
||||
{% endblock %}
|
||||
@@ -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 %}
|
||||
@@ -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 %}
|
||||
@@ -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:
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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.')
|
||||
@@ -1,3 +1,7 @@
|
||||
# This is just a scratch pad to temporarily park code, just in case I need
|
||||
# it later. Hardly used since i have proper versioning
|
||||
|
||||
|
||||
@login_required()
|
||||
def workout_edit_view(request,id=0):
|
||||
if request.method == 'POST':
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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),
|
||||
|
||||
284
rowers/views.py
284
rowers/views.py
@@ -1,6 +1,7 @@
|
||||
import time
|
||||
import zipfile
|
||||
import operator
|
||||
import warnings
|
||||
from django.views.generic.base import TemplateView
|
||||
from django.db.models import Q
|
||||
from django.db import IntegrityError, transaction
|
||||
@@ -46,7 +47,7 @@ import requests
|
||||
import json
|
||||
from rest_framework.renderers import JSONRenderer
|
||||
from rest_framework.parsers import JSONParser
|
||||
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,handle_sendemailcsv
|
||||
from rowers.tasks import handle_sendemail_unrecognized
|
||||
from scipy.signal import savgol_filter
|
||||
@@ -105,8 +106,10 @@ USER_LANGUAGE = 'en-US'
|
||||
|
||||
from interactiveplots import *
|
||||
|
||||
# Define the API documentation
|
||||
schema_view = get_swagger_view(title='Rowsandall API (Unstable)')
|
||||
|
||||
# Custom error pages with Rowsandall headers
|
||||
def error500_view(request):
|
||||
response = render_to_response('500.html', {},
|
||||
context_instance = RequestContext(request))
|
||||
@@ -135,6 +138,8 @@ def error403_view(request):
|
||||
response.status_code = 403
|
||||
return response
|
||||
|
||||
# Wrapper around the rowingdata call to catch some exceptions
|
||||
# Checks for CSV file, then for gzipped CSV file, and if all fails, returns 0
|
||||
def rdata(file,rower=rrower()):
|
||||
try:
|
||||
res = rrdata(file,rower=rower)
|
||||
@@ -146,6 +151,7 @@ def rdata(file,rower=rrower()):
|
||||
|
||||
return res
|
||||
|
||||
# Used for the interval editor - translates seconds to a time object
|
||||
def get_time(second):
|
||||
if (second<=0) or (second>1e9):
|
||||
hours = 0
|
||||
@@ -166,10 +172,13 @@ def get_time(second):
|
||||
return datetime.time(hours,minutes,sec,microsecond)
|
||||
|
||||
|
||||
|
||||
# get the workout ID from the SportTracks URI
|
||||
def getidfromsturi(uri):
|
||||
return uri[len(uri)-8:]
|
||||
|
||||
# Splits SportTracks data which is one long sequence of
|
||||
# [t,[lat,lon],t2,[lat2,lon2] ...]
|
||||
# to [t,t2,t3, ...], [[lat,long],[lat2,long2],...
|
||||
def splitstdata(lijst):
|
||||
t = []
|
||||
latlong = []
|
||||
@@ -188,10 +197,13 @@ def geo_distance(lat1,lon1,lat2,lon2):
|
||||
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 earth in km
|
||||
# radius of our earth in km --> should be moved to settings if
|
||||
# rowing takes off on other planets
|
||||
R = 6373.0
|
||||
|
||||
# pi
|
||||
@@ -219,11 +231,13 @@ def geo_distance(lat1,lon1,lat2,lon2):
|
||||
|
||||
return [distance,bearing]
|
||||
|
||||
# Check if a user is a Pro member
|
||||
def promember(user):
|
||||
r = Rower.objects.get(user=user)
|
||||
result = user.is_authenticated() and (r.rowerplan=='pro' or r.rowerplan=='coach')
|
||||
return result
|
||||
|
||||
# User registration
|
||||
def rower_register_view(request):
|
||||
if request.method == 'POST':
|
||||
form = RegistrationFormUniqueEmail(request.POST)
|
||||
@@ -290,6 +304,7 @@ def rower_register_view(request):
|
||||
"registration_form.html",
|
||||
{'form':form,})
|
||||
|
||||
# Shows email form and sends it if submitted
|
||||
def sendmail(request):
|
||||
if request.method == 'POST':
|
||||
form = EmailForm(request.POST)
|
||||
@@ -312,6 +327,7 @@ def sendmail(request):
|
||||
else:
|
||||
return HttpResponseRedirect('/rowers/email/')
|
||||
|
||||
# Check if workout belongs to this user
|
||||
def checkworkoutuser(user,workout):
|
||||
try:
|
||||
r = Rower.objects.get(user=user)
|
||||
@@ -319,6 +335,8 @@ def checkworkoutuser(user,workout):
|
||||
except Rower.DoesNotExist:
|
||||
return(False)
|
||||
|
||||
# Create workout data from Strava or Concept2
|
||||
# data and create the associated Workout object and save it
|
||||
def add_workout_from_strokedata(user,importid,data,strokedata,
|
||||
source='c2',splitdata=None):
|
||||
workouttype = data['type']
|
||||
@@ -393,10 +411,8 @@ def add_workout_from_strokedata(user,importid,data,strokedata,
|
||||
pace = strokedata.ix[:,'p']/10.
|
||||
|
||||
velo = 500./pace
|
||||
# if (source=='c2' or source=='strava'):
|
||||
|
||||
power = 2.8*velo**3
|
||||
# else:
|
||||
# power = 0.0*velo
|
||||
|
||||
# save csv
|
||||
# Create data frame with all necessary data to write to csv
|
||||
@@ -419,7 +435,7 @@ def add_workout_from_strokedata(user,importid,data,strokedata,
|
||||
' ElapsedTime (sec)':seconds
|
||||
})
|
||||
|
||||
# data.sort(['TimeStamp (sec)'],ascending=True)
|
||||
|
||||
df.sort_values(by='TimeStamp (sec)',ascending=True)
|
||||
|
||||
timestr = strftime("%Y%m%d-%H%M%S")
|
||||
@@ -451,19 +467,21 @@ def add_workout_from_strokedata(user,importid,data,strokedata,
|
||||
|
||||
# end autosmoothing
|
||||
|
||||
# Create CSV file name and save data to CSV file
|
||||
csvfilename ='media/Import_'+str(importid)+'.csv'
|
||||
|
||||
res = df.to_csv(csvfilename+'.gz',index_label='index',
|
||||
compression='gzip')
|
||||
|
||||
averagehr = df[' HRCur (bpm)'].mean()
|
||||
maxhr = df[' HRCur (bpm)'].max()
|
||||
|
||||
# make workout
|
||||
rr = rrower(hrmax=r.max,hrut2=r.ut2,
|
||||
hrut1=r.ut1,hrat=r.at,
|
||||
hrtr=r.tr,hran=r.an,ftp=r.ftp)
|
||||
row = rdata(csvfilename,rower=rr)
|
||||
|
||||
averagehr = row.df[' HRCur (bpm)'].mean()
|
||||
maxhr = row.df[' HRCur (bpm)'].max()
|
||||
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)']
|
||||
@@ -488,8 +506,9 @@ def add_workout_from_strokedata(user,importid,data,strokedata,
|
||||
ws = Workout.objects.filter(starttime=workoutstarttime,
|
||||
user=r)
|
||||
if (len(ws) != 0):
|
||||
print "Warning: This workout probably already exists in the database"
|
||||
warnings.warn("Probably a duplicate workout",UserWarning)
|
||||
|
||||
# Create the Workout object
|
||||
w = Workout(user=r,name=title,
|
||||
date=workoutdate,workouttype=workouttype,
|
||||
duration=duration,distance=totaldist,
|
||||
@@ -503,6 +522,8 @@ def add_workout_from_strokedata(user,importid,data,strokedata,
|
||||
|
||||
return w.id
|
||||
|
||||
# Create workout from SportTracks Data, which are slightly different
|
||||
# than Strava or Concept2 data
|
||||
def add_workout_from_stdata(user,importid,data):
|
||||
workouttype = data['type']
|
||||
if workouttype not in [x[0] for x in Workout.workouttypes]:
|
||||
@@ -512,7 +533,6 @@ def add_workout_from_stdata(user,importid,data):
|
||||
except:
|
||||
comments = ''
|
||||
|
||||
# comments = "Imported data \n"+str(comments)
|
||||
try:
|
||||
thetimezone = tz(data['timezone'])
|
||||
except:
|
||||
@@ -653,8 +673,6 @@ def add_workout_from_stdata(user,importid,data):
|
||||
|
||||
df = df.fillna(0)
|
||||
|
||||
|
||||
# data.sort(['TimeStamp (sec)'],ascending=True)
|
||||
df.sort_values(by='TimeStamp (sec)',ascending=True)
|
||||
|
||||
timestr = strftime("%Y%m%d-%H%M%S")
|
||||
@@ -689,14 +707,15 @@ def add_workout_from_stdata(user,importid,data):
|
||||
res = df.to_csv(csvfilename+'.gz',index_label='index',
|
||||
compression='gzip')
|
||||
|
||||
averagehr = df[' HRCur (bpm)'].mean()
|
||||
maxhr = df[' HRCur (bpm)'].max()
|
||||
|
||||
# make workout
|
||||
rr = rrower(hrmax=r.max,hrut2=r.ut2,
|
||||
hrut1=r.ut1,hrat=r.at,
|
||||
hrtr=r.tr,hran=r.an,ftp=r.ftp)
|
||||
row = rdata(csvfilename,rower=rr)
|
||||
|
||||
averagehr = row.df[' HRCur (bpm)'].mean()
|
||||
maxhr = row.df[' HRCur (bpm)'].max()
|
||||
|
||||
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)']
|
||||
@@ -736,12 +755,13 @@ def add_workout_from_stdata(user,importid,data):
|
||||
|
||||
return w.id
|
||||
|
||||
# Checks if user has Concept2 tokens, resets tokens if they are
|
||||
# expired.
|
||||
def c2_open(user):
|
||||
r = Rower.objects.get(user=user)
|
||||
if (r.c2token == '') or (r.c2token is None):
|
||||
s = "Token doesn't exist. Need to authorize"
|
||||
raise C2NoTokenError("User has no token")
|
||||
# return HttpResponseRedirect("/rowers/me/c2authorize/")
|
||||
else:
|
||||
if (timezone.now()>r.tokenexpirydate):
|
||||
res = c2stuff.rower_c2_token_refresh(user)
|
||||
@@ -754,7 +774,7 @@ def c2_open(user):
|
||||
|
||||
return thetoken
|
||||
|
||||
|
||||
# Checks if user has SportTracks token, renews them if they are expired
|
||||
def sporttracks_open(user):
|
||||
r = Rower.objects.get(user=user)
|
||||
if (r.sporttrackstoken == '') or (r.sporttrackstoken is None):
|
||||
@@ -768,60 +788,7 @@ def sporttracks_open(user):
|
||||
|
||||
return thetoken
|
||||
|
||||
|
||||
# Create your views here.
|
||||
|
||||
@login_required()
|
||||
def list_c2_upload_view(request,id=0):
|
||||
message = ""
|
||||
try:
|
||||
thetoken = c2_open(request.user)
|
||||
except C2NoTokenError:
|
||||
return HttpResponseRedirect("/rowers/me/c2authorize/")
|
||||
|
||||
# ready to upload. Hurray
|
||||
w = Workout.objects.get(id=id)
|
||||
if (checkworkoutuser(request.user,w)):
|
||||
c2userid = c2stuff.get_userid(thetoken)
|
||||
data = c2stuff.createc2workoutdata(w)
|
||||
authorizationstring = str('Bearer ' + thetoken)
|
||||
headers = {'Authorization': authorizationstring,
|
||||
'user-agent': 'sanderroosendaal',
|
||||
'Content-Type': 'application/json'}
|
||||
import urllib
|
||||
url = "https://log.concept2.com/api/users/%s/results" % (c2userid)
|
||||
response = requests.post(url,headers=headers,data=json.dumps(data))
|
||||
|
||||
# check for duplicate error first
|
||||
if (response.status_code == 409 ):
|
||||
message = "Duplicate error"
|
||||
w.uploadedtoc2 = -1
|
||||
w.save()
|
||||
elif (response.status_code == 201 or response.status_code == 200):
|
||||
try:
|
||||
s= json.loads(response.text)
|
||||
c2id = s['data']['id']
|
||||
w.uploadedtoc2 = c2id
|
||||
w.save()
|
||||
url = reverse(workouts_view)
|
||||
return HttpResponseRedirect(url)
|
||||
except:
|
||||
message = "Something went wrong in list_c2_upload_view. Response 200/201 but Upload to C2 failed: "+response.text
|
||||
else:
|
||||
s = response
|
||||
message = "Something went wrong in list_c2_upload_view. Upload to C2 failed."
|
||||
|
||||
else:
|
||||
message = "You are not authorized to upload this workout"
|
||||
|
||||
url = reverse(workouts_view,
|
||||
kwargs = {
|
||||
'message':str(message),
|
||||
})
|
||||
|
||||
|
||||
return HttpResponseRedirect(url)
|
||||
|
||||
# Export workout to TCX and send to user's email address
|
||||
@login_required()
|
||||
def workout_tcxemail_view(request,id=0):
|
||||
message = ""
|
||||
@@ -874,6 +841,7 @@ def workout_tcxemail_view(request,id=0):
|
||||
|
||||
return response
|
||||
|
||||
# Get Workout CSV file and send it to user's email address
|
||||
@login_required()
|
||||
def workout_csvemail_view(request,id=0):
|
||||
message = ""
|
||||
@@ -910,6 +878,8 @@ def workout_csvemail_view(request,id=0):
|
||||
|
||||
return response
|
||||
|
||||
# Send workout to Strava
|
||||
# abundance of error logging here because there were/are some bugs
|
||||
@login_required()
|
||||
def workout_strava_upload_view(request,id=0):
|
||||
message = ""
|
||||
@@ -991,6 +961,7 @@ def workout_strava_upload_view(request,id=0):
|
||||
|
||||
return response
|
||||
|
||||
# Upload workout to Concept2 logbook
|
||||
@login_required()
|
||||
def workout_c2_upload_view(request,id=0):
|
||||
message = ""
|
||||
@@ -1059,6 +1030,7 @@ def workout_c2_upload_view(request,id=0):
|
||||
|
||||
return HttpResponseRedirect(url)
|
||||
|
||||
# Upload workout to SportTracks
|
||||
@login_required()
|
||||
def workout_sporttracks_upload_view(request,id=0):
|
||||
message = ""
|
||||
@@ -1116,7 +1088,7 @@ def workout_sporttracks_upload_view(request,id=0):
|
||||
|
||||
return HttpResponseRedirect(url)
|
||||
|
||||
|
||||
# Concept2 authorization
|
||||
@login_required()
|
||||
def rower_c2_authorize(request):
|
||||
# Generate a random string for the state parameter
|
||||
@@ -1132,6 +1104,7 @@ def rower_c2_authorize(request):
|
||||
url += "&scope="+scope
|
||||
return HttpResponseRedirect(url)
|
||||
|
||||
# Strava Authorization
|
||||
@login_required()
|
||||
def rower_strava_authorize(request):
|
||||
# Generate a random string for the state parameter
|
||||
@@ -1149,6 +1122,7 @@ def rower_strava_authorize(request):
|
||||
|
||||
return HttpResponseRedirect(url)
|
||||
|
||||
# SportTracks Authorization
|
||||
@login_required()
|
||||
def rower_sporttracks_authorize(request):
|
||||
# Generate a random string for the state parameter
|
||||
@@ -1167,6 +1141,7 @@ def rower_sporttracks_authorize(request):
|
||||
|
||||
return HttpResponseRedirect(url)
|
||||
|
||||
# Concept2 token refresh. URL for manual refresh. Not visible to users
|
||||
@login_required()
|
||||
def rower_c2_token_refresh(request):
|
||||
r = Rower.objects.get(user=request.user)
|
||||
@@ -1191,6 +1166,7 @@ def rower_c2_token_refresh(request):
|
||||
|
||||
return imports_view(request,successmessage=successmessage,message=message)
|
||||
|
||||
# SportTracks token refresh. URL for manual refresh. Not visible to users
|
||||
@login_required()
|
||||
def rower_sporttracks_token_refresh(request):
|
||||
r = Rower.objects.get(user=request.user)
|
||||
@@ -1211,6 +1187,7 @@ def rower_sporttracks_token_refresh(request):
|
||||
return imports_view(request,successmessage=successmessage)
|
||||
|
||||
|
||||
# Concept2 Callback
|
||||
@login_required()
|
||||
def rower_process_callback(request):
|
||||
try:
|
||||
@@ -1235,6 +1212,7 @@ def rower_process_callback(request):
|
||||
successmessage = "Tokens stored. Good to go"
|
||||
return imports_view(request,successmessage=successmessage)
|
||||
|
||||
# The imports page
|
||||
@login_required()
|
||||
def imports_view(request,successmessage="",message=""):
|
||||
return render(request,"imports.html",
|
||||
@@ -1242,16 +1220,19 @@ def imports_view(request,successmessage="",message=""):
|
||||
'message': message,
|
||||
})
|
||||
|
||||
# Just for testing purposes
|
||||
@login_required()
|
||||
def test_reverse_view(request):
|
||||
successmessage = "Tokens stored. Good to go"
|
||||
|
||||
return imports_view(request,successmessage=successmessage)
|
||||
|
||||
# dummy
|
||||
@login_required()
|
||||
def rower_process_twittercallback(request):
|
||||
return "dummy"
|
||||
|
||||
# Process Strava Callback
|
||||
@login_required()
|
||||
def rower_process_stravacallback(request):
|
||||
code = request.GET['code']
|
||||
@@ -1268,7 +1249,7 @@ def rower_process_stravacallback(request):
|
||||
return imports_view(request,successmessage=successmessage)
|
||||
|
||||
|
||||
|
||||
# Process SportTracks callback
|
||||
@login_required()
|
||||
def rower_process_sporttrackscallback(request):
|
||||
code = request.GET['code']
|
||||
@@ -1290,6 +1271,7 @@ def rower_process_sporttrackscallback(request):
|
||||
successmessage = "Tokens stored. Good to go"
|
||||
return imports_view(request,successmessage=successmessage)
|
||||
|
||||
# Process Own API callback - for API testing purposes
|
||||
@login_required()
|
||||
def rower_process_testcallback(request):
|
||||
code = request.GET['code']
|
||||
@@ -1309,7 +1291,7 @@ def rower_process_testcallback(request):
|
||||
|
||||
return HttpResponse(text)
|
||||
|
||||
|
||||
# View around the Histogram of all strokes. Not implemented in UI
|
||||
@login_required()
|
||||
def histo_all(request,theuser=0):
|
||||
promember=0
|
||||
@@ -1356,6 +1338,7 @@ def histo_all(request,theuser=0):
|
||||
'theuser':u,
|
||||
})
|
||||
|
||||
# The Flex plot for a large selection of workouts
|
||||
@login_required()
|
||||
def cum_flex(request,theuser=0,
|
||||
xparam='spm',
|
||||
@@ -1478,6 +1461,7 @@ def cum_flex(request,theuser=0,
|
||||
'promember':promember,
|
||||
})
|
||||
|
||||
# Show the EMpower Oarlock generated Stroke Profile
|
||||
@user_passes_test(promember,login_url="/",redirect_field_name=None)
|
||||
def workout_forcecurve_view(request,id=0,workstrokesonly=False):
|
||||
row = Workout.objects.get(id=id)
|
||||
@@ -1516,6 +1500,7 @@ def workout_forcecurve_view(request,id=0,workstrokesonly=False):
|
||||
'workstrokesonly': not workstrokesonly,
|
||||
})
|
||||
|
||||
# Show Stroke power histogram for a workout
|
||||
@login_required()
|
||||
def workout_histo_view(request,id=0):
|
||||
row = Workout.objects.get(id=id)
|
||||
@@ -1544,6 +1529,7 @@ def workout_histo_view(request,id=0):
|
||||
'mayedit':mayedit,
|
||||
})
|
||||
|
||||
# Histogram for a date/time range
|
||||
@login_required()
|
||||
def histo(request,theuser=0,
|
||||
startdate=timezone.now()-datetime.timedelta(days=365),
|
||||
@@ -1651,6 +1637,7 @@ def histo(request,theuser=0,
|
||||
'deltaform':deltaform,
|
||||
})
|
||||
|
||||
# Show ranking distances including predicted paces
|
||||
@login_required()
|
||||
def rankings_view(request,theuser=0,
|
||||
startdate=timezone.now()-datetime.timedelta(days=365),
|
||||
@@ -1683,7 +1670,7 @@ def rankings_view(request,theuser=0,
|
||||
if result:
|
||||
promember=1
|
||||
|
||||
# get all indoor rows of in date range
|
||||
# get all indoor rows in date range
|
||||
|
||||
# process form
|
||||
if request.method == 'POST' and "daterange" in request.POST:
|
||||
@@ -1945,6 +1932,7 @@ def rankings_view(request,theuser=0,
|
||||
'enddate':enddate,
|
||||
})
|
||||
|
||||
# Reload the workout and calculate the summary from the stroke data (lapIDx)
|
||||
@login_required()
|
||||
def workout_recalcsummary_view(request,id=0):
|
||||
row = Workout.objects.get(id=id)
|
||||
@@ -1967,7 +1955,7 @@ def workout_recalcsummary_view(request,id=0):
|
||||
|
||||
return HttpResponseRedirect(url)
|
||||
|
||||
|
||||
# List Workouts
|
||||
@login_required()
|
||||
def workouts_view(request,message='',successmessage='',
|
||||
startdatestring="",enddatestring="",
|
||||
@@ -1975,9 +1963,6 @@ def workouts_view(request,message='',successmessage='',
|
||||
enddate=timezone.now()+datetime.timedelta(days=1)):
|
||||
try:
|
||||
r = Rower.objects.get(user=request.user)
|
||||
# res = mailprocessing.safeprocessattachments()
|
||||
#if len(res)>0 and np.cumsum(np.array(res)).max()>0:
|
||||
# successmessage = 'New Workouts have been created from email'
|
||||
|
||||
if request.method == 'POST':
|
||||
dateform = DateRangeForm(request.POST)
|
||||
@@ -2049,6 +2034,7 @@ def workouts_view(request,message='',successmessage='',
|
||||
except Rower.DoesNotExist:
|
||||
return HttpResponse("User has no rower instance")
|
||||
|
||||
# List of workouts to compare a selected workout to
|
||||
@user_passes_test(promember,login_url="/",redirect_field_name=None)
|
||||
def workout_comparison_list(request,id=0,message='',successmessage='',
|
||||
startdatestring="",enddatestring="",
|
||||
@@ -2124,6 +2110,7 @@ def workout_comparison_list(request,id=0,message='',successmessage='',
|
||||
except Rower.DoesNotExist:
|
||||
return HttpResponse("User has no rower instance")
|
||||
|
||||
# Basic 'EDIT' view of workout
|
||||
def workout_view(request,id=0):
|
||||
try:
|
||||
# check if valid ID exists (workout exists)
|
||||
@@ -2162,7 +2149,7 @@ def workout_view(request,id=0):
|
||||
except Workout.DoesNotExist:
|
||||
return HttpResponseNotFound("Workout doesn't exist")
|
||||
|
||||
|
||||
# Resets stroke data to raw data (pace)
|
||||
@user_passes_test(promember,login_url="/",redirect_field_name=None)
|
||||
def workout_undo_smoothenpace_view(request,id=0,message="",successmessage=""):
|
||||
row = Workout.objects.get(id=id)
|
||||
@@ -2189,7 +2176,7 @@ def workout_undo_smoothenpace_view(request,id=0,message="",successmessage=""):
|
||||
return HttpResponseRedirect(url)
|
||||
|
||||
|
||||
|
||||
# Data smoothing of pace data
|
||||
@user_passes_test(promember,login_url="/",redirect_field_name=None)
|
||||
def workout_smoothenpace_view(request,id=0,message="",successmessage=""):
|
||||
row = Workout.objects.get(id=id)
|
||||
@@ -2225,6 +2212,7 @@ def workout_smoothenpace_view(request,id=0,message="",successmessage=""):
|
||||
|
||||
return HttpResponseRedirect(url)
|
||||
|
||||
# Process CrewNerd Summary CSV and update summary
|
||||
@user_passes_test(promember,login_url="/",redirect_field_name=None)
|
||||
def workout_crewnerd_summary_view(request,id=0,message="",successmessage=""):
|
||||
row = Workout.objects.get(id=id)
|
||||
@@ -2269,6 +2257,7 @@ def workout_crewnerd_summary_view(request,id=0,message="",successmessage=""):
|
||||
{'form':form,
|
||||
'id':row.id})
|
||||
|
||||
# Get weather for given location and date/time
|
||||
@user_passes_test(promember,login_url="/",redirect_field_name=None)
|
||||
def workout_downloadwind_view(request,id=0,message="",successmessage=""):
|
||||
row = Workout.objects.get(id=id)
|
||||
@@ -2325,7 +2314,7 @@ def workout_downloadwind_view(request,id=0,message="",successmessage=""):
|
||||
|
||||
return response
|
||||
|
||||
|
||||
# Show form to update wind data
|
||||
@user_passes_test(promember,login_url="/",redirect_field_name=None)
|
||||
def workout_wind_view(request,id=0,message="",successmessage=""):
|
||||
row = Workout.objects.get(id=id)
|
||||
@@ -2425,6 +2414,7 @@ def workout_wind_view(request,id=0,message="",successmessage=""):
|
||||
'gmapdiv':gmdiv})
|
||||
|
||||
|
||||
# Show form to update River stream data (for river dwellers)
|
||||
@user_passes_test(promember,login_url="/",redirect_field_name=None)
|
||||
def workout_stream_view(request,id=0,message="",successmessage=""):
|
||||
row = Workout.objects.get(id=id)
|
||||
@@ -2486,7 +2476,7 @@ def workout_stream_view(request,id=0,message="",successmessage=""):
|
||||
'form':form,
|
||||
'the_div':div})
|
||||
|
||||
|
||||
# Form to set average crew weight and boat type, then run power calcs
|
||||
@user_passes_test(promember, login_url="/",redirect_field_name=None)
|
||||
def workout_otwsetpower_view(request,id=0,message="",successmessage=""):
|
||||
row = Workout.objects.get(id=id)
|
||||
@@ -2572,7 +2562,7 @@ def workout_otwsetpower_view(request,id=0,message="",successmessage=""):
|
||||
'form':form,
|
||||
})
|
||||
|
||||
|
||||
# A special Edit page with all the Geeky functionality for the workout
|
||||
@login_required()
|
||||
def workout_geeky_view(request,id=0,message="",successmessage=""):
|
||||
row = Workout.objects.get(id=id)
|
||||
@@ -2617,7 +2607,7 @@ def workout_geeky_view(request,id=0,message="",successmessage=""):
|
||||
'interactiveplot':script,
|
||||
'the_div':div})
|
||||
|
||||
#@user_passes_test(promember,login_url="/",redirect_field_name=None)
|
||||
# The Advanced edit page
|
||||
@login_required()
|
||||
def workout_advanced_view(request,id=0,message="",successmessage=""):
|
||||
row = Workout.objects.get(id=id)
|
||||
@@ -2662,6 +2652,7 @@ def workout_advanced_view(request,id=0,message="",successmessage=""):
|
||||
'interactiveplot':script,
|
||||
'the_div':div})
|
||||
|
||||
# The interactive plot comparing two workouts (obsolete version)
|
||||
def workout_comparison_view(request,id1=0,id2=0,xparam='distance',yparam='spm'):
|
||||
promember=0
|
||||
if not request.user.is_anonymous():
|
||||
@@ -2687,6 +2678,7 @@ def workout_comparison_view(request,id1=0,id2=0,xparam='distance',yparam='spm'):
|
||||
'yparam':yparam,
|
||||
})
|
||||
|
||||
# Updated version of comparison pliot
|
||||
def workout_comparison_view2(request,id1=0,id2=0,xparam='distance',
|
||||
yparam='spm',plottype='line'):
|
||||
promember=0
|
||||
@@ -2715,7 +2707,7 @@ def workout_comparison_view2(request,id1=0,id2=0,xparam='distance',
|
||||
})
|
||||
|
||||
|
||||
|
||||
# The famous flex chart
|
||||
def workout_flexchart3_view(request,*args,**kwargs):
|
||||
|
||||
try:
|
||||
@@ -2879,98 +2871,8 @@ def workout_flexchart3_view(request,*args,**kwargs):
|
||||
'maxfav':maxfav,
|
||||
})
|
||||
|
||||
def testbokeh(request):
|
||||
|
||||
TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,hover,resize,crosshair'
|
||||
|
||||
x = np.array(range(51))/10.
|
||||
y = x**2.
|
||||
z = (2*(x-2.5))**2
|
||||
data = dict(
|
||||
x = x,
|
||||
y = y,
|
||||
z = z,
|
||||
)
|
||||
|
||||
df = pd.DataFrame(data)
|
||||
filtered_df = df
|
||||
|
||||
source = ColumnDataSource(
|
||||
df
|
||||
)
|
||||
|
||||
source2 = ColumnDataSource(
|
||||
filtered_df
|
||||
)
|
||||
|
||||
plot = Figure(tools=TOOLS)
|
||||
plot.circle('x','y',source=source2)
|
||||
plot.xaxis.axis_label = "X"
|
||||
plot.yaxis.axis_label = "Y"
|
||||
plot.x_range = Range1d(-1,6)
|
||||
plot.y_range = Range1d(-5,30)
|
||||
|
||||
callback = CustomJS(args = dict(source=source,source2=source2), code="""
|
||||
var data = source.data
|
||||
var data2 = source2.data
|
||||
var x1 = data['x']
|
||||
var y1 = data['y']
|
||||
var z1 = data['z']
|
||||
var mina = mina.value
|
||||
var maxa = maxa.value
|
||||
var minz = minz.value
|
||||
var maxz = maxz.value
|
||||
data2['x'] = []
|
||||
data2['y'] = []
|
||||
data2['z'] = []
|
||||
|
||||
for (i=0; i<x1.length; i++) {
|
||||
if (x1[i]>=mina && x1[i]<=maxa && z1[i]<=maxz && z1[i]>=minz) {
|
||||
data2['x'].push(x1[i])
|
||||
data2['y'].push(y1[i])
|
||||
data2['z'].push(z1[i])
|
||||
}
|
||||
}
|
||||
|
||||
source2.trigger('change');
|
||||
""")
|
||||
|
||||
s1 = Slider(start=0.0, end=5,value=0.0, step=.1,title="Min X Value",callback=callback)
|
||||
callback.args["mina"] = s1
|
||||
|
||||
s2 = Slider(start=0.0, end=5,value=5.0, step=.1,title="Max X Value",callback=callback)
|
||||
callback.args["maxa"] = s2
|
||||
|
||||
s3 = Slider(start=0.0, end=25,value=0.0, step=.1,title="Min Z Value",callback=callback)
|
||||
callback.args["minz"] = s3
|
||||
|
||||
s4 = Slider(start=0.0, end=25,value=25.0, step=.1,title="Max Z Value",callback=callback)
|
||||
callback.args["maxz"] = s4
|
||||
|
||||
hover = plot.select(dict(type=HoverTool))
|
||||
|
||||
hover.tooltips = OrderedDict([
|
||||
('X value','@x'),
|
||||
('Y value','@y'),
|
||||
('Z value','@z'),
|
||||
])
|
||||
|
||||
hover.mode = 'mouse'
|
||||
|
||||
layout = layoutrow([layoutcolumn([s1,s2,s3,s4]),plot])
|
||||
# widgetbox(s)
|
||||
script, div = components(layout)
|
||||
js_resources = INLINE.render_js()
|
||||
css_resources = INLINE.render_css()
|
||||
return render(request,
|
||||
'test.html',
|
||||
{'the_script': script,
|
||||
'the_div': div,
|
||||
'js_res': js_resources,
|
||||
'css_res':css_resources,
|
||||
})
|
||||
|
||||
#@user_passes_test(promember,login_url="/",redirect_field_name=None)
|
||||
# The interactive plot with the colored Heart rate zones
|
||||
def workout_biginteractive_view(request,id=0,message="",successmessage=""):
|
||||
row = Workout.objects.get(id=id)
|
||||
# check if user is owner of this workout
|
||||
@@ -3007,6 +2909,7 @@ def workout_biginteractive_view(request,id=0,message="",successmessage=""):
|
||||
'promember':promember,
|
||||
'mayedit':mayedit})
|
||||
|
||||
# The interactive plot with wind corrected pace for OTW outings
|
||||
def workout_otwpowerplot_view(request,id=0,message="",successmessage=""):
|
||||
row = Workout.objects.get(id=id)
|
||||
# check if user is owner of this workout
|
||||
@@ -4414,31 +4317,6 @@ def graph_delete_view(request,id=0):
|
||||
return HttpResponse("Graph Image doesn't exist")
|
||||
|
||||
|
||||
@login_required()
|
||||
def dashboard_view(request,message="",successmessage=""):
|
||||
try:
|
||||
request.session[translation.LANGUAGE_SESSION_KEY] = USER_LANGUAGE
|
||||
|
||||
r = Rower.objects.get(user=request.user)
|
||||
workouts = Workout.objects.filter(user=r).order_by("-date", "-starttime")
|
||||
g = GraphImage.objects.filter(workout__in=workouts).order_by("-creationdatetime")
|
||||
if (len(g)<=3):
|
||||
return render(request,'dashboard.html',
|
||||
{'workouts':workouts,
|
||||
'graphs1':g[0:3],
|
||||
'message':message,
|
||||
'successmessage':successmessage})
|
||||
else:
|
||||
return render(request,'dashboard.html',
|
||||
{'workouts':workouts,
|
||||
'graphs1':g[0:3],
|
||||
'graphs2':g[3:6],
|
||||
'message':message,
|
||||
'successmessage':successmessage})
|
||||
|
||||
except Rower.DoesNotExist:
|
||||
return HttpResponse("User has no rower instance")
|
||||
|
||||
|
||||
@login_required()
|
||||
def graphs_view(request):
|
||||
|
||||
@@ -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']
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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])
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
|
||||
def addrc(x,y,z):
|
||||
return int(x)+int(y)
|
||||
@@ -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/'
|
||||
@@ -1,3 +0,0 @@
|
||||
graph = {{ my_data|safe }};
|
||||
|
||||
mpld3.draw_figure("fig01", graph);
|
||||
@@ -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')),
|
||||
]
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user