mirror of
https://github.com/seanmorley15/AdventureLog.git
synced 2025-08-10 07:35:17 +02:00
feat: add wanderer link support in TrailSerializer and TrailCard; update measurement system handling in location page
This commit is contained in:
parent
eb3c9c2b64
commit
c502ae350e
4 changed files with 57 additions and 15 deletions
|
@ -92,11 +92,24 @@ class CategorySerializer(serializers.ModelSerializer):
|
||||||
class TrailSerializer(CustomModelSerializer):
|
class TrailSerializer(CustomModelSerializer):
|
||||||
provider = serializers.SerializerMethodField()
|
provider = serializers.SerializerMethodField()
|
||||||
wanderer_data = serializers.SerializerMethodField()
|
wanderer_data = serializers.SerializerMethodField()
|
||||||
|
wanderer_link = serializers.SerializerMethodField()
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self._wanderer_integration_cache = {}
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Trail
|
model = Trail
|
||||||
fields = ['id', 'user', 'name', 'location', 'created_at','link','wanderer_id', 'provider', 'wanderer_data']
|
fields = ['id', 'user', 'name', 'location', 'created_at','link','wanderer_id', 'provider', 'wanderer_data', 'wanderer_link']
|
||||||
read_only_fields = ['id', 'created_at', 'user', 'provider']
|
read_only_fields = ['id', 'created_at', 'user', 'provider']
|
||||||
|
|
||||||
|
def _get_wanderer_integration(self, user):
|
||||||
|
"""Cache wanderer integration to avoid multiple database queries"""
|
||||||
|
if user.id not in self._wanderer_integration_cache:
|
||||||
|
from integrations.models import WandererIntegration
|
||||||
|
self._wanderer_integration_cache[user.id] = WandererIntegration.objects.filter(user=user).first()
|
||||||
|
return self._wanderer_integration_cache[user.id]
|
||||||
|
|
||||||
def get_provider(self, obj):
|
def get_provider(self, obj):
|
||||||
if obj.wanderer_id:
|
if obj.wanderer_id:
|
||||||
return 'Wanderer'
|
return 'Wanderer'
|
||||||
|
@ -116,23 +129,39 @@ class TrailSerializer(CustomModelSerializer):
|
||||||
if not obj.wanderer_id:
|
if not obj.wanderer_id:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
# Use cached integration
|
||||||
|
integration = self._get_wanderer_integration(obj.user)
|
||||||
|
if not integration:
|
||||||
|
return None
|
||||||
|
|
||||||
# Fetch the Wanderer trail data
|
# Fetch the Wanderer trail data
|
||||||
from integrations.models import WandererIntegration
|
|
||||||
from integrations.wanderer_services import fetch_trail_by_id
|
from integrations.wanderer_services import fetch_trail_by_id
|
||||||
try:
|
try:
|
||||||
integration = WandererIntegration.objects.filter(user=obj.user).first()
|
|
||||||
if not integration:
|
|
||||||
return None
|
|
||||||
|
|
||||||
# Assuming there's a method to fetch trail data by ID
|
|
||||||
trail_data = fetch_trail_by_id(integration, obj.wanderer_id)
|
trail_data = fetch_trail_by_id(integration, obj.wanderer_id)
|
||||||
if not trail_data:
|
if not trail_data:
|
||||||
return None
|
return None
|
||||||
obj.wanderer_data = trail_data
|
|
||||||
|
# Cache the trail data and link on the object to avoid refetching
|
||||||
|
obj._wanderer_data = trail_data
|
||||||
|
base_url = integration.server_url.rstrip('/')
|
||||||
|
obj._wanderer_link = f"{base_url}/trails/{obj.wanderer_id}"
|
||||||
|
|
||||||
return trail_data
|
return trail_data
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error fetching Wanderer trail data for {obj.wanderer_id}")
|
logger.error(f"Error fetching Wanderer trail data for {obj.wanderer_id}: {e}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def get_wanderer_link(self, obj):
|
||||||
|
if not obj.wanderer_id:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Use cached integration
|
||||||
|
integration = self._get_wanderer_integration(obj.user)
|
||||||
|
if not integration:
|
||||||
|
return None
|
||||||
|
|
||||||
|
base_url = integration.server_url.rstrip('/')
|
||||||
|
return f"{base_url}/trail/view/@{integration.username}/{obj.wanderer_id}"
|
||||||
|
|
||||||
|
|
||||||
class ActivitySerializer(CustomModelSerializer):
|
class ActivitySerializer(CustomModelSerializer):
|
||||||
|
|
|
@ -140,9 +140,9 @@
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if trail.link}
|
{#if trail.link || trail.wanderer_link}
|
||||||
<a
|
<a
|
||||||
href={trail.link}
|
href={trail.wanderer_link || trail.link}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
class="btn btn-sm btn-primary"
|
class="btn btn-sm btn-primary"
|
||||||
|
|
|
@ -330,6 +330,7 @@ export type Trail = {
|
||||||
wanderer_id?: string | null; // Optional ID for integration with Wanderer
|
wanderer_id?: string | null; // Optional ID for integration with Wanderer
|
||||||
provider: string; // Provider of the trail data, e.g., 'wanderer', 'external'
|
provider: string; // Provider of the trail data, e.g., 'wanderer', 'external'
|
||||||
wanderer_data: WandererTrail | null; // Optional data from Wanderer integration
|
wanderer_data: WandererTrail | null; // Optional data from Wanderer integration
|
||||||
|
wanderer_link: string | null; // Optional link to the Wanderer trail
|
||||||
};
|
};
|
||||||
|
|
||||||
export type StravaActivity = {
|
export type StravaActivity = {
|
||||||
|
|
|
@ -119,6 +119,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
export let data: PageData;
|
export let data: PageData;
|
||||||
|
let measurementSystem = data.user?.measurement_system || 'metric';
|
||||||
console.log(data);
|
console.log(data);
|
||||||
|
|
||||||
let adventure: AdditionalLocation;
|
let adventure: AdditionalLocation;
|
||||||
|
@ -175,7 +176,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function getTotalDistance(adventure: AdditionalLocation) {
|
function getTotalDistance(adventure: AdditionalLocation) {
|
||||||
return adventure.visits.reduce(
|
const totalMeters = adventure.visits.reduce(
|
||||||
(total, visit) =>
|
(total, visit) =>
|
||||||
total +
|
total +
|
||||||
(visit.activities
|
(visit.activities
|
||||||
|
@ -183,10 +184,14 @@
|
||||||
: 0),
|
: 0),
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Convert meters to km, then to miles if using imperial system
|
||||||
|
const totalKm = totalMeters / 1000;
|
||||||
|
return measurementSystem === 'imperial' ? totalKm * 0.621371 : totalKm;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getTotalElevationGain(adventure: AdditionalLocation) {
|
function getTotalElevationGain(adventure: AdditionalLocation) {
|
||||||
return adventure.visits.reduce(
|
const totalMeters = adventure.visits.reduce(
|
||||||
(total, visit) =>
|
(total, visit) =>
|
||||||
total +
|
total +
|
||||||
(visit.activities
|
(visit.activities
|
||||||
|
@ -194,6 +199,9 @@
|
||||||
: 0),
|
: 0),
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Convert to feet if using imperial system
|
||||||
|
return measurementSystem === 'imperial' ? totalMeters * 3.28084 : totalMeters;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function saveEdit(event: CustomEvent<AdditionalLocation>) {
|
async function saveEdit(event: CustomEvent<AdditionalLocation>) {
|
||||||
|
@ -860,7 +868,9 @@
|
||||||
<div class="stat">
|
<div class="stat">
|
||||||
<div class="stat-title">Total Distance</div>
|
<div class="stat-title">Total Distance</div>
|
||||||
<div class="stat-value text-xl">
|
<div class="stat-value text-xl">
|
||||||
{getTotalDistance(adventure).toFixed(1)} km
|
{getTotalDistance(adventure).toFixed(1)}
|
||||||
|
{#if measurementSystem === 'imperial'}mi
|
||||||
|
{:else}km{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -868,7 +878,9 @@
|
||||||
<div class="stat">
|
<div class="stat">
|
||||||
<div class="stat-title">Total Elevation</div>
|
<div class="stat-title">Total Elevation</div>
|
||||||
<div class="stat-value text-xl">
|
<div class="stat-value text-xl">
|
||||||
{getTotalElevationGain(adventure).toFixed(0)} m
|
{getTotalElevationGain(adventure).toFixed(0)}
|
||||||
|
{#if measurementSystem === 'imperial'}ft
|
||||||
|
{:else}m{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue