Files
spoolman2/scripts/parse_temps_from_comments.py
tonym e264d7ed4e
Some checks failed
CI / style (push) Has been cancelled
CI / build-client (push) Has been cancelled
CI / build-amd64 (push) Has been cancelled
CI / build-tester (push) Has been cancelled
CI / test (cockroachdb) (push) Has been cancelled
CI / test (mariadb) (push) Has been cancelled
CI / test (postgres) (push) Has been cancelled
CI / test (sqlite) (push) Has been cancelled
CI / build-arm64 (push) Has been cancelled
CI / build-armv7 (push) Has been cancelled
CI / publish-images (push) Has been cancelled
CI / publish-release (push) Has been cancelled
Add scripts to parse temps from comments
- parse_temps_api.py - Parse temps via API (for remote instances)
- parse_temps_from_comments.py - Parse temps via direct DB access

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-15 00:00:00 -06:00

191 lines
7.6 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
"""Parse temperature values from filament comments and update temp range fields."""
import re
import asyncio
import sys
from pathlib import Path
# Add the spoolman module to the path
sys.path.insert(0, str(Path(__file__).parent.parent))
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
from sqlalchemy.orm import sessionmaker
from spoolman.database.models import Filament
def parse_temps_from_comment(comment: str) -> dict:
"""Parse extruder and bed temps from a comment string.
Patterns to match:
- "extrude 190-230" or "extruder 190-230" or "nozzle 190-230"
- "bed 50-70"
- "190-230 / 50-70" (extruder / bed)
- "190-230C" or "190-230°C"
- Single values like "extrude 210" → min=max=210
"""
result = {
"extruder_temp_min": None,
"extruder_temp_max": None,
"bed_temp_min": None,
"bed_temp_max": None,
}
if not comment:
return result
comment_lower = comment.lower()
# Pattern for temperature ranges: 190-230 or 190 - 230
temp_range_pattern = r"(\d{2,3})\s*[-]\s*(\d{2,3})"
# Pattern for single temp: 210
single_temp_pattern = r"(\d{2,3})"
# Try to find extruder temp (usually higher values, 150-300 range)
extruder_keywords = ["extrude", "extruder", "nozzle", "hotend", "hot end", "printing temp"]
for keyword in extruder_keywords:
if keyword in comment_lower:
# Find temp after keyword
idx = comment_lower.index(keyword)
search_area = comment[idx:idx+50] # Look in next 50 chars
# Try range first
match = re.search(temp_range_pattern, search_area)
if match:
result["extruder_temp_min"] = int(match.group(1))
result["extruder_temp_max"] = int(match.group(2))
break
# Try single value
match = re.search(single_temp_pattern, search_area[len(keyword):])
if match:
val = int(match.group(1))
if 150 <= val <= 350: # Reasonable extruder temp range
result["extruder_temp_min"] = val
result["extruder_temp_max"] = val
break
# Try to find bed temp (usually lower values, 0-120 range)
bed_keywords = ["bed", "plate", "build plate"]
for keyword in bed_keywords:
if keyword in comment_lower:
# Find temp after keyword
idx = comment_lower.index(keyword)
search_area = comment[idx:idx+40] # Look in next 40 chars
# Try range first
match = re.search(temp_range_pattern, search_area)
if match:
result["bed_temp_min"] = int(match.group(1))
result["bed_temp_max"] = int(match.group(2))
break
# Try single value
match = re.search(single_temp_pattern, search_area[len(keyword):])
if match:
val = int(match.group(1))
if 0 <= val <= 150: # Reasonable bed temp range
result["bed_temp_min"] = val
result["bed_temp_max"] = val
break
# If no keywords found, try to find generic temp pattern
# Format: "190-230 / 50-70" (extruder / bed separated by /)
if result["extruder_temp_min"] is None and "/" in comment:
parts = comment.split("/")
if len(parts) >= 2:
# First part might be extruder
match = re.search(temp_range_pattern, parts[0])
if match:
t1, t2 = int(match.group(1)), int(match.group(2))
if t1 > 100: # Likely extruder temp
result["extruder_temp_min"] = t1
result["extruder_temp_max"] = t2
# Second part might be bed
match = re.search(temp_range_pattern, parts[1])
if match:
t1, t2 = int(match.group(1)), int(match.group(2))
if t1 < 150: # Likely bed temp
result["bed_temp_min"] = t1
result["bed_temp_max"] = t2
return result
async def main(db_path: str = None, dry_run: bool = True):
"""Main function to parse temps from comments."""
# Determine database URL
if db_path:
db_url = f"sqlite+aiosqlite:///{db_path}"
else:
# Default to local spoolman database
default_path = Path.home() / ".local/share/spoolman/spoolman.db"
if not default_path.exists():
# Try the project directory
default_path = Path(__file__).parent.parent / "spoolman.db"
db_url = f"sqlite+aiosqlite:///{default_path}"
print(f"Connecting to: {db_url}")
engine = create_async_engine(db_url, echo=False)
async_session = sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)
async with async_session() as session:
# Get all filaments with comments
result = await session.execute(select(Filament))
filaments = result.scalars().all()
print(f"\nFound {len(filaments)} filaments total")
updated_count = 0
for fil in filaments:
if not fil.comment:
continue
parsed = parse_temps_from_comment(fil.comment)
# Check if we found any temps
has_extruder = parsed["extruder_temp_min"] is not None
has_bed = parsed["bed_temp_min"] is not None
if has_extruder or has_bed:
print(f"\n--- Filament #{fil.id}: {fil.name or 'unnamed'} ---")
print(f" Comment: {fil.comment}")
print(f" Parsed extruder: {parsed['extruder_temp_min']}-{parsed['extruder_temp_max']} °C" if has_extruder else " No extruder temp found")
print(f" Parsed bed: {parsed['bed_temp_min']}-{parsed['bed_temp_max']} °C" if has_bed else " No bed temp found")
print(f" Current extruder: {fil.settings_extruder_temp_min}-{fil.settings_extruder_temp_max}" if fil.settings_extruder_temp_min else " Current extruder: Not set")
print(f" Current bed: {fil.settings_bed_temp_min}-{fil.settings_bed_temp_max}" if fil.settings_bed_temp_min else " Current bed: Not set")
if not dry_run:
# Only update if not already set
if has_extruder and not fil.settings_extruder_temp_min:
fil.settings_extruder_temp_min = parsed["extruder_temp_min"]
fil.settings_extruder_temp_max = parsed["extruder_temp_max"]
if has_bed and not fil.settings_bed_temp_min:
fil.settings_bed_temp_min = parsed["bed_temp_min"]
fil.settings_bed_temp_max = parsed["bed_temp_max"]
# Clear comment after parsing
fil.comment = None
print(" → Updated and cleared comment")
updated_count += 1
if not dry_run and updated_count > 0:
await session.commit()
print(f"\n✓ Committed changes to {updated_count} filaments")
elif dry_run:
print(f"\n[DRY RUN] Would update {updated_count} filaments")
print("Run with --apply to actually make changes")
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser(description="Parse temps from filament comments")
parser.add_argument("--db", help="Path to SQLite database file")
parser.add_argument("--apply", action="store_true", help="Actually apply changes (default is dry run)")
args = parser.parse_args()
asyncio.run(main(db_path=args.db, dry_run=not args.apply))