Skip to content

Latest commit

 

History

History
379 lines (296 loc) · 14.8 KB

File metadata and controls

379 lines (296 loc) · 14.8 KB

Work Log

This file tracks significant changes and work sessions on the project.

2025-11-08 - Lap Markers and Bikelog Improvements

Summary

Implemented lap marker support for KML generation and comprehensive bikelog/PDF improvements including description parsing, weight extraction, and improved formatting.

Changes Made

Lap Marker Feature (--laps flag)

  • Added lap marker support to KML generation:

    • New --laps command-line flag to enable lap marker output
    • Fetches detailed activity data to access lap information
    • Optimization: Only fetches detailed data when --laps is enabled and laps array not already present
    • Creates circular marker placemarks at lap button press locations
    • Labels show "Lap 1", "Lap 2", etc. when clicked in Google Earth
    • Labels hidden by default (scale=0) to avoid clutter
  • KML styling for lap markers:

    • Added _addLapMarkerStyle() method to create lap marker icon style
    • Uses Google's placemark_circle.png icon (scale 0.6)
    • Integrated lap marker style into KML header when --laps enabled
  • Implementation details:

    • _outputLapMarkers(): Iterates through laps array and outputs point placemarks
    • _outputLapPoint(): Creates individual KML Point placemark for each lap
    • Uses lap.start_index to find coordinate in activity's coordinate array
    • Coordinates output as lng,lat,0 per KML spec

Bikelog/PDF Generation Improvements

  • Weight extraction from descriptions:

    • Added extractWeight() method with case-insensitive parsing
    • Supports formats: "165", "165 kg", "165kg", "Weight=165", etc.
    • Automatically populates <wt> field in bikelog XML output
    • Weight excluded from note text (appears only in dedicated XML field)
  • Description and private_note merging:

    • Created parseActivityText() method to merge description + private_note
    • Private note content prefixed with "Private: " before merging
    • Combined text parsed for key=value pairs
    • Remaining non-key/value lines become the description
  • Custom properties parsing:

    • Extracts key=value pairs from merged description/private_note text
    • Keys converted to Title Case for consistent output
    • Blank lines filtered out from final description
    • Special handling for "weight" key (extracted to XML field)
  • Bikelog formatting improvements:

    • Double newline separator between multiple activities on same day
    • Moving/Elapsed times now appear BEFORE description text
    • Custom property key/value pairs appear AFTER description
    • All keys displayed in Title Case (e.g., "Biker:", "Motor:")
  • PDF generation enhancement:

    • Now fetches detailed activity data to access description and private_note fields
    • Consistent with KML lap marker approach for detailed data fetching

Type and Configuration Updates

  • Added types:

    • laps?: boolean to Kml.Opts type
    • Added to option definitions in cmd/options/definitions.ts
    • Added to KML command configuration
  • Files modified:

    • src/app/app.ts: Detailed activity fetching for laps and PDF
    • src/bikelog/bikelog.ts: Description parsing, weight extraction, formatting
    • src/kml/kml.ts: Lap marker style and output methods
    • src/kml/types.ts: Added laps option type
    • src/cmd/kml/cmd.ts: Added laps to command config
    • src/cmd/options/definitions.ts: Defined --laps option

Testing Notes

  • Lap markers ready for testing in Google Earth
  • Weight extraction tested with various formats
  • Description parsing handles edge cases (blank lines, empty fields)

2025-11-07 - KML Generation Implementation

Summary

Implemented the complete getKml() method in app.ts to fetch activities from Strava and generate KML files. Fixed type errors throughout the KML module.

Changes Made

app.ts - getKml() Implementation

  • Implemented complete workflow:
    1. Initialize KML generator with options and line styles
    2. Validate that activities or segments requested
    3. Fetch activities for each date range using Api.getActivities()
    4. Convert returned Dict[] to Activity.Base[] objects
    5. Filter activities based on commute option ('yes', 'no', or 'all')
    6. Fetch coordinates for each activity using Api.getStreamCoords()
    7. Generate KML file with kml.outputData()
  • API Usage:
    • Used correct Api.ActivityOpts type with athleteId and query parameters
    • Properly convert epoch timestamps from Date objects
    • Handle FileSpec and string output paths correctly

kml/kml.ts - Type Fixes

  • Fixed imports: Added compare, escapeHtml, fieldCapitalize, Fmt from fmt.ts
  • Removed unused Main reference: Deleted private main: Main; field
  • Fixed _dateString(): Properly handle DateRangeDef[] with Date objects, format to ISO strings
  • Simplified _buildActivityDescription(): Basic implementation with distance and elevation (full implementation marked as TODO)
  • Fixed type annotations: Added proper types to all parameters and return values
  • Removed verbose option: No longer referencing removed option

kml/types.ts - Type Corrections

  • Changed dates type: From DateRanges[] to DateRangeDef[] (correct type from @epdoc/daterange)
  • Added Coord type: [number, number] for lat/lng pairs
  • Updated PlacemarkParams: Changed coordinates from unknown[] to Coord[]

fmt.ts - Type Safety

  • Fixed compare function: Made generic with proper type guards for string and number comparison
  • Added type annotations: All parameters now properly typed (name, unsafe, $1)
  • Fixed Fmt class: All calls to precision() now properly scoped as Fmt.precision()

segment module - Type Fixes

  • segment/dep.ts: Fixed import paths to use correct relative paths to strava-api
  • segment/base.ts:
    • Fixed Schema import path
    • Added default initializers for all properties
  • segment/data.ts:
    • Fixed Coord import
    • Added default initializers for all properties
    • Used localeCompare for string sorting instead of generic compare

Status

  • ✅ getKml() method fully implemented
  • ✅ Type checking passes for app.ts and kml.ts
  • ✅ All imports corrected
  • ⏳ Segment fetching not yet implemented (marked as TODO)
  • ⏳ Full activity description not implemented (marked as TODO)
  • ⏳ End-to-end testing needed

Next Steps

  • Test KML generation with real Strava data
  • Implement segment fetching if needed
  • Enhance activity descriptions with full details
  • Address remaining type errors in other parts of codebase

2025-11-07 - FileSpecWriter Migration

Summary

Migrated KML generation from old stream-based writing to new FileSpecWriter with async/await patterns.

Changes to @epdoc/fs (v1.1.1 → v1.1.2)

Repository: /Users/jpravetz/dev/@epdoc/std/fs

Modified Files

  • src/fs.ts: Added FileSpecWriter as Writer to exports for convenient access via FS.Writer
  • deno.json:
    • Bumped version to 1.1.2
    • Updated description to: "Type-safe file system operations with FileSpec, FolderSpec, and streaming support for Node.js and Deno"

Commits

  • 1126e93 - Export FileSpecWriter as Writer from fs module
  • ec9401c - Improve package description for @epdoc/fs

Testing

Changes to @epdoc/strava/packages/strava

Repository: /Users/jpravetz/dev/@epdoc/strava

Modified Files

  • packages/strava/src/kml/kml.ts: Complete refactor of KmlMain class

Detailed Changes

  1. Replaced stream with writer:

    • Changed private stream: fs.WriteStream to private writer?: FS.Writer
  2. Refactored outputData() method:

    • Removed event-based stream handling (stream.once('open'), etc.)
    • Replaced promise chain with clean async/await
    • Added proper try/catch with resource cleanup
  3. Refactored addActivities() method:

    • Removed .reduce() promise chain
    • Replaced with for...of loop with await
    • Simplified control flow
  4. Refactored addSegments() method:

    • Replaced .forEach() with for...of loops
    • Made properly async
  5. Updated header() and footer() methods:

    • Converted to async/await
    • Fixed to write to buffer (not directly to writer parameter)
  6. Updated flush() and _flush() methods:

    • Now properly writes buffered content to FileSpecWriter
    • Uses await this.writer.write(content)

Dependencies Updated

  • Updated @epdoc/fs from 1.1.1 to 1.1.2 in both:
    • packages/strava/deno.json
    • packages/strava-api/deno.json

Status

  • Changes complete, code compiles
  • Not yet committed
  • Pre-existing type errors in other parts of codebase (unrelated to these changes)

Impact

  • Cleaner, more maintainable code with modern async/await patterns
  • Eliminates complex event handling and promise chains
  • Proper resource management with try/catch cleanup
  • Consistent with project code style guidelines

Next Steps

  • Test KML generation end-to-end
  • Complete app.getKml() implementation
  • Address remaining TODOs in the codebase

2025-11-07 - Documentation and KML Options

Summary

Created project documentation files (CLAUDE.md, WORKLOG.md) and completed KML command options to match legacy implementation requirements.

Documentation Created

  1. @epdoc/strava/CLAUDE.md - Project guide with:

    • Repository structure and relationships
    • Key dependencies (@epdoc/logger, @epdoc/cliapp, @epdoc/std)
    • Architecture patterns (command structure, options system)
    • Development workflow
    • Code style guidelines
  2. @epdoc/strava/WORKLOG.md - Work log for tracking changes

  3. @epdoc/logger/CLAUDE.md - Guide referencing GEMINI.md for detailed docs

  4. @epdoc/std/CLAUDE.md - Monorepo guide with package overview

KML Options Completed

Added to src/cmd/options/definitions.ts:

  • segmentsFlatFolder - Flat folder structure option for segments
  • imperial - Imperial units option (was missing from definitions)

Updated src/cmd/kml/cmd.ts cmdConfig:

Added missing options to enable:

  • segmentsFlatFolder - Flat vs hierarchical segment folders
  • imperial - Imperial units support
  • refresh - Refresh starred segments list

Updated src/kml/types.ts Kml.Opts:

  • Added output - Output filename (string | FileSpec)
  • Added commute - Commute filter ('yes' | 'no' | 'all')
  • Added dryRun - Dry run mode
  • Added segmentsFlatFolder - Flat folder option
  • Added imperial - Imperial units
  • Added refresh - Refresh segments
  • Removed verbose - No longer needed (using @epdoc/logger)
  • Added detailed comments for all fields

Options Coverage

KML command now supports all options from legacy implementation:

  • ✅ output (filename)
  • ✅ dates (date ranges)
  • ✅ more (additional details)
  • ✅ commute (filter)
  • ✅ dryRun
  • ✅ activities (with optional filter)
  • ✅ segments
  • ✅ segmentsFlatFolder
  • ✅ imperial
  • ✅ refresh

Status

  • All KML options defined and configured
  • Types updated to match
  • Ready for implementation testing

2025-11-07 - Global Options and Segments Command

Summary

Corrected option handling for global vs command-specific options, and created new segments command structure.

Global Options Clarification

Discovery: imperial is a global option defined at the root command level, not a command-specific option.

How Global Options Work:

  • Global options are defined in src/cmd/root/cmd.ts via addOptions()
  • Commander.js automatically merges parent (global) options into child command options
  • Command action handlers receive both global and command-specific options in the options parameter
  • Currently defined global options:
    • --id <athleteId> - Athlete ID
    • --imperial - Use imperial units
    • --offline - Offline mode

Changes Made:

  • Removed imperial from src/cmd/options/definitions.ts (was incorrectly added as command-specific)
  • Removed imperial from src/cmd/kml/cmd.ts cmdConfig (now uses global option)
  • Added comment in kml cmdConfig explaining that imperial is global
  • Kept imperial in Kml.Opts type since it will be available via global option merging

Segments Command Created

Purpose: Analyze starred segments with effort times (separate from KML generation)

Files Created:

  • src/cmd/segments/cmd.ts - Command implementation with refresh option
  • src/cmd/segments/mod.ts - Module exports

Registration:

  • Added import in src/cmd/root/cmd.ts
  • Registered command in root command initialization

Options for Segments Command:

  • dates - Date range filtering
  • refresh - Refresh starred segments list from Strava
  • dryRun - Dry run mode

Note: refresh option belongs to segments command, not KML command. Segments are fetched/refreshed via segments command, then can be output to KML via kml command.

Updated Option Assignments

KML Command Options (command-specific):

  • output, dates, more, commute, dryRun, activities, segments, segmentsFlatFolder
  • Uses global imperial option

Segments Command Options (command-specific):

  • dates, refresh, dryRun
  • Uses global imperial option

Status

  • Segments command structure complete (implementation TODO)
  • Global vs command-specific options properly separated
  • All commands registered and ready for implementation

2025-11-07 - Lint Fixes and Runtime Error Resolution

Summary

Fixed lint errors and runtime error when running kml command.

Lint Fixes

Files Modified:

  • src/cmd/segments/cmd.ts - Prefixed unused opts parameter with underscore
  • src/kml/kml.ts - Prefixed unused segment parameter with underscore in buildSegmentDescription()

Runtime Error Fix

Issue: Cannot use 'in' operator to search for 'Commute' in undefined

Root Causes:

  1. isValidActivityType() guard was checking Api.ActivityName which doesn't exist
  2. Correct path is Api.Schema.ActivityName
  3. LineStyleDefs type was too restrictive - only allowed Api.ActivityType | 'Default'
  4. Custom style names like 'Commute', 'Moto', 'Segment' were not supported

Changes Made:

  1. src/kml/types.ts:

    • Changed LineStyleDefs from Record<Api.ActivityType | 'Default', LineStyle> to Record<string, LineStyle>
    • Removed unused Api import
    • Added comment explaining that it supports ActivityTypes plus custom names
  2. src/kml/guards.ts:

    • Fixed path from Api.ActivityName to Api.Schema.ActivityName
    • Changed return type from name is Api.ActivityType to boolean (since we now allow any string)
    • Added explicit checks for custom style names: 'Default', 'Commute', 'Moto', 'Segment'
    • Added null check for Api.Schema.ActivityName before using in operator

Note: While 'Commute' is now a separate option (not treated as an activity type), it remains a valid line style name for backwards compatibility and styling purposes.

Status

  • All lint errors in modified files resolved
  • Runtime error fixed
  • Code ready for testing