commit 8c1ad7ebdf20220d0f70b193c2a979f9b8e03518 Author: friedemann.blume Date: Sun Jul 21 14:10:43 2024 +0200 init - v1 diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..77f818a --- /dev/null +++ b/Dockerfile @@ -0,0 +1,38 @@ +FROM debian:bookworm-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y \ + python3 \ + python3-venv \ + python3-pip \ + gpsbabel \ + && rm -rf /var/lib/apt/lists/* + +# Set up app directory +WORKDIR /app + +# Copy application files +COPY app.py /app/ +COPY templates /app/templates +COPY requirements.txt /app/ + +# Create and activate virtual environment +ENV VIRTUAL_ENV=/opt/venv +RUN python3 -m venv $VIRTUAL_ENV +ENV PATH="$VIRTUAL_ENV/bin:$PATH" + +# Install Python dependencies in virtual environment +RUN pip install --upgrade pip && pip install --no-cache-dir -r requirements.txt + +# Create input and output directories +RUN mkdir /app/input /app/output + +# Set environment variables +ENV FLASK_APP=app.py +ENV FLASK_RUN_HOST=0.0.0.0 + +# Expose port +EXPOSE 5000 + +# Run the application +CMD ["python", "-m", "flask", "run"] diff --git a/Readme.md b/Readme.md new file mode 100644 index 0000000..9a29d65 --- /dev/null +++ b/Readme.md @@ -0,0 +1,82 @@ +# FIT to GPX Converter + +FIT to GPX Converter is a web application that allows users to easily convert Garmin FIT (Flexible and Interoperable Data Transfer) files to GPX (GPS Exchange Format) files. This tool is particularly useful for athletes, outdoor enthusiasts, and anyone who needs to convert their fitness tracking data from FIT to the more widely compatible GPX format. + +## Features + +- Upload FIT files through a user-friendly web interface +- Automatic conversion of FIT files to GPX format +- View and manage both input (FIT) and output (GPX) files +- Select and delete multiple files +- Responsive design for both desktop and mobile use +- Auto-refresh to show newly converted files + +## Prerequisites + +Before you begin, ensure you have met the following requirements: + +- Docker and Docker Compose installed on your system +- Git for cloning the repository + +## Installation + +To install FIT to GPX Converter, follow these steps: + +1. Clone the repository: + ``` + git clone https://github.com/your-username/fit-to-gpx-converter.git + cd fit-to-gpx-converter + ``` + +2. Build the Docker image: + ``` + docker-compose build + ``` + +3. Start the application: + ``` + docker-compose up + ``` + +The application should now be running and accessible at `http://localhost:5000`. + +## Usage + +1. Open your web browser and navigate to `http://localhost:5000`. +2. Use the file input to select and upload a .fit file. +3. Click the "Upload and Convert" button. +4. Wait for the conversion process to complete. +5. The converted .gpx file will appear in the "Output Files" list. +6. Click on the file name in the "Output Files" list to download the converted .gpx file. + +### Managing Files + +- To delete input or output files, select the checkboxes next to the files you wish to remove, then click the "Delete Selected Files" button for the respective list. +- The page automatically refreshes every 30 seconds to show newly converted files. + +## Contributing + +Contributions to the FIT to GPX Converter are welcome. Please follow these steps to contribute: + +1. Fork the repository. +2. Create a new branch: `git checkout -b `. +3. Make your changes and commit them: `git commit -m ''` +4. Push to the original branch: `git push origin /` +5. Create the pull request. + +Alternatively, see the GitHub documentation on [creating a pull request](https://help.github.com/articles/creating-a-pull-request/). + +## License + +This project uses the following license: [MIT License](https://opensource.org/licenses/MIT). + +## Contact + +If you want to contact the maintainer of this project, please email [your-email@example.com](mailto:your-email@example.com). + +## Acknowledgements + +- [Flask](https://flask.palletsprojects.com/) - The web framework used +- [GPSBabel](https://www.gpsbabel.org/) - Used for file conversion +- [Tailwind CSS](https://tailwindcss.com/) - Used for styling +- [Alpine.js](https://alpinejs.dev/) - Used for interactive elements diff --git a/app.py b/app.py new file mode 100644 index 0000000..cc366c5 --- /dev/null +++ b/app.py @@ -0,0 +1,71 @@ +import os +from flask import Flask, request, render_template, send_from_directory, redirect, url_for, flash, jsonify +import subprocess +from werkzeug.utils import secure_filename + +app = Flask(__name__) +app.secret_key = 'your_secret_key_here' # Required for flash messaging + +INPUT_FOLDER = '/app/input' +OUTPUT_FOLDER = '/app/output' +ALLOWED_EXTENSIONS = {'fit'} + +app.config['INPUT_FOLDER'] = INPUT_FOLDER +app.config['OUTPUT_FOLDER'] = OUTPUT_FOLDER + +def allowed_file(filename): + return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS + +@app.route('/', methods=['GET', 'POST']) +def upload_file(): + if request.method == 'POST': + if 'file' not in request.files: + flash('No file part', 'error') + return redirect(url_for('upload_file')) + file = request.files['file'] + if file.filename == '': + flash('No selected file', 'error') + return redirect(url_for('upload_file')) + if file and allowed_file(file.filename): + filename = secure_filename(file.filename) + input_path = os.path.join(app.config['INPUT_FOLDER'], filename) + file.save(input_path) + + output_filename = f"{os.path.splitext(filename)[0]}.gpx" + output_path = os.path.join(app.config['OUTPUT_FOLDER'], output_filename) + + subprocess.run(['gpsbabel', '-i', 'garmin_fit', '-f', input_path, '-o', 'gpx', '-F', output_path]) + + flash('File uploaded and converted successfully', 'success') + return redirect(url_for('upload_file')) + else: + flash('Invalid file type. Please upload a .fit file.', 'error') + return redirect(url_for('upload_file')) + + input_files = os.listdir(app.config['INPUT_FOLDER']) + output_files = os.listdir(app.config['OUTPUT_FOLDER']) + return render_template('index.html', input_files=input_files, output_files=output_files) + +@app.route('/download/') +def download_file(filename): + return send_from_directory(app.config['OUTPUT_FOLDER'], filename, as_attachment=True) + +@app.route('/delete', methods=['POST']) +def delete_files(): + files_to_delete = request.json.get('files', []) + folder = request.json.get('folder', '') + + if folder not in ['input', 'output']: + return jsonify({'error': 'Invalid folder specified'}), 400 + + target_folder = app.config['INPUT_FOLDER'] if folder == 'input' else app.config['OUTPUT_FOLDER'] + + for filename in files_to_delete: + file_path = os.path.join(target_folder, filename) + if os.path.exists(file_path): + os.remove(file_path) + + return jsonify({'message': 'Files deleted successfully'}), 200 + +if __name__ == '__main__': + app.run(debug=True, host='0.0.0.0') diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..cbfff9e --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,8 @@ +services: + fit2gpx: + image: fit2gpx + ports: + - "5001:5000" + volumes: + - ./input:/app/input + - ./output:/app/output diff --git a/input/1713548128_activities.fit b/input/1713548128_activities.fit new file mode 100644 index 0000000..bd8b713 Binary files /dev/null and b/input/1713548128_activities.fit differ diff --git a/input/1718720200_activities.fit b/input/1718720200_activities.fit new file mode 100644 index 0000000..402600b Binary files /dev/null and b/input/1718720200_activities.fit differ diff --git a/output/1713548128_activities.gpx b/output/1713548128_activities.gpx new file mode 100644 index 0000000..4dd7d37 --- /dev/null +++ b/output/1713548128_activities.gpx @@ -0,0 +1,720 @@ + + + + + + LAP001 + LAP001 + LAP001 + + + + + 119.800 + + 4.180000 + + + 120.600 + + 5.141000 + + + 121.400 + + 4.637000 + + + 122.000 + + 5.141000 + + + 122.200 + + 4.637000 + + + 123.800 + + 3.938000 + + + 124.200 + + 3.919000 + + + 124.600 + + 3.919000 + + + 124.800 + + 5.160000 + + + 125.000 + + 5.440000 + + + 125.000 + + 3.228000 + + + 125.200 + + 4.731000 + + + 125.200 + + 4.731000 + + + 125.200 + + 4.721000 + + + 125.200 + + 4.385000 + + + 125.600 + + 4.721000 + + + 125.800 + + 4.563000 + + + 126.000 + + 4.339000 + + + 126.000 + + 4.385000 + + + 126.200 + + 4.563000 + + + 126.200 + + 4.563000 + + + 126.200 + + 4.964000 + + + 126.400 + + 4.964000 + + + 126.400 + + 4.964000 + + + 126.600 + + 5.048000 + + + 126.600 + + 5.645000 + + + 126.800 + + 6.457000 + + + 126.000 + + 5.766000 + + + 126.200 + + 5.048000 + + + 126.400 + + 3.891000 + + + 126.600 + + 4.507000 + + + 126.400 + + 5.757000 + + + 126.400 + + 5.113000 + + + 127.600 + + 5.496000 + + + 128.200 + + 5.570000 + + + 128.600 + + 5.141000 + + + 129.000 + + 5.095000 + + + 129.200 + + 4.059000 + + + 129.400 + + 2.118000 + + + 129.400 + + 1.903000 + + + 129.600 + + 0.000000 + + + + + 130.400 + + 2.762000 + + + 130.400 + + 3.256000 + + + 129.800 + + 4.152000 + + + 130.200 + + 5.179000 + + + 131.200 + + 5.272000 + + + 131.800 + + 5.179000 + + + 131.800 + + 5.281000 + + + 131.400 + + 5.552000 + + + 130.800 + + 5.552000 + + + 130.600 + + 5.552000 + + + 129.600 + + 5.636000 + + + 129.400 + + 5.636000 + + + 129.000 + + 5.710000 + + + 129.000 + + 6.466000 + + + 128.600 + + 6.485000 + + + 128.400 + + 6.597000 + + + 128.400 + + 6.541000 + + + 127.400 + + 6.494000 + + + 127.400 + + 6.597000 + + + 127.400 + + 6.597000 + + + 127.400 + + 6.597000 + + + 127.400 + + 6.587000 + + + 127.200 + + 6.364000 + + + 126.600 + + 6.783000 + + + 125.800 + + 6.783000 + + + 124.800 + + 6.765000 + + + 124.200 + + 6.765000 + + + 123.800 + + 6.765000 + + + 123.600 + + 6.877000 + + + 123.600 + + 6.895000 + + + 123.400 + + 6.914000 + + + 123.400 + + 6.914000 + + + 123.400 + + 6.914000 + + + 123.200 + + 6.821000 + + + 122.600 + + 6.662000 + + + 122.400 + + 6.662000 + + + 122.200 + + 6.653000 + + + 121.600 + + 6.653000 + + + 121.400 + + 6.653000 + + + 120.600 + + 6.615000 + + + 120.400 + + 6.615000 + + + 120.400 + + 6.522000 + + + 119.600 + + 7.007000 + + + 119.400 + + 7.007000 + + + 118.200 + + 7.091000 + + + 117.400 + + 7.147000 + + + 117.400 + + 7.147000 + + + 117.200 + + 7.091000 + + + 116.800 + + 7.091000 + + + 116.800 + + 7.091000 + + + 116.400 + + 6.802000 + + + 116.200 + + 6.606000 + + + 116.200 + + 6.578000 + + + 115.800 + + 5.953000 + + + 115.800 + + 6.364000 + + + 115.800 + + 5.309000 + + + 115.600 + + 6.205000 + + + 115.400 + + 6.205000 + + + 115.000 + + 6.205000 + + + 114.800 + + 6.895000 + + + 114.000 + + 7.269000 + + + 114.000 + + 7.259000 + + + 113.600 + + 6.998000 + + + 113.600 + + 6.998000 + + + 113.200 + + 7.045000 + + + 113.000 + + 7.073000 + + + 112.800 + + 7.026000 + + + 112.200 + + 7.455000 + + + 112.200 + + 6.522000 + + + 112.400 + + 6.522000 + + + 112.400 + + 6.513000 + + + 112.600 + + 6.298000 + + + 112.600 + + 6.336000 + + + 112.800 + + 6.298000 + + + 113.000 + + 6.298000 + + + 113.000 + + 6.298000 + + + 113.200 + + 6.336000 + + + 113.400 + + 6.503000 + + + 113.600 + + 6.476000 + + + 114.000 + + 6.476000 + + + 114.400 + + 6.476000 + + + 115.000 + + 6.550000 + + + 115.200 + + 6.550000 + + + 116.400 + + 6.550000 + + + 117.200 + + 5.682000 + + + 117.400 + + 5.682000 + + + 117.800 + + 5.944000 + + + 118.400 + + 4.824000 + + + 118.600 + + 4.787000 + + + 119.000 + + 3.387000 + + + 119.800 + + 2.043000 + + + 120.000 + + 0.000000 + + + + + 120.400 + + 1.717000 + + + 120.800 + + 1.717000 + + + 121.000 + + 2.090000 + + + 121.000 + + 1.698000 + + + 121.600 + + 0.000000 + + + + + 121.600 + + 1.661000 + + + 121.400 + + 1.642000 + + + + diff --git a/output/1718720200_activities.gpx b/output/1718720200_activities.gpx new file mode 100644 index 0000000..1aedc22 --- /dev/null +++ b/output/1718720200_activities.gpx @@ -0,0 +1,746 @@ + + + + + + LAP001 + LAP001 + LAP001 + + + + + 108.400 + + 6.578000 + + + 108.400 + + 6.578000 + + + 108.000 + + 6.578000 + + + 109.200 + + 6.485000 + + + 109.400 + + 6.485000 + + + 110.200 + + 6.401000 + + + 110.400 + + 6.392000 + + + 110.600 + + 6.392000 + + + 110.600 + + 6.354000 + + + 111.200 + + 6.354000 + + + 111.400 + + 6.354000 + + + 111.800 + + 6.354000 + + + 111.800 + + 6.317000 + + + 112.000 + + 6.261000 + + + 112.200 + + 6.261000 + + + 112.400 + + 6.140000 + + + 112.600 + + 6.140000 + + + 112.600 + + 6.140000 + + + 113.000 + + 6.186000 + + + 113.200 + + 6.252000 + + + 113.600 + + 6.336000 + + + 113.800 + + 6.410000 + + + 114.600 + + 6.476000 + + + 114.600 + + 6.522000 + + + 114.800 + + 6.522000 + + + 115.200 + + 6.466000 + + + 115.400 + + 6.457000 + + + 115.200 + + 6.354000 + + + 115.200 + + 6.289000 + + + 115.200 + + 6.102000 + + + 115.200 + + 6.084000 + + + 115.800 + + 6.084000 + + + 116.200 + + 5.981000 + + + 116.600 + + 5.860000 + + + 116.800 + + 5.645000 + + + 116.600 + + 5.645000 + + + 116.600 + + 5.645000 + + + 116.400 + + 5.748000 + + + 116.400 + + 6.289000 + + + 117.200 + + 6.326000 + + + 117.800 + + 6.326000 + + + 117.800 + + 6.326000 + + + 117.400 + + 6.354000 + + + 117.600 + + 6.354000 + + + 117.600 + + 6.224000 + + + 117.600 + + 6.121000 + + + 117.600 + + 5.860000 + + + 118.200 + + 4.749000 + + + 118.400 + + 4.749000 + + + 118.400 + + 5.291000 + + + 118.800 + + 5.468000 + + + 119.200 + + 5.374000 + + + 119.200 + + 5.365000 + + + 120.000 + + 6.811000 + + + 120.400 + + 7.735000 + + + 121.000 + + 8.127000 + + + 121.000 + + 7.866000 + + + 120.800 + + 7.539000 + + + 120.600 + + 7.539000 + + + 120.200 + + 7.437000 + + + 119.800 + + 7.269000 + + + 119.600 + + 7.101000 + + + 119.400 + + 6.998000 + + + 119.400 + + 6.998000 + + + 119.200 + + 6.793000 + + + 119.000 + + 6.755000 + + + 118.600 + + 6.737000 + + + 118.400 + + 6.494000 + + + 118.200 + + 5.953000 + + + 118.400 + + 6.280000 + + + 118.400 + + 6.438000 + + + 118.400 + + 6.438000 + + + 118.200 + + 6.438000 + + + 117.800 + + 6.559000 + + + 117.800 + + 6.578000 + + + 117.800 + + 6.578000 + + + 117.800 + + 6.559000 + + + 117.800 + + 6.559000 + + + 117.800 + + 6.448000 + + + 118.600 + + 6.018000 + + + 119.200 + + 6.018000 + + + 119.600 + + 5.990000 + + + 119.800 + + 5.832000 + + + 120.200 + + 5.057000 + + + 120.200 + + 5.580000 + + + 119.600 + + 6.149000 + + + 119.200 + + 6.513000 + + + 119.000 + + 6.541000 + + + 118.400 + + 6.149000 + + + 118.000 + + 6.448000 + + + 117.800 + + 6.867000 + + + 117.200 + + 7.931000 + + + 115.800 + + 8.006000 + + + 114.800 + + 6.709000 + + + 114.400 + + 5.962000 + + + 113.800 + + 4.647000 + + + 113.000 + + 3.630000 + + + 112.400 + + 5.188000 + + + 112.400 + + 4.283000 + + + 112.600 + + 3.807000 + + + 111.600 + + 5.029000 + + + 110.800 + + 6.326000 + + + 110.200 + + 7.567000 + + + 109.800 + + 7.931000 + + + 109.600 + + 8.099000 + + + 108.400 + + 8.043000 + + + 108.000 + + 7.931000 + + + 107.600 + + 8.099000 + + + 107.200 + + 8.099000 + + + 106.400 + + 8.099000 + + + 106.200 + + 8.071000 + + + 105.600 + + 7.521000 + + + 105.400 + + 6.476000 + + + 105.000 + + 5.384000 + + + 104.200 + + 2.631000 + + + 104.000 + + 2.165000 + + + 103.800 + + 1.316000 + + + 103.800 + + 1.306000 + + + 103.600 + + 1.316000 + + + 103.600 + + 1.306000 + + + 103.400 + + 1.316000 + + + 103.200 + + 5.160000 + + + 103.000 + + 5.505000 + + + 103.000 + + 5.160000 + + + 102.800 + + 5.169000 + + + 102.400 + + 6.392000 + + + 102.400 + + 6.802000 + + + 102.200 + + 7.483000 + + + 101.600 + + 7.390000 + + + 101.200 + + 7.241000 + + + 100.000 + + 7.241000 + + + 98.600 + + 6.793000 + + + 98.600 + + 2.463000 + + + 99.000 + + 1.708000 + + + 99.000 + + 2.370000 + + + 98.600 + + 1.008000 + + + 98.400 + + 1.008000 + + + 98.400 + + 1.232000 + + + 98.200 + + 1.008000 + + + 98.200 + + 0.998000 + + + 98.200 + + 1.008000 + + + 98.000 + + 1.082000 + + + 98.000 + + 0.000000 + + + + + 98.200 + + 1.633000 + + + 98.200 + + 1.260000 + + + 98.200 + + 1.260000 + + + + diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..8009003 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +Flask==2.2.5 +Werkzeug==2.2.3 diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..7b3a048 --- /dev/null +++ b/templates/index.html @@ -0,0 +1,133 @@ + + + + + + FIT to GPX Converter + + + + + +
+
+

FIT to GPX Converter

+

Convert your .fit files to .gpx format with ease!

+
+ +
+ + + {% with messages = get_flashed_messages(with_categories=true) %} + {% if messages %} + {% for category, message in messages %} + + {% endfor %} + {% endif %} + {% endwith %} + +
+
+ + +
+
+ +
+
+

Input Files:

+ {% if input_files %} +
    + {% for file in input_files %} +
  • + + {{ file }} +
  • + {% endfor %} +
+ + {% else %} +

No input files yet. Upload a .fit file to get started!

+ {% endif %} +
+ +
+

Output Files:

+ {% if output_files %} +
    + {% for file in output_files %} +
  • + + {{ file }} +
  • + {% endfor %} +
+ + {% else %} +

No converted files yet. Upload a .fit file to get started!

+ {% endif %} +
+
+
+
+ +