API & Client - add wind direction to workout detail - fix #134

This commit is contained in:
Sam 2022-01-15 21:54:37 +01:00
parent 7e2d123cdc
commit 5725a574bd
21 changed files with 165 additions and 30 deletions

View File

@ -1 +1 @@
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><!--[if IE]><link rel="icon" href="/favicon.ico"><![endif]--><link rel="stylesheet" href="/static/css/fork-awesome.min.css"><link rel="stylesheet" href="/static/css/leaflet.css"><title>FitTrackee</title><link href="/static/css/admin.e77f8b26.css" rel="prefetch"><link href="/static/css/profile.8b668068.css" rel="prefetch"><link href="/static/css/reset.fc19709e.css" rel="prefetch"><link href="/static/css/statistics.2afdc8a9.css" rel="prefetch"><link href="/static/css/workouts.c5521765.css" rel="prefetch"><link href="/static/js/admin.5f46d0fe.js" rel="prefetch"><link href="/static/js/chunk-2d0c9189.c81458cc.js" rel="prefetch"><link href="/static/js/chunk-2d0cf391.020c75ea.js" rel="prefetch"><link href="/static/js/chunk-2d0da8f3.c8c3e7e8.js" rel="prefetch"><link href="/static/js/chunk-2d2248b6.d84473c1.js" rel="prefetch"><link href="/static/js/chunk-2d22523a.4b710d99.js" rel="prefetch"><link href="/static/js/profile.d25975e2.js" rel="prefetch"><link href="/static/js/reset.ca898ebe.js" rel="prefetch"><link href="/static/js/statistics.d03ca304.js" rel="prefetch"><link href="/static/js/workouts.dcd0b64a.js" rel="prefetch"><link href="/static/css/app.b54fa5fe.css" rel="preload" as="style"><link href="/static/js/app.15c3e6ab.js" rel="preload" as="script"><link href="/static/js/chunk-vendors.6e2f6ef7.js" rel="preload" as="script"><link href="/static/css/app.b54fa5fe.css" rel="stylesheet"><link rel="icon" type="image/png" sizes="32x32" href="/img/icons/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="/img/icons/favicon-16x16.png"><link rel="manifest" href="/manifest.json"><meta name="theme-color" content="#4DBA87"><meta name="apple-mobile-web-app-capable" content="no"><meta name="apple-mobile-web-app-status-bar-style" content="default"><meta name="apple-mobile-web-app-title" content="fittrackee_client"><link rel="apple-touch-icon" href="/img/icons/apple-touch-icon-152x152.png"><link rel="mask-icon" href="/img/icons/safari-pinned-tab.svg" color="#4DBA87"><meta name="msapplication-TileImage" content="/img/icons/msapplication-icon-144x144.png"><meta name="msapplication-TileColor" content="#000000"></head><body><noscript><strong>We're sorry but FitTrackee doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div><script src="/static/js/chunk-vendors.6e2f6ef7.js"></script><script src="/static/js/app.15c3e6ab.js"></script></body></html> <!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><!--[if IE]><link rel="icon" href="/favicon.ico"><![endif]--><link rel="stylesheet" href="/static/css/fork-awesome.min.css"><link rel="stylesheet" href="/static/css/leaflet.css"><title>FitTrackee</title><link href="/static/css/admin.e77f8b26.css" rel="prefetch"><link href="/static/css/profile.8b668068.css" rel="prefetch"><link href="/static/css/reset.fc19709e.css" rel="prefetch"><link href="/static/css/statistics.2afdc8a9.css" rel="prefetch"><link href="/static/css/workouts.fe5cb36d.css" rel="prefetch"><link href="/static/js/admin.5f46d0fe.js" rel="prefetch"><link href="/static/js/chunk-2d0c9189.c81458cc.js" rel="prefetch"><link href="/static/js/chunk-2d0cf391.020c75ea.js" rel="prefetch"><link href="/static/js/chunk-2d0da8f3.c8c3e7e8.js" rel="prefetch"><link href="/static/js/chunk-2d2248b6.d84473c1.js" rel="prefetch"><link href="/static/js/chunk-2d22523a.4b710d99.js" rel="prefetch"><link href="/static/js/profile.d25975e2.js" rel="prefetch"><link href="/static/js/reset.ca898ebe.js" rel="prefetch"><link href="/static/js/statistics.d03ca304.js" rel="prefetch"><link href="/static/js/workouts.becfe714.js" rel="prefetch"><link href="/static/css/app.b54fa5fe.css" rel="preload" as="style"><link href="/static/js/app.d9d90958.js" rel="preload" as="script"><link href="/static/js/chunk-vendors.6e2f6ef7.js" rel="preload" as="script"><link href="/static/css/app.b54fa5fe.css" rel="stylesheet"><link rel="icon" type="image/png" sizes="32x32" href="/img/icons/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="/img/icons/favicon-16x16.png"><link rel="manifest" href="/manifest.json"><meta name="theme-color" content="#4DBA87"><meta name="apple-mobile-web-app-capable" content="no"><meta name="apple-mobile-web-app-status-bar-style" content="default"><meta name="apple-mobile-web-app-title" content="fittrackee_client"><link rel="apple-touch-icon" href="/img/icons/apple-touch-icon-152x152.png"><link rel="mask-icon" href="/img/icons/safari-pinned-tab.svg" color="#4DBA87"><meta name="msapplication-TileImage" content="/img/icons/msapplication-icon-144x144.png"><meta name="msapplication-TileColor" content="#000000"></head><body><noscript><strong>We're sorry but FitTrackee doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div><script src="/static/js/chunk-vendors.6e2f6ef7.js"></script><script src="/static/js/app.d9d90958.js"></script></body></html>

View File

@ -72,7 +72,7 @@ self.__precacheManifest = (self.__precacheManifest || []).concat([
"url": "/img/workouts/start.svg" "url": "/img/workouts/start.svg"
}, },
{ {
"revision": "c26adc3efe5f26cacdbb91b86e3a5e38", "revision": "493ca68b0529951c123ad94e2e8864b1",
"url": "/index.html" "url": "/index.html"
}, },
{ {
@ -88,7 +88,7 @@ self.__precacheManifest = (self.__precacheManifest || []).concat([
"url": "/static/css/admin.e77f8b26.css" "url": "/static/css/admin.e77f8b26.css"
}, },
{ {
"revision": "58739c2038f30839f6ea", "revision": "be8240ee93d2f582e2be",
"url": "/static/css/app.b54fa5fe.css" "url": "/static/css/app.b54fa5fe.css"
}, },
{ {
@ -112,8 +112,8 @@ self.__precacheManifest = (self.__precacheManifest || []).concat([
"url": "/static/css/statistics.2afdc8a9.css" "url": "/static/css/statistics.2afdc8a9.css"
}, },
{ {
"revision": "af09a25559d1f134383d", "revision": "9276c3f7a6a4697c1656",
"url": "/static/css/workouts.c5521765.css" "url": "/static/css/workouts.fe5cb36d.css"
}, },
{ {
"revision": "e719f9244c69e28e7d00e725ca1e280e", "revision": "e719f9244c69e28e7d00e725ca1e280e",
@ -200,8 +200,8 @@ self.__precacheManifest = (self.__precacheManifest || []).concat([
"url": "/static/js/admin.5f46d0fe.js" "url": "/static/js/admin.5f46d0fe.js"
}, },
{ {
"revision": "58739c2038f30839f6ea", "revision": "be8240ee93d2f582e2be",
"url": "/static/js/app.15c3e6ab.js" "url": "/static/js/app.d9d90958.js"
}, },
{ {
"revision": "bd7d183c9f68e5f4027d", "revision": "bd7d183c9f68e5f4027d",
@ -240,7 +240,7 @@ self.__precacheManifest = (self.__precacheManifest || []).concat([
"url": "/static/js/statistics.d03ca304.js" "url": "/static/js/statistics.d03ca304.js"
}, },
{ {
"revision": "af09a25559d1f134383d", "revision": "9276c3f7a6a4697c1656",
"url": "/static/js/workouts.dcd0b64a.js" "url": "/static/js/workouts.becfe714.js"
} }
]); ]);

View File

@ -14,7 +14,7 @@
importScripts("https://storage.googleapis.com/workbox-cdn/releases/4.3.1/workbox-sw.js"); importScripts("https://storage.googleapis.com/workbox-cdn/releases/4.3.1/workbox-sw.js");
importScripts( importScripts(
"/precache-manifest.c7eccdefc201d4db22dbdf20aa0937a3.js" "/precache-manifest.c471b20527d5a4f06820639e2531369f.js"
); );
workbox.core.setCacheNameDetails({prefix: "fittrackee_client"}); workbox.core.setCacheNameDetails({prefix: "fittrackee_client"});

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

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

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

File diff suppressed because one or more lines are too long

View File

@ -29,6 +29,7 @@ def get_weather(point: GPXRoutePoint) -> Optional[Dict]:
'temperature': weather.temperature, 'temperature': weather.temperature,
'humidity': weather.humidity, 'humidity': weather.humidity,
'wind': weather.windSpeed, 'wind': weather.windSpeed,
'windBearing': weather.windBearing,
} }
except Exception as e: except Exception as e:
appLog.error(e) appLog.error(e)

View File

@ -0,0 +1,58 @@
<template>
<div class="wind">
<Distance
:distance="weather.wind"
unitFrom="m"
:digits="1"
:displayUnit="false"
:useImperialUnits="useImperialUnits"
/>
{{ useImperialUnits ? 'ft' : 'm' }}/s
<div class="wind-bearing">
<i
v-if="weather.windBearing"
class="fa fa-long-arrow-up"
:style="{
transform: `rotate(${weather.windBearing}deg)`,
}"
aria-hidden="true"
:title="getWindDirectionTitle(weather.windBearing)"
/>
</div>
</div>
</template>
<script setup lang="ts">
import { toRefs } from 'vue'
import { useI18n } from 'vue-i18n'
import { IWeather } from '@/types/workouts'
import { convertDegreeToDirection } from '@/utils/weather'
interface Props {
weather: IWeather
useImperialUnits: boolean
}
const props = defineProps<Props>()
const { useImperialUnits, weather } = toRefs(props)
const { t } = useI18n()
function getWindDirectionTitle(windBearing: number): string {
return t(
`workouts.WEATHER.WIND_DIRECTIONS.${convertDegreeToDirection(
windBearing
)}`
)
}
</script>
<style lang="scss" scoped>
.wind {
display: flex;
justify-content: center;
.wind-bearing {
padding-left: 5px;
}
}
</style>

View File

@ -90,24 +90,16 @@
/> />
</td> </td>
<td> <td>
<Distance <WeatherWind
:distance="workoutObject.weatherStart.wind" :weather="workoutObject.weatherStart"
unitFrom="m"
:digits="1"
:displayUnit="false"
:useImperialUnits="useImperialUnits" :useImperialUnits="useImperialUnits"
/> />
{{ useImperialUnits ? 'ft' : 'm' }}/s
</td> </td>
<td> <td>
<Distance <WeatherWind
:distance="workoutObject.weatherEnd.wind" :weather="workoutObject.weatherEnd"
unitFrom="m"
:digits="1"
:displayUnit="false"
:useImperialUnits="useImperialUnits" :useImperialUnits="useImperialUnits"
/> />
{{ useImperialUnits ? 'ft' : 'm' }}/s
</td> </td>
</tr> </tr>
</tbody> </tbody>
@ -118,6 +110,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { toRefs } from 'vue' import { toRefs } from 'vue'
import WeatherWind from '@/components/Workout/WorkoutDetail/WeatherWind.vue'
import { IWorkoutObject } from '@/types/workouts' import { IWorkoutObject } from '@/types/workouts'
interface Props { interface Props {

View File

@ -60,6 +60,24 @@
"HUMIDITY": "humidity", "HUMIDITY": "humidity",
"TEMPERATURE": "temperature", "TEMPERATURE": "temperature",
"WIND": "wind", "WIND": "wind",
"WIND_DIRECTIONS": {
"N": "N",
"NNE": "NNE",
"NE": "NE",
"ENE": "ENE",
"E": "E",
"ESE": "ESE",
"SE": "SE",
"SSE": "SSE",
"S": "S",
"SSW": "SSW",
"SW": "SW",
"WSW": "WSW",
"W": "W",
"WNW": "WNW",
"NW": "NW",
"NNW": "NNW"
},
"DARK_SKY": { "DARK_SKY": {
"clear-day": "clear day", "clear-day": "clear day",
"clear-night": "clear night", "clear-night": "clear night",
@ -70,7 +88,7 @@
"rain": "rain", "rain": "rain",
"sleet": "sleet", "sleet": "sleet",
"snow": "snow", "snow": "snow",
"wind": "wind" "wind": "wind"
} }
}, },
"WITH_GPX": "with .gpx file", "WITH_GPX": "with .gpx file",

View File

@ -60,6 +60,24 @@
"HUMIDITY": "humidité", "HUMIDITY": "humidité",
"TEMPERATURE": "température", "TEMPERATURE": "température",
"WIND": "vent", "WIND": "vent",
"WIND_DIRECTIONS": {
"N": "N",
"NNE": "NNE",
"NE": "NE",
"ENE": "ENE",
"E": "E",
"ESE": "ESE",
"SE": "SE",
"SSE": "SSE",
"S": "S",
"SSW": "SSO",
"SW": "SO",
"WSW": "OSO",
"W": "O",
"WNW": "ONO",
"NW": "NO",
"NNW": "NNO"
},
"DARK_SKY": { "DARK_SKY": {
"clear-day": "ensoleillé", "clear-day": "ensoleillé",
"clear-night": "nuit claire", "clear-night": "nuit claire",

View File

@ -43,6 +43,7 @@ export interface IWeather {
summary: string summary: string
temperature: number temperature: number
wind: number wind: number
windBearing?: number
} }
export interface IWorkout { export interface IWorkout {

View File

@ -0,0 +1,23 @@
const directions = [
'N',
'NNE',
'NE',
'ENE',
'E',
'ESE',
'SE',
'SSE',
'S',
'SSW',
'SW',
'WSW',
'W',
'WNW',
'NW',
'NNW',
]
export const convertDegreeToDirection = (angle: number): string => {
const value = Math.floor(angle / 22.5 + 0.5)
return directions[value % 16]
}

View File

@ -0,0 +1,23 @@
import { assert } from 'chai'
import { convertDegreeToDirection } from '@/utils/weather'
describe('convertDegreeToDirection', () => {
const testsParams: [number, string][] = [
[0, 'N'],
[45, 'NE'],
[90, 'E'],
[135, 'SE'],
[180, 'S'],
[225, 'SW'],
[270, 'W'],
[315, 'NW'],
[22, 'NNE'],
[359, 'N'],
]
testsParams.map((testParams) => {
it(`convert ${testParams[0]}° to ${testParams[1]}`, () => {
assert.equal(convertDegreeToDirection(testParams[0]), testParams[1])
})
})
})