commit f395d6713618d4714af36807c075900e79838a10 Author: consultoria-as Date: Mon Jan 19 08:45:03 2026 +0000 Initial commit: Sistema Autoparts DB - Base de datos SQLite con información de vehículos - Dashboard web con Flask y Bootstrap - Scripts de web scraping para RockAuto - Interfaz CLI para consultas - Documentación completa del proyecto Incluye: - 12 marcas de vehículos - 10,923 modelos - 10,919 especificaciones de motores - 12,075 combinaciones modelo-año-motor diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d773489 --- /dev/null +++ b/.gitignore @@ -0,0 +1,53 @@ +# Python +__pycache__/ +*.py[cod] +*$py.class +*.so +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg + +# Virtual environments +venv/ +ENV/ +env/ +.venv/ + +# IDE +.vscode/ +.idea/ +*.swp +*.swo +*~ + +# OS +.DS_Store +Thumbs.db + +# Logs +*.log + +# Local configuration +.claude/ +*.local.json + +# Temporary files +*.tmp +*.temp + +# Backup files +*.bak +*.backup diff --git a/DASHBOARD_GUIDE.md b/DASHBOARD_GUIDE.md new file mode 100644 index 0000000..c59c665 --- /dev/null +++ b/DASHBOARD_GUIDE.md @@ -0,0 +1,103 @@ +# Vehicle Database Dashboard - Complete Solution + +## Overview + +I have created a complete web-based dashboard for searching and filtering your vehicle database with the following components: + +1. **Web Dashboard Interface** - A responsive, modern UI built with HTML, CSS, and JavaScript +2. **Flask Backend** - API server that connects to your SQLite database +3. **Filtering System** - Search by brand, model, year, and engine +4. **Data Visualization** - Display vehicle details in an attractive card layout + +## Features + +- **Four-way Filtering**: Filter vehicles by brand, model, year, and engine type +- **Responsive Design**: Works on desktop, tablet, and mobile devices +- **Real-time Updates**: Filters update immediately when selections change +- **Detailed Information**: Shows power, displacement, cylinders, fuel type, trim, drivetrain, and transmission +- **Modern UI**: Bootstrap-based interface with cards, badges, and icons + +## Architecture + +### Frontend +- `index.html`: Main dashboard page with Bootstrap styling +- `dashboard.js`: JavaScript for UI interactions and API calls + +### Backend +- `server.py`: Flask application serving the dashboard and API +- API endpoints for brands, models, years, engines, and vehicles + +## How to Use + +### Starting the Dashboard +1. Navigate to the dashboard directory: + ```bash + cd /home/Autopartes/dashboard + ``` + +2. Start the server: + ```bash + python3 server.py + ``` + +3. Open your web browser and go to: `http://localhost:5000` + +### Using the Dashboard +1. On the left panel, use the dropdowns to select filters: + - Brand: Select a vehicle manufacturer + - Model: Select a model (updates based on selected brand) + - Year: Select a production year + - Engine: Select an engine type + +2. Click "Search Vehicles" to apply filters + +3. Results appear in the right panel with detailed information + +4. Use "Reset Filters" to clear all selections + +## API Endpoints + +The dashboard uses these API endpoints: +- `GET /api/brands` - Get all vehicle brands +- `GET /api/models?brand=[brand]` - Get models for a specific brand +- `GET /api/years` - Get all years +- `GET /api/engines` - Get all engines +- `GET /api/vehicles?[filters]` - Search vehicles with optional filters + +## Files Created + +``` +dashboard/ +├── index.html # Main dashboard page +├── dashboard.js # Frontend JavaScript +├── server.py # Flask backend +├── requirements.txt # Python dependencies +├── start_dashboard.sh # Startup script +├── README.md # Documentation +└── static/ # Static files directory (created by Flask) +``` + +## Customization + +You can customize the dashboard by: +- Modifying the CSS styles in index.html +- Adding more filters in the JavaScript +- Changing the layout in index.html +- Adding more vehicle details in the display + +## Troubleshooting + +- If the server won't start, ensure Flask is installed: `sudo apt-get install python3-flask` +- If filters don't populate, check that your vehicle database has data +- If the page doesn't load, verify that the database file exists at `../vehicle_database/vehicle_database.db` +- Check the browser console for JavaScript errors +- Check the terminal for server errors + +## Next Steps + +1. Start the dashboard server +2. Browse your vehicle data using the intuitive interface +3. Add more data to your database using the manual input tools +4. Customize the dashboard to meet your specific needs + +The dashboard is now ready to use and provides a powerful interface for exploring your vehicle database! \ No newline at end of file diff --git a/FINAL_SUMMARY.md b/FINAL_SUMMARY.md new file mode 100644 index 0000000..c5ef080 --- /dev/null +++ b/FINAL_SUMMARY.md @@ -0,0 +1,126 @@ +# Vehicle Database with RockAuto Data Integration + +## Project Overview + +This project combines two components: +1. A comprehensive vehicle database system +2. A data extraction system for RockAuto.com vehicle information + +Due to anti-bot measures on RockAuto.com, a manual extraction approach is recommended for collecting vehicle data. + +## System Components + +### 1. Vehicle Database +- SQLite database with normalized schema +- Tables for brands, models, years, engines, and their relationships +- Python API for managing the database +- Interactive query interface + +### 2. Data Extraction Tools +- Automated scraper (for sites without anti-bot measures) +- Manual extraction guide for RockAuto.com +- Data import functionality + +## Database Schema + +The database consists of five main tables: + +- **brands**: Vehicle manufacturers (Toyota, Ford, etc.) +- **models**: Vehicle models (Camry, F-150, etc.) +- **engines**: Engine specifications (2JZ-GTE, EcoBoost, etc.) +- **years**: Calendar years for vehicle production +- **model_year_engine**: Junction table linking all entities with trim levels and specifications + +## Using the System + +### Initial Setup +```bash +cd vehicle_database +./setup.sh +``` + +### Querying the Database +```bash +python3 scripts/query_interface.py +``` + +### Adding More Data Manually +```python +from ../vehicle_scraper/manual_input import ManualDataInput +input_tool = ManualDataInput() + +# Add a single vehicle +input_tool.add_vehicle_data("Toyota", "Corolla", 2021, "1.8L 4-Cylinder") + +# Add multiple vehicles +vehicles = [ + {"make": "Nissan", "model": "Altima", "year": 2020, "engine": "2.5L 4-Cylinder"}, + {"make": "Hyundai", "model": "Elantra", "year": 2019, "engine": "2.0L 4-Cylinder"} +] +input_tool.add_multiple_vehicles(vehicles) +``` + +## Manual Data Extraction from RockAuto.com + +Since RockAuto has anti-bot measures, follow this process: + +1. Open your web browser and go to: https://www.rockauto.com +2. Click on the "Catalog" link in the navigation menu +3. You will see a list of vehicle manufacturers (makes) +4. For each manufacturer: + - Click on the manufacturer name + - You'll see a page with vehicle models organized by year + - Note down the models and years you see +5. To find engine information: + - Click on a specific model/year combination + - You'll see parts categories for that vehicle + - Look for "Engine" or "Engine Mechanical" category + - Note down the engine type/specifications +6. Use the ManualDataInput class to add the collected data to your database + +## File Structure +``` +vehicle_database/ # Main database system +├── sql/ +│ └── schema.sql # Database schema +├── scripts/ +│ ├── database_manager.py # Main database manager +│ ├── query_interface.py # Interactive query interface +│ └── csv_importer.py # CSV import functionality +├── data/ # Sample CSV data files +├── vehicle_database.db # SQLite database file +├── setup.sh # Setup script +├── README.md # Project documentation +└── GETTING_STARTED.md # Getting started guide + +vehicle_scraper/ # Data extraction tools +├── rockauto_scraper.py # Automated scraper (for other sites) +├── rockauto_scraper_enhanced.py # Enhanced scraper +├── manual_input.py # Manual input tool +├── manual_input_simple.py # Simplified manual input +└── requirements.txt # Python dependencies +``` + +## Extending the Database + +To add more vehicle data: +1. Collect data manually from RockAuto.com using the provided guide +2. Use the ManualDataInput class to add data to the database +3. Or prepare CSV files in the required format and use the CSV importer + +## Future Enhancements + +- Web scraping capabilities for other automotive parts sites +- Export functionality to share data +- Advanced search and filtering options +- Data validation and cleaning tools + +## Troubleshooting + +If you encounter issues: +1. Check that Python 3.x is installed +2. Ensure all required packages are installed (`pip3 install -r requirements.txt`) +3. Verify database file permissions +4. Check that the schema matches the expected structure + +The system is now ready to use. You can start by exploring the existing data through the query interface and then add more data as needed. \ No newline at end of file diff --git a/QUICK_START.sh b/QUICK_START.sh new file mode 100755 index 0000000..a8420fb --- /dev/null +++ b/QUICK_START.sh @@ -0,0 +1,53 @@ +#!/bin/bash +# Quick Start Script for Vehicle Database with RockAuto Integration + +echo "===========================================" +echo "Vehicle Database & RockAuto Data Integration" +echo "===========================================" +echo "" +echo "Welcome! This system allows you to create a comprehensive vehicle database" +echo "with information from RockAuto.com (via manual extraction)." +echo "" +echo "STEP 1: Explore the existing database" +echo "-------------------------------------" +echo "Start the query interface to see existing data:" +echo " cd vehicle_database" +echo " python3 scripts/query_interface.py" +echo "" +echo "STEP 2: Add more data from RockAuto.com" +echo "---------------------------------------" +echo "Since RockAuto.com has anti-bot measures, you'll need to manually" +echo "extract data. Here's how:" +echo "" +echo "1. Visit https://www.rockauto.com/catalog/" +echo "2. Browse through manufacturers, models, years, and engines" +echo "3. Note down the information you find" +echo "4. Add it to your database using Python:" +echo "" +echo " from vehicle_scraper.manual_input_simple import ManualDataInput" +echo " input_tool = ManualDataInput(db_path='vehicle_database/vehicle_database.db')" +echo " input_tool.add_vehicle_data('Make', 'Model', Year, 'Engine')" +echo "" +echo "STEP 3: Database structure" +echo "--------------------------" +echo "The database contains these tables:" +echo "- brands: Vehicle manufacturers" +echo "- models: Vehicle models" +echo "- years: Production years" +echo "- engines: Engine specifications" +echo "- model_year_engine: Links between all the above" +echo "" +echo "STEP 4: Sample queries" +echo "----------------------" +echo "Try these queries in the interface:" +echo "- Search for all Toyotas: Brand = 'Toyota'" +echo "- Find 2020 models: Year = 2020" +echo "- Look for turbo engines: Engine = 'Turbo'" +echo "" +echo "Your database is located at: vehicle_database/vehicle_database.db" +echo "" +echo "To see all available scripts:" +echo " ls -la vehicle_database/scripts/" +echo "" +echo "Ready to start building your vehicle database!" +echo "==============================================" \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..e8759a7 --- /dev/null +++ b/README.md @@ -0,0 +1,306 @@ +# Autoparts DB + +Sistema completo de gestión de base de datos de vehículos y autopartes con dashboard web, herramientas de web scraping y múltiples interfaces de consulta. + +## Descripción + +**Autoparts DB** es una solución integral para la gestión de información de vehículos que incluye: + +- Base de datos SQLite normalizada con información de marcas, modelos, motores y años +- Dashboard web moderno y responsivo para consultar y explorar datos +- Herramientas de web scraping para recopilar datos de RockAuto.com +- Interfaces de línea de comandos (CLI) y programática +- Scripts de utilidad para gestión y mantenimiento de datos + +## Estadísticas de la Base de Datos + +| Elemento | Cantidad | +|----------|----------| +| Marcas | 12 | +| Modelos | 10,923 | +| Motores | 10,919 | +| Combinaciones modelo-año-motor | 12,075 | + +## Tecnologías Utilizadas + +### Backend +- **Python 3** - Lenguaje principal +- **SQLite 3** - Base de datos +- **Flask 2.3.3** - Framework web +- **BeautifulSoup4** - Web scraping +- **requests** - HTTP client +- **lxml** - Parser XML/HTML + +### Frontend +- **HTML5** - Estructura +- **Bootstrap 5.3.0** - Framework CSS +- **JavaScript (ES6+)** - Lógica cliente +- **Font Awesome 6.0.0** - Iconos + +## Estructura del Proyecto + +``` +Autopartes/ +├── vehicle_database/ # Sistema principal de base de datos +│ ├── sql/ +│ │ └── schema.sql # Esquema de la base de datos +│ ├── scripts/ +│ │ ├── database_manager.py # Gestión de la BD +│ │ ├── query_interface.py # Interfaz CLI +│ │ └── csv_importer.py # Importador CSV +│ ├── data/ +│ │ ├── brands.csv # Datos de marcas +│ │ ├── engines.csv # Datos de motores +│ │ └── models.csv # Datos de modelos +│ ├── vehicle_database.db # Base de datos SQLite +│ └── setup.sh # Script de inicialización +│ +├── dashboard/ # Interfaz web +│ ├── server.py # Backend Flask +│ ├── index.html # Frontend HTML +│ ├── dashboard.js # Lógica JavaScript +│ └── start_dashboard.sh # Script de inicio +│ +├── vehicle_scraper/ # Herramientas de web scraping +│ ├── rockauto_scraper.py # Scraper RockAuto +│ ├── rockauto_scraper_v2.py # Scraper mejorado +│ ├── scrape_toyota.py # Scraper Toyota +│ ├── scrape_nissan_ford_chevrolet.py +│ └── manual_input.py # Ingreso manual +│ +├── add_*.py # Scripts para agregar datos +├── remove_*.py # Scripts de limpieza +└── QUICK_START.sh # Guía rápida de inicio +``` + +## Instalación + +### Requisitos Previos + +- Python 3.8 o superior +- pip (gestor de paquetes de Python) + +### Pasos de Instalación + +1. **Clonar el repositorio** + ```bash + git clone https://git.consultoria-as.com/[usuario]/Autoparts-DB.git + cd Autoparts-DB + ``` + +2. **Instalar dependencias** + ```bash + pip install flask requests beautifulsoup4 lxml + ``` + +3. **Inicializar la base de datos (opcional - ya incluye datos)** + ```bash + cd vehicle_database + ./setup.sh + ``` + +## Uso + +### Iniciar el Dashboard Web + +```bash +cd dashboard +python3 server.py +``` + +El dashboard estará disponible en: `http://localhost:5000` + +### Usar la Interfaz CLI + +```bash +cd vehicle_database/scripts +python3 query_interface.py +``` + +### Ejecutar Web Scraping + +```bash +cd vehicle_scraper +python3 rockauto_scraper_v2.py +``` + +### Agregar Datos Manualmente + +```bash +cd vehicle_scraper +python3 manual_input.py +``` + +## API REST + +El dashboard expone los siguientes endpoints: + +| Endpoint | Método | Descripción | +|----------|--------|-------------| +| `/api/brands` | GET | Obtiene todas las marcas | +| `/api/models?brand=X` | GET | Obtiene modelos por marca | +| `/api/years` | GET | Obtiene años disponibles | +| `/api/engines` | GET | Obtiene motores disponibles | +| `/api/vehicles` | GET | Búsqueda con filtros | + +### Ejemplo de Uso + +```bash +# Obtener todas las marcas +curl http://localhost:5000/api/brands + +# Buscar vehículos por marca y año +curl "http://localhost:5000/api/vehicles?brand=Toyota&year=2020" +``` + +## Esquema de Base de Datos + +### Tablas + +#### brands +| Campo | Tipo | Descripción | +|-------|------|-------------| +| id | INTEGER | Clave primaria | +| name | TEXT | Nombre de la marca | +| country | TEXT | País de origen | +| founded_year | INTEGER | Año de fundación | + +#### models +| Campo | Tipo | Descripción | +|-------|------|-------------| +| id | INTEGER | Clave primaria | +| brand_id | INTEGER | FK a brands | +| name | TEXT | Nombre del modelo | +| body_type | TEXT | Tipo de carrocería | +| generation | TEXT | Generación | +| production_start_year | INTEGER | Año inicio producción | +| production_end_year | INTEGER | Año fin producción | + +#### engines +| Campo | Tipo | Descripción | +|-------|------|-------------| +| id | INTEGER | Clave primaria | +| name | TEXT | Nombre del motor | +| displacement_cc | INTEGER | Cilindrada en cc | +| cylinders | INTEGER | Número de cilindros | +| fuel_type | TEXT | Tipo de combustible | +| power_hp | INTEGER | Potencia en HP | +| torque_nm | INTEGER | Torque en Nm | +| engine_code | TEXT | Código del motor | + +#### years +| Campo | Tipo | Descripción | +|-------|------|-------------| +| id | INTEGER | Clave primaria | +| year | INTEGER | Año | + +#### model_year_engine +| Campo | Tipo | Descripción | +|-------|------|-------------| +| id | INTEGER | Clave primaria | +| model_id | INTEGER | FK a models | +| year_id | INTEGER | FK a years | +| engine_id | INTEGER | FK a engines | +| trim_level | TEXT | Nivel de equipamiento | +| drivetrain | TEXT | Tracción | +| transmission | TEXT | Transmisión | + +### Diagrama de Relaciones + +``` +brands ──┐ + │ + ├──< models ──┐ + │ │ +years ───┼─────────────┼──< model_year_engine + │ │ +engines ─┴─────────────┘ +``` + +## Scripts Disponibles + +### Scripts de Datos + +| Script | Descripción | +|--------|-------------| +| `add_toyota_data.py` | Agrega datos de Toyota | +| `add_honda_data.py` | Agrega datos de Honda | +| `add_nissan_data.py` | Agrega datos de Nissan | +| `add_ford_data.py` | Agrega datos de Ford | +| `add_chevrolet_data.py` | Agrega datos de Chevrolet | +| `add_audi_data.py` | Agrega datos de Audi | +| `add_acura_data.py` | Agrega datos de Acura | +| ... | Y más marcas | + +### Scripts de Mantenimiento + +| Script | Descripción | +|--------|-------------| +| `remove_brands_and_cleanup.py` | Limpia marcas innecesarias | +| `check_and_remove_brands.py` | Verifica y elimina marcas | + +## Funcionalidades del Dashboard + +### Panel de Filtros +- Selección de marca +- Selección de modelo (dinámico según marca) +- Filtro por año +- Filtro por motor + +### Panel de Resultados +- Visualización en tarjetas +- Información detallada del vehículo +- Especificaciones del motor +- Datos de transmisión y tracción + +### Características +- Diseño responsivo +- Actualización en tiempo real +- Animaciones y transiciones suaves +- Soporte para múltiples idiomas + +## Arquitectura del Sistema + +``` +┌─────────────────┐ ┌──────────────────┐ +│ RockAuto.com │────>│ Web Scraper │ +└─────────────────┘ └────────┬─────────┘ + │ + v +┌─────────────────┐ ┌──────────────────┐ +│ Manual Input │────>│ SQLite Database │ +└─────────────────┘ └────────┬─────────┘ + │ + ┌───────────────────────┼───────────────────────┐ + │ │ │ + v v v +┌─────────────────┐ ┌──────────────────┐ ┌──────────────────┐ +│ Flask API │ │ CLI Interface │ │ CSV Importer │ +└────────┬────────┘ └──────────────────┘ └──────────────────┘ + │ + v +┌─────────────────┐ +│ Web Dashboard │ +│ (Browser) │ +└─────────────────┘ +``` + +## Contribuir + +1. Fork el repositorio +2. Crea una rama para tu feature (`git checkout -b feature/nueva-funcionalidad`) +3. Commit tus cambios (`git commit -am 'Agrega nueva funcionalidad'`) +4. Push a la rama (`git push origin feature/nueva-funcionalidad`) +5. Crea un Pull Request + +## Licencia + +Este proyecto es de uso interno. + +## Contacto + +Para más información, contactar al equipo de desarrollo. + +--- + +**Autoparts DB** - Sistema de Gestión de Base de Datos de Vehículos diff --git a/add_abarth_data.py b/add_abarth_data.py new file mode 100644 index 0000000..11a9624 --- /dev/null +++ b/add_abarth_data.py @@ -0,0 +1,177 @@ +#!/usr/bin/env python3 +""" +Script para agregar datos de Abarth a la base de datos de vehículos +""" + +import sqlite3 +import os + +def add_abarth_data(): + # Verificar que la base de datos exista + db_path = "vehicle_database/vehicle_database.db" + if not os.path.exists(db_path): + print(f"Error: Base de datos no encontrada en {db_path}") + return + + conn = sqlite3.connect(db_path) + cursor = conn.cursor() + + print("Agregando datos de Abarth a la base de datos...") + + try: + # Insertar la marca Abarth + cursor.execute("INSERT OR IGNORE INTO brands (name) VALUES (?)", ("Abarth",)) + cursor.execute("SELECT id FROM brands WHERE name = ?", ("Abarth",)) + brand_id = cursor.fetchone()[0] + print(f"Marca Abarth tiene ID: {brand_id}") + + # Insertar modelos de Abarth + models = [ + '1000', '1300', '2000', 'SIMCA', '850', '1150', '1600', '750', '204', '205' + ] + + for model in models: + cursor.execute( + "INSERT OR IGNORE INTO models (brand_id, name) VALUES (?, ?)", + (brand_id, model) + ) + print(f"Agregados {len(models)} modelos de Abarth") + + # Datos de años y motores para cada modelo + abarth_data = [ + # 1000 + ('1000', 1969, '982cc L4'), + ('1000', 1968, '982cc L4'), + ('1000', 1967, '982cc L4'), + ('1000', 1966, '982cc L4'), + ('1000', 1965, '982cc L4'), + ('1000', 1964, '982cc L4'), + ('1000', 1963, '982cc L4'), + ('1000', 1962, '982cc L4'), + + # 1300 + ('1300', 1969, '1.3L L4'), + ('1300', 1968, '1.3L L4'), + ('1300', 1967, '1.3L L4'), + ('1300', 1966, '1.3L L4'), + + # 2000 + ('2000', 1969, '1.9L L4'), + ('2000', 1968, '1.9L L4'), + ('2000', 1967, '1.9L L4'), + ('2000', 1966, '1.9L L4'), + + # SIMCA + ('SIMCA', 1969, '1.3L L4'), + ('SIMCA', 1969, '2.0L L4'), + ('SIMCA', 1968, '1.3L L4'), + ('SIMCA', 1968, '2.0L L4'), + ('SIMCA', 1967, '1.3L L4'), + ('SIMCA', 1967, '2.0L L4'), + ('SIMCA', 1966, '1.3L L4'), + ('SIMCA', 1966, '2.0L L4'), + ('SIMCA', 1965, '1.3L L4'), + ('SIMCA', 1965, '2.0L L4'), + ('SIMCA', 1964, '1.3L L4'), + ('SIMCA', 1964, '2.0L L4'), + ('SIMCA', 1963, '1.3L L4'), + ('SIMCA', 1963, '2.0L L4'), + ('SIMCA', 1962, '1.3L L4'), + ('SIMCA', 1962, '2.0L L4'), + + # 850 + ('850', 1966, '847cc L4'), + ('850', 1965, '847cc L4'), + ('850', 1964, '847cc L4'), + ('850', 1963, '847cc L4'), + ('850', 1962, '847cc L4'), + ('850', 1961, '833cc L4'), + ('850', 1960, '833cc L4'), + + # 1150 + ('1150', 1966, '1.1L L4'), + + # 1600 + ('1600', 1966, '1.6L L4'), + ('1600', 1965, '1.6L L4'), + ('1600', 1964, '1.6L L4'), + ('1600', 1963, '1.6L L4'), + ('1600', 1962, '1.6L L4'), + ('1600', 1961, '1.6L L4'), + ('1600', 1960, '1.6L L4'), + + # 750 + ('750', 1961, '747cc L4'), + ('750', 1960, '747cc L4'), + ('750', 1959, '747cc L4'), + ('750', 1958, '747cc L4'), + ('750', 1957, '747cc L4'), + ('750', 1956, '747cc L4'), + + # 204 + ('204', 1955, '1.1L L4'), + ('204', 1954, '1.1L L4'), + ('204', 1953, '1.1L L4'), + ('204', 1952, '1.1L L4'), + ('204', 1951, '1.1L L4'), + ('204', 1950, '1.1L L4'), + ('204', 1949, '1.1L L4'), + + # 205 + ('205', 1955, '1.1L L4'), + ('205', 1954, '1.1L L4'), + ('205', 1953, '1.1L L4'), + ('205', 1952, '1.1L L4'), + ('205', 1951, '1.1L L4'), + ('205', 1950, '1.1L L4'), + ('205', 1949, '1.1L L4'), + ] + + # Insertar años + years = list(set([data[1] for data in abarth_data])) # Obtener años únicos + for year in years: + cursor.execute("INSERT OR IGNORE INTO years (year) VALUES (?)", (year,)) + print(f"Agregados {len(years)} años") + + # Insertar motores + engines = list(set([data[2] for data in abarth_data])) # Obtener motores únicos + for engine in engines: + cursor.execute("INSERT OR IGNORE INTO engines (name) VALUES (?)", (engine,)) + print(f"Agregados {len(engines)} motores") + + # Crear combinaciones modelo-año-motor + for model_name, year, engine_name in abarth_data: + # Obtener IDs + cursor.execute("SELECT id FROM models WHERE brand_id = ? AND name = ?", (brand_id, model_name)) + model_id = cursor.fetchone() + if model_id: + model_id = model_id[0] + else: + print(f"Advertencia: Modelo {model_name} no encontrado para la marca Abarth") + continue + + cursor.execute("SELECT id FROM years WHERE year = ?", (year,)) + year_id = cursor.fetchone()[0] + + cursor.execute("SELECT id FROM engines WHERE name = ?", (engine_name,)) + engine_id = cursor.fetchone()[0] + + # Insertar la combinación modelo-año-motor + cursor.execute( + """INSERT OR IGNORE INTO model_year_engine + (model_id, year_id, engine_id) VALUES (?, ?, ?)""", + (model_id, year_id, engine_id) + ) + + conn.commit() + print(f"Se agregaron {len(abarth_data)} combinaciones modelo-año-motor para Abarth") + print("Datos de Abarth agregados exitosamente a la base de datos!") + + except Exception as e: + print(f"Error al agregar datos de Abarth: {e}") + conn.rollback() + finally: + conn.close() + +if __name__ == "__main__": + add_abarth_data() \ No newline at end of file diff --git a/add_ac_data.py b/add_ac_data.py new file mode 100644 index 0000000..a1c9b27 --- /dev/null +++ b/add_ac_data.py @@ -0,0 +1,178 @@ +#!/usr/bin/env python3 +""" +Script para agregar datos de AC a la base de datos de vehículos +""" + +import sqlite3 +import os + +def add_ac_data(): + # Verificar que la base de datos exista + db_path = "vehicle_database/vehicle_database.db" + if not os.path.exists(db_path): + print(f"Error: Base de datos no encontrada en {db_path}") + return + + conn = sqlite3.connect(db_path) + cursor = conn.cursor() + + print("Agregando datos de AC a la base de datos...") + + try: + # Insertar la marca AC + cursor.execute("INSERT OR IGNORE INTO brands (name) VALUES (?)", ("AC",)) + cursor.execute("SELECT id FROM brands WHERE name = ?", ("AC",)) + brand_id = cursor.fetchone()[0] + print(f"Marca AC tiene ID: {brand_id}") + + # Insertar modelos de AC + models = [ + '428', 'ACE', 'ACECA', 'GREYHOUND', 'TWO-LITRE' + ] + + for model in models: + cursor.execute( + "INSERT OR IGNORE INTO models (brand_id, name) VALUES (?, ?)", + (brand_id, model) + ) + print(f"Agregados {len(models)} modelos de AC") + + # Datos de años y motores para cada modelo + ac_data = [ + # 428 + ('428', 1973, '7.0L V8'), + ('428', 1972, '7.0L V8'), + ('428', 1971, '7.0L V8'), + ('428', 1970, '7.0L V8'), + ('428', 1969, '7.0L V8'), + ('428', 1968, '7.0L V8'), + ('428', 1967, '7.0L V8'), + + # ACE + ('ACE', 1963, '2.0L L6'), + ('ACE', 1963, '2.6L L6'), + ('ACE', 1962, '2.0L L6'), + ('ACE', 1962, '2.6L L6'), + ('ACE', 1961, '2.0L L6'), + ('ACE', 1961, '2.6L L6'), + ('ACE', 1960, '2.0L L6'), + ('ACE', 1960, '2.6L L6'), + ('ACE', 1959, '2.0L L6'), + ('ACE', 1958, '2.0L L6'), + ('ACE', 1957, '2.0L L6'), + ('ACE', 1956, '2.0L L6'), + ('ACE', 1955, '2.0L L6'), + ('ACE', 1954, '2.0L L6'), + ('ACE', 1953, '2.0L L6'), + + # ACECA + ('ACECA', 1963, '2.0L L6'), + ('ACECA', 1963, '2.6L L6'), + ('ACECA', 1962, '2.0L L6'), + ('ACECA', 1962, '2.6L L6'), + ('ACECA', 1961, '2.0L L6'), + ('ACECA', 1961, '2.6L L6'), + ('ACECA', 1960, '2.0L L6'), + ('ACECA', 1960, '2.6L L6'), + ('ACECA', 1958, '2.0L L6'), + ('ACECA', 1957, '2.0L L6'), + ('ACECA', 1956, '2.0L L6'), + ('ACECA', 1955, '2.0L L6'), + + # GREYHOUND + ('GREYHOUND', 1963, '2.0L L6'), + ('GREYHOUND', 1962, '2.0L L6'), + ('GREYHOUND', 1961, '2.0L L6'), + ('GREYHOUND', 1960, '2.0L L6'), + + # TWO-LITRE + ('TWO-LITRE', 1958, '2.0L L6'), + ('TWO-LITRE', 1957, '2.0L L6'), + ('TWO-LITRE', 1956, '2.0L L6'), + ('TWO-LITRE', 1955, '2.0L L6'), + ('TWO-LITRE', 1954, '2.0L L6'), + ('TWO-LITRE', 1953, '2.0L L6'), + ('TWO-LITRE', 1952, '2.0L L6'), + ('TWO-LITRE', 1951, '2.0L L6'), + ('TWO-LITRE', 1950, '2.0L L6'), + ('TWO-LITRE', 1949, '2.0L 122cid L6'), + ('TWO-LITRE', 1948, '2.0L 122cid L6'), + ('TWO-LITRE', 1947, '2.0L 122cid L6'), + ] + + # Insertar años + years = list(set([data[1] for data in ac_data])) # Obtener años únicos + for year in years: + cursor.execute("INSERT OR IGNORE INTO years (year) VALUES (?)", (year,)) + print(f"Agregados {len(years)} años") + + # Insertar motores + engines = list(set([data[2] for data in ac_data])) # Obtener motores únicos + for engine in engines: + cursor.execute("INSERT OR IGNORE INTO engines (name) VALUES (?)", (engine,)) + print(f"Agregados {len(engines)} motores") + + # Crear combinaciones modelo-año-motor + for model_name, year, engine_name in ac_data: + # Obtener IDs + cursor.execute("SELECT id FROM models WHERE brand_id = ? AND name = ?", (brand_id, model_name)) + model_result = cursor.fetchone() + if model_result: + model_id = model_result[0] + else: + print(f"Advertencia: Modelo {model_name} no encontrado para la marca AC") + continue + + cursor.execute("SELECT id FROM years WHERE year = ?", (year,)) + year_result = cursor.fetchone() + if year_result: + year_id = year_result[0] + else: + print(f"Advertencia: Año {year} no encontrado") + continue + + cursor.execute("SELECT id FROM engines WHERE name = ?", (engine_name,)) + engine_result = cursor.fetchone() + if engine_result: + engine_id = engine_result[0] + else: + print(f"Advertencia: Motor {engine_name} no encontrado") + continue + + # Insertar la combinación modelo-año-motor + cursor.execute( + """INSERT OR REPLACE INTO model_year_engine + (model_id, year_id, engine_id) VALUES (?, ?, ?)""", + (model_id, year_id, engine_id) + ) + + conn.commit() + print(f"Se agregaron {len(ac_data)} combinaciones modelo-año-motor para AC") + print("Datos de AC agregados exitosamente a la base de datos!") + + # Mostrar algunos resultados + print("\nAlgunos vehículos AC agregados:") + cursor.execute(""" + SELECT b.name, m.name, y.year, e.name + FROM model_year_engine mye + JOIN models m ON mye.model_id = m.id + JOIN brands b ON m.brand_id = b.id + JOIN years y ON mye.year_id = y.id + JOIN engines e ON mye.engine_id = e.id + WHERE b.name = 'AC' + ORDER BY m.name, y.year DESC + LIMIT 10 + """) + + results = cursor.fetchall() + for brand, model, year, engine in results: + print(f" {year} {brand} {model} - {engine}") + + except Exception as e: + print(f"Error al agregar datos de AC: {e}") + conn.rollback() + finally: + conn.close() + +if __name__ == "__main__": + add_ac_data() \ No newline at end of file diff --git a/add_acura_additional_data.py b/add_acura_additional_data.py new file mode 100644 index 0000000..db7f21b --- /dev/null +++ b/add_acura_additional_data.py @@ -0,0 +1,200 @@ +#!/usr/bin/env python3 +""" +Script para agregar datos adicionales de Acura a la base de datos de vehículos +""" + +import sqlite3 +import os + +def add_acura_additional_data(): + # Verificar que la base de datos exista + db_path = "vehicle_database/vehicle_database.db" + if not os.path.exists(db_path): + print(f"Error: Base de datos no encontrada en {db_path}") + return + + conn = sqlite3.connect(db_path) + cursor = conn.cursor() + + print("Agregando datos adicionales de Acura a la base de datos...") + + try: + # Asegurar que la marca Acura exista + cursor.execute("INSERT OR IGNORE INTO brands (name) VALUES (?)", ("ACURA",)) + cursor.execute("SELECT id FROM brands WHERE name = ?", ("ACURA",)) + brand_id = cursor.fetchone()[0] + print(f"Marca ACURA tiene ID: {brand_id}") + + # Insertar modelos adicionales de Acura que pueden faltar + additional_models = [ + 'CSX', 'MDX', 'RL', 'RSX', 'TL', 'TSX', 'EL', 'CL', 'NSX', 'SLX', 'INTEGRA' + ] + + for model in additional_models: + cursor.execute( + "INSERT OR IGNORE INTO models (brand_id, name) VALUES (?, ?)", + (brand_id, model) + ) + print(f"Agregados {len(additional_models)} modelos de Acura (existentes o nuevos)") + + # Datos adicionales de años y motores para cada modelo + acura_additional_data = [ + # 2006 + ('CSX', 2006, '2.0L L4'), + ('MDX', 2006, '3.5L V6'), + ('RL', 2006, '3.5L V6'), + ('RSX', 2006, '2.0L L4'), + ('TL', 2006, '3.2L V6'), + ('TSX', 2006, '2.4L L4'), + + # 2005 + ('EL', 2005, '1.7L L4'), + ('MDX', 2005, '3.5L V6'), + ('NSX', 2005, '3.0L V6'), + ('RSX', 2005, '3.2L V6'), + ('RL', 2005, '3.5L V6'), + ('RSX', 2005, '2.0L L4'), + ('TL', 2005, '3.2L V6'), + ('TSX', 2005, '2.4L L4'), + + # 2004 + ('EL', 2004, '1.7L L4'), + ('MDX', 2004, '3.5L V6'), + ('NSX', 2004, '3.0L V6'), + ('RSX', 2004, '3.2L V6'), + ('RL', 2004, '3.5L V6'), + ('TL', 2004, '3.2L V6'), + ('TSX', 2004, '2.4L L4'), + + # 2003 + ('CL', 2003, '3.2L V6'), + ('EL', 2003, '1.7L L4'), + ('MDX', 2003, '3.5L V6'), + ('NSX', 2003, '3.0L V6'), + ('RL', 2003, '3.5L V6'), + ('RSX', 2003, '2.0L L4'), + ('TL', 2003, '3.2L V6'), + + # 2002 + ('CL', 2002, '3.2L V6'), + ('EL', 2002, '1.7L L4'), + ('MDX', 2002, '3.5L V6'), + ('NSX', 2002, '3.0L V6'), + ('RL', 2002, '3.5L V6'), + ('RSX', 2002, '2.0L L4'), + ('TL', 2002, '3.2L V6'), + + # 2001 + ('CL', 2001, '3.2L V6'), + ('EL', 2001, '1.7L L4'), + ('INTEGRA', 2001, '1.8L L4'), + ('MDX', 2001, '3.5L V6'), + ('NSX', 2001, '3.0L V6'), + ('RL', 2001, '3.5L V6'), + ('TL', 2001, '3.2L V6'), + + # 2000 + ('EL', 2000, '1.6L L4'), + ('INTEGRA', 2000, '1.8L L4'), + ('NSX', 2000, '3.0L V6'), + ('RL', 2000, '3.5L V6'), + ('TL', 2000, '3.2L V6'), + + # 1999 + ('CL', 1999, '2.3L L4'), + ('CL', 1999, '3.0L V6'), + ('EL', 1999, '1.6L L4'), + ('INTEGRA', 1999, '1.8L L4'), + ('NSX', 1999, '3.0L V6'), + ('RL', 1999, '3.5L V6'), + ('SLX', 1999, '3.5L V6'), + ('TL', 1999, '3.2L V6'), + + # 1998 + ('CL', 1998, '2.3L L4'), + ('CL', 1998, '3.0L V6'), + ('EL', 1998, '1.6L L4'), + ('INTEGRA', 1998, '1.8L L4'), + ('NSX', 1998, '3.0L V6'), + ('RL', 1998, '3.5L V6'), + ('SLX', 1998, '3.5L V6'), + ('TL', 1998, '2.5L L5'), + ('TL', 1998, '3.2L V6') + ] + + # Insertar años adicionales + years = list(set([data[1] for data in acura_additional_data])) # Obtener años únicos + for year in years: + cursor.execute("INSERT OR IGNORE INTO years (year) VALUES (?)", (year,)) + print(f"Agregados {len(years)} años adicionales") + + # Insertar motores adicionales + engines = list(set([data[2] for data in acura_additional_data])) # Obtener motores únicos + for engine in engines: + cursor.execute("INSERT OR IGNORE INTO engines (name) VALUES (?)", (engine,)) + print(f"Agregados {len(engines)} motores adicionales") + + # Crear combinaciones modelo-año-motor adicionales + for model_name, year, engine_name in acura_additional_data: + # Obtener IDs + cursor.execute("SELECT id FROM models WHERE brand_id = ? AND name = ?", (brand_id, model_name)) + model_result = cursor.fetchone() + if model_result: + model_id = model_result[0] + else: + print(f"Advertencia: Modelo {model_name} no encontrado para la marca ACURA") + continue + + cursor.execute("SELECT id FROM years WHERE year = ?", (year,)) + year_result = cursor.fetchone() + if year_result: + year_id = year_result[0] + else: + print(f"Advertencia: Año {year} no encontrado") + continue + + cursor.execute("SELECT id FROM engines WHERE name = ?", (engine_name,)) + engine_result = cursor.fetchone() + if engine_result: + engine_id = engine_result[0] + else: + print(f"Advertencia: Motor {engine_name} no encontrado") + continue + + # Insertar la combinación modelo-año-motor + cursor.execute( + """INSERT OR REPLACE INTO model_year_engine + (model_id, year_id, engine_id) VALUES (?, ?, ?)""", + (model_id, year_id, engine_id) + ) + + conn.commit() + print(f"Se agregaron {len(acura_additional_data)} combinaciones modelo-año-motor adicionales para ACURA") + print("Datos adicionales de ACURA agregados exitosamente a la base de datos!") + + # Mostrar algunos resultados + print("\nAlgunos vehículos ACURA adicionales agregados:") + cursor.execute(""" + SELECT b.name, m.name, y.year, e.name + FROM model_year_engine mye + JOIN models m ON mye.model_id = m.id + JOIN brands b ON m.brand_id = b.id + JOIN years y ON mye.year_id = y.id + JOIN engines e ON mye.engine_id = e.id + WHERE b.name = 'ACURA' AND y.year <= 2006 + ORDER BY m.name, y.year DESC + LIMIT 15 + """) + + results = cursor.fetchall() + for brand, model, year, engine in results: + print(f" {year} {brand} {model} - {engine}") + + except Exception as e: + print(f"Error al agregar datos adicionales de ACURA: {e}") + conn.rollback() + finally: + conn.close() + +if __name__ == "__main__": + add_acura_additional_data() \ No newline at end of file diff --git a/add_acura_data.py b/add_acura_data.py new file mode 100644 index 0000000..4018dad --- /dev/null +++ b/add_acura_data.py @@ -0,0 +1,317 @@ +#!/usr/bin/env python3 +""" +Script para agregar datos de Acura a la base de datos de vehículos +""" + +import sqlite3 +import os + +def add_acura_data(): + # Verificar que la base de datos exista + db_path = "vehicle_database/vehicle_database.db" + if not os.path.exists(db_path): + print(f"Error: Base de datos no encontrada en {db_path}") + return + + conn = sqlite3.connect(db_path) + cursor = conn.cursor() + + print("Agregando datos de Acura a la base de datos...") + + try: + # Insertar la marca Acura + cursor.execute("INSERT OR IGNORE INTO brands (name) VALUES (?)", ("ACURA",)) + cursor.execute("SELECT id FROM brands WHERE name = ?", ("ACURA",)) + brand_id = cursor.fetchone()[0] + print(f"Marca ACURA tiene ID: {brand_id}") + + # Insertar modelos de Acura + models = [ + 'MDX', 'ADX', 'INTEGRA', 'RDX', 'TLX', 'ZDX', 'ILX', 'NSX', 'RLX', 'TL', 'TSX', 'CSX' + ] + + for model in models: + cursor.execute( + "INSERT OR IGNORE INTO models (brand_id, name) VALUES (?, ?)", + (brand_id, model) + ) + print(f"Agregados {len(models)} modelos de Acura") + + # Datos de años y motores para cada modelo + acura_data = [ + # 2026 + ('MDX', 2026, '3.0L V6 Turbocharged'), + ('MDX', 2026, '3.5L V6'), + + # 2025 + ('ADX', 2025, '1.5L L4 Turbocharged'), + ('INTEGRA', 2025, '1.5L L4 Turbocharged'), + ('INTEGRA', 2025, '2.0L L4 Turbocharged'), + ('MDX', 2025, '3.0L V6 Turbocharged'), + ('MDX', 2025, '3.5L V6'), + ('RDX', 2025, '2.0L L4 Turbocharged'), + ('TLX', 2025, '2.0L L4 Turbocharged'), + ('TLX', 2025, '3.0L V6 Turbocharged'), + + # 2024 + ('INTEGRA', 2024, '1.5L L4 Turbocharged'), + ('INTEGRA', 2024, '2.0L L4 Turbocharged'), + ('MDX', 2024, '3.0L V6 Turbocharged'), + ('MDX', 2024, '3.5L V6'), + ('RDX', 2024, '2.0L L4 Turbocharged'), + ('TLX', 2024, '2.0L L4 Turbocharged'), + ('TLX', 2024, '3.0L V6 Turbocharged'), + ('ZDX', 2024, 'ELECTRIC'), + + # 2023 + ('INTEGRA', 2023, '1.5L L4 Turbocharged'), + ('MDX', 2023, '3.0L V6 Turbocharged'), + ('MDX', 2023, '3.5L V6'), + ('RDX', 2023, '2.0L L4 Turbocharged'), + ('TLX', 2023, '2.0L L4 Turbocharged'), + ('TLX', 2023, '3.0L V6 Turbocharged'), + + # 2022 + ('ILX', 2022, '2.4L L4'), + ('MDX', 2022, '3.0L V6 Turbocharged'), + ('MDX', 2022, '3.5L V6'), + ('NSX', 2022, '3.5L V6 ELECTRIC/GAS Turbocharged'), + ('RDX', 2022, '2.0L L4 Turbocharged'), + ('TLX', 2022, '2.0L L4 Turbocharged'), + ('TLX', 2022, '3.0L V6 Turbocharged'), + + # 2021 + ('ILX', 2021, '2.4L L4'), + ('NSX', 2021, '3.5L V6 ELECTRIC/GAS Turbocharged'), + ('RDX', 2021, '2.0L L4 Turbocharged'), + ('TLX', 2021, '2.0L L4 Turbocharged'), + ('TLX', 2021, '3.0L V6 Turbocharged'), + + # 2020 + ('ILX', 2020, '2.4L L4'), + ('MDX', 2020, '3.0L V6 ELECTRIC/GAS'), + ('MDX', 2020, '3.5L V6'), + ('NSX', 2020, '3.5L V6 ELECTRIC/GAS Turbocharged'), + ('RDX', 2020, '2.0L L4 Turbocharged'), + ('RLX', 2020, '3.5L V6'), + ('RLX', 2020, '3.5L V6 ELECTRIC/GAS'), + ('TLX', 2020, '2.4L L4'), + ('TLX', 2020, '3.5L V6 DOHC'), + ('TLX', 2020, '3.5L V6 SOHC'), + + # 2019 + ('ILX', 2019, '2.4L L4'), + ('MDX', 2019, '3.0L V6'), + ('MDX', 2019, '3.0L V6 ELECTRIC/GAS'), + ('MDX', 2019, '3.5L V6'), + ('NSX', 2019, '3.5L V6 ELECTRIC/GAS Turbocharged'), + ('RDX', 2019, '2.0L L4 Turbocharged'), + ('RLX', 2019, '3.5L V6'), + ('RLX', 2019, '3.5L V6 ELECTRIC/GAS'), + ('TLX', 2019, '2.4L L4'), + ('TLX', 2019, '3.5L V6'), + + # 2018 + ('ILX', 2018, '2.4L L4'), + ('MDX', 2018, '3.0L V6 ELECTRIC/GAS'), + ('MDX', 2018, '3.5L V6'), + ('NSX', 2018, '3.5L V6 ELECTRIC/GAS Turbocharged'), + ('RDX', 2018, '3.5L V6'), + ('RLX', 2018, '3.5L V6'), + ('RLX', 2018, '3.5L V6 ELECTRIC/GAS'), + ('TLX', 2018, '2.4L L4'), + ('TLX', 2018, '3.5L V6'), + + # 2017 + ('ILX', 2017, '2.4L L4'), + ('MDX', 2017, '3.0L V6 ELECTRIC/GAS'), + ('MDX', 2017, '3.5L V6'), + ('NSX', 2017, '3.5L V6 ELECTRIC/GAS Turbocharged'), + ('RDX', 2017, '3.5L V6'), + ('RLX', 2017, '3.5L V6'), + ('RLX', 2017, '3.5L V6 ELECTRIC/GAS'), + ('TLX', 2017, '2.4L L4'), + ('TLX', 2017, '3.5L V6'), + + # 2016 + ('ILX', 2016, '2.4L L4'), + ('MDX', 2016, '3.5L V6'), + ('RDX', 2016, '3.5L V6'), + ('RLX', 2016, '3.5L V6'), + ('RLX', 2016, '3.5L V6 ELECTRIC/GAS'), + ('TLX', 2016, '2.4L L4'), + ('TLX', 2016, '3.5L V6'), + + # 2015 + ('ILX', 2015, '1.5L L4 ELECTRIC/GAS'), + ('ILX', 2015, '2.0L L4'), + ('ILX', 2015, '2.4L L4'), + ('MDX', 2015, '3.5L V6'), + ('RDX', 2015, '3.5L V6'), + ('RLX', 2015, '3.5L V6'), + ('RLX', 2015, '3.5L V6 ELECTRIC/GAS'), + ('TLX', 2015, '2.4L L4'), + ('TLX', 2015, '3.5L V6'), + + # 2014 + ('ILX', 2014, '1.5L L4 ELECTRIC/GAS'), + ('ILX', 2014, '2.0L L4'), + ('ILX', 2014, '2.4L L4'), + ('MDX', 2014, '3.5L V6'), + ('RDX', 2014, '3.5L V6'), + ('RLX', 2014, '3.5L V6'), + ('RLX', 2014, '3.5L V6 ELECTRIC/GAS'), + ('TL', 2014, '3.5L V6'), + ('TSX', 2014, '2.4L L4'), + ('TSX', 2014, '3.5L V6'), + + # 2013 + ('ILX', 2013, '1.5L L4 ELECTRIC/GAS'), + ('ILX', 2013, '2.0L L4'), + ('ILX', 2013, '2.4L L4'), + ('MDX', 2013, '3.7L V6'), + ('RDX', 2013, '3.5L V6'), + ('TL', 2013, '3.5L V6'), + ('TL', 2013, '3.7L V6'), + ('TSX', 2013, '2.4L L4'), + ('TSX', 2013, '3.5L V6'), + ('ZDX', 2013, '3.7L V6'), + + # 2012 + ('MDX', 2012, '3.7L V6'), + ('RDX', 2012, '2.3L L4 Turbocharged'), + ('RL', 2012, '3.7L V6'), + ('TL', 2012, '3.5L V6'), + ('TL', 2012, '3.7L V6'), + ('TSX', 2012, '2.4L L4'), + ('TSX', 2012, '3.5L V6'), + ('ZDX', 2012, '3.7L V6'), + + # 2011 + ('CSX', 2011, '2.0L L4'), + ('MDX', 2011, '3.7L V6'), + ('RDX', 2011, '2.3L L4 Turbocharged'), + ('RL', 2011, '3.7L V6'), + ('TL', 2011, '3.5L V6'), + ('TL', 2011, '3.7L V6'), + ('TSX', 2011, '2.4L L4'), + ('TSX', 2011, '3.5L V6'), + ('ZDX', 2011, '3.7L V6'), + + # 2010 + ('CSX', 2010, '2.0L L4'), + ('MDX', 2010, '3.7L V6'), + ('RDX', 2010, '2.3L L4 Turbocharged'), + ('RL', 2010, '3.7L V6'), + ('TL', 2010, '3.5L V6'), + ('TL', 2010, '3.7L V6'), + ('TSX', 2010, '2.4L L4'), + ('TSX', 2010, '3.5L V6'), + ('ZDX', 2010, '3.7L V6'), + + # 2009 + ('CSX', 2009, '2.0L L4'), + ('MDX', 2009, '3.7L V6'), + ('RDX', 2009, '2.3L L4 Turbocharged'), + ('RL', 2009, '3.7L V6'), + ('TL', 2009, '3.5L V6'), + ('TL', 2009, '3.7L V6'), + ('TSX', 2009, '2.4L L4'), + ('TSX', 2009, '3.5L V6'), + + # 2008 + ('CSX', 2008, '2.0L L4'), + ('MDX', 2008, '3.7L V6'), + ('RDX', 2008, '2.3L L4 Turbocharged'), + ('RL', 2008, '3.5L V6'), + ('TL', 2008, '3.2L V6'), + ('TL', 2008, '3.5L V6'), + ('TSX', 2008, '2.4L L4'), + ('TSX', 2008, '3.5L V6'), + + # 2007 + ('CSX', 2007, '2.0L L4'), + ('MDX', 2007, '3.7L V6'), + ('RDX', 2007, '2.3L L4 Turbocharged'), + ('RL', 2007, '3.5L V6'), + ('TL', 2007, '3.2L V6'), + ('TL', 2007, '3.5L V6'), + ('TSX', 2007, '2.4L L4') + ] + + # Insertar años + years = list(set([data[1] for data in acura_data])) # Obtener años únicos + for year in years: + cursor.execute("INSERT OR IGNORE INTO years (year) VALUES (?)", (year,)) + print(f"Agregados {len(years)} años") + + # Insertar motores + engines = list(set([data[2] for data in acura_data])) # Obtener motores únicos + for engine in engines: + cursor.execute("INSERT OR IGNORE INTO engines (name) VALUES (?)", (engine,)) + print(f"Agregados {len(engines)} motores") + + # Crear combinaciones modelo-año-motor + for model_name, year, engine_name in acura_data: + # Obtener IDs + cursor.execute("SELECT id FROM models WHERE brand_id = ? AND name = ?", (brand_id, model_name)) + model_result = cursor.fetchone() + if model_result: + model_id = model_result[0] + else: + print(f"Advertencia: Modelo {model_name} no encontrado para la marca ACURA") + continue + + cursor.execute("SELECT id FROM years WHERE year = ?", (year,)) + year_result = cursor.fetchone() + if year_result: + year_id = year_result[0] + else: + print(f"Advertencia: Año {year} no encontrado") + continue + + cursor.execute("SELECT id FROM engines WHERE name = ?", (engine_name,)) + engine_result = cursor.fetchone() + if engine_result: + engine_id = engine_result[0] + else: + print(f"Advertencia: Motor {engine_name} no encontrado") + continue + + # Insertar la combinación modelo-año-motor + cursor.execute( + """INSERT OR REPLACE INTO model_year_engine + (model_id, year_id, engine_id) VALUES (?, ?, ?)""", + (model_id, year_id, engine_id) + ) + + conn.commit() + print(f"Se agregaron {len(acura_data)} combinaciones modelo-año-motor para ACURA") + print("Datos de ACURA agregados exitosamente a la base de datos!") + + # Mostrar algunos resultados + print("\nAlgunos vehículos ACURA agregados:") + cursor.execute(""" + SELECT b.name, m.name, y.year, e.name + FROM model_year_engine mye + JOIN models m ON mye.model_id = m.id + JOIN brands b ON m.brand_id = b.id + JOIN years y ON mye.year_id = y.id + JOIN engines e ON mye.engine_id = e.id + WHERE b.name = 'ACURA' + ORDER BY m.name, y.year DESC + LIMIT 15 + """) + + results = cursor.fetchall() + for brand, model, year, engine in results: + print(f" {year} {brand} {model} - {engine}") + + except Exception as e: + print(f"Error al agregar datos de ACURA: {e}") + conn.rollback() + finally: + conn.close() + +if __name__ == "__main__": + add_acura_data() \ No newline at end of file diff --git a/add_acura_historical_data.py b/add_acura_historical_data.py new file mode 100644 index 0000000..155992a --- /dev/null +++ b/add_acura_historical_data.py @@ -0,0 +1,188 @@ +#!/usr/bin/env python3 +""" +Script para agregar datos históricos adicionales de Acura a la base de datos de vehículos +""" + +import sqlite3 +import os + +def add_acura_historical_data(): + # Verificar que la base de datos exista + db_path = "vehicle_database/vehicle_database.db" + if not os.path.exists(db_path): + print(f"Error: Base de datos no encontrada en {db_path}") + return + + conn = sqlite3.connect(db_path) + cursor = conn.cursor() + + print("Agregando datos históricos adicionales de Acura a la base de datos...") + + try: + # Asegurar que la marca Acura exista + cursor.execute("INSERT OR IGNORE INTO brands (name) VALUES (?)", ("ACURA",)) + cursor.execute("SELECT id FROM brands WHERE name = ?", ("ACURA",)) + brand_id = cursor.fetchone()[0] + print(f"Marca ACURA tiene ID: {brand_id}") + + # Insertar modelos adicionales de Acura + additional_models = ['CL', 'INTEGRA', 'NSX', 'RL', 'SLX', 'TL', 'LEGEND', 'VIGOR'] + + for model in additional_models: + cursor.execute( + "INSERT OR IGNORE INTO models (brand_id, name) VALUES (?, ?)", + (brand_id, model) + ) + print(f"Agregados {len(additional_models)} modelos adicionales de Acura") + + # Datos históricos de años y motores para cada modelo + acura_historical_data = [ + # 1997 + ('CL', 1997, '2.2L L4'), + ('CL', 1997, '3.0L V6'), + ('INTEGRA', 1997, '1.8L L4'), + ('NSX', 1997, '3.0L V6'), + ('NSX', 1997, '3.2L V6'), + ('RL', 1997, '3.5L V6'), + ('SLX', 1997, '3.2L V6'), + ('TL', 1997, '2.5L L5'), + ('TL', 1997, '3.2L V6'), + + # 1996 + ('INTEGRA', 1996, '1.8L L4'), + ('NSX', 1996, '3.0L V6'), + ('RL', 1996, '3.5L V6'), + ('SLX', 1996, '3.2L V6'), + ('TL', 1996, '2.5L L5'), + ('TL', 1996, '3.2L V6'), + + # 1995 + ('INTEGRA', 1995, '1.8L L4'), + ('LEGEND', 1995, '3.2L V6'), + ('NSX', 1995, '3.0L V6'), + ('TL', 1995, '2.5L L5'), + + # 1994 + ('INTEGRA', 1994, '1.8L L4'), + ('LEGEND', 1994, '3.2L V6'), + ('NSX', 1994, '3.0L V6'), + ('VIGOR', 1994, '2.5L L5'), + + # 1993 + ('INTEGRA', 1993, '1.7L L4'), + ('INTEGRA', 1993, '1.8L L4'), + ('LEGEND', 1993, '3.2L V6'), + ('NSX', 1993, '3.0L V6'), + ('VIGOR', 1993, '2.5L L5'), + + # 1992 + ('INTEGRA', 1992, '1.7L L4'), + ('INTEGRA', 1992, '1.8L L4'), + ('LEGEND', 1992, '3.2L V6'), + ('NSX', 1992, '3.0L V6'), + ('VIGOR', 1992, '2.5L L5'), + + # 1991 + ('INTEGRA', 1991, '1.8L L4'), + ('LEGEND', 1991, '3.2L V6'), + ('NSX', 1991, '3.0L V6'), + + # 1990 + ('INTEGRA', 1990, '1.8L L4'), + ('LEGEND', 1990, '2.7L V6'), + + # 1989 + ('INTEGRA', 1989, '1.6L L4'), + ('LEGEND', 1989, '2.7L V6'), + + # 1988 + ('INTEGRA', 1988, '1.6L L4'), + ('LEGEND', 1988, '2.7L V6'), + + # 1987 + ('INTEGRA', 1987, '1.6L L4'), + ('LEGEND', 1987, '2.5L V6'), + ('LEGEND', 1987, '2.7L V6'), + + # 1986 + ('INTEGRA', 1986, '1.6L L4'), + ('LEGEND', 1986, '2.5L V6'), + ('LEGEND', 1986, '2.7L V6') + ] + + # Insertar años históricos + years = list(set([data[1] for data in acura_historical_data])) # Obtener años únicos + for year in years: + cursor.execute("INSERT OR IGNORE INTO years (year) VALUES (?)", (year,)) + print(f"Agregados {len(years)} años históricos adicionales") + + # Insertar motores históricos + engines = list(set([data[2] for data in acura_historical_data])) # Obtener motores únicos + for engine in engines: + cursor.execute("INSERT OR IGNORE INTO engines (name) VALUES (?)", (engine,)) + print(f"Agregados {len(engines)} motores históricos adicionales") + + # Crear combinaciones modelo-año-motor históricas + for model_name, year, engine_name in acura_historical_data: + # Obtener IDs + cursor.execute("SELECT id FROM models WHERE brand_id = ? AND name = ?", (brand_id, model_name)) + model_result = cursor.fetchone() + if model_result: + model_id = model_result[0] + else: + print(f"Advertencia: Modelo {model_name} no encontrado para la marca ACURA") + continue + + cursor.execute("SELECT id FROM years WHERE year = ?", (year,)) + year_result = cursor.fetchone() + if year_result: + year_id = year_result[0] + else: + print(f"Advertencia: Año {year} no encontrado") + continue + + cursor.execute("SELECT id FROM engines WHERE name = ?", (engine_name,)) + engine_result = cursor.fetchone() + if engine_result: + engine_id = engine_result[0] + else: + print(f"Advertencia: Motor {engine_name} no encontrado") + continue + + # Insertar la combinación modelo-año-motor + cursor.execute( + """INSERT OR REPLACE INTO model_year_engine + (model_id, year_id, engine_id) VALUES (?, ?, ?)""", + (model_id, year_id, engine_id) + ) + + conn.commit() + print(f"Se agregaron {len(acura_historical_data)} combinaciones modelo-año-motor históricas adicionales para ACURA") + print("Datos históricos adicionales de ACURA agregados exitosamente a la base de datos!") + + # Mostrar algunos resultados + print("\nAlgunos vehículos ACURA históricos adicionales agregados:") + cursor.execute(""" + SELECT b.name, m.name, y.year, e.name + FROM model_year_engine mye + JOIN models m ON mye.model_id = m.id + JOIN brands b ON m.brand_id = b.id + JOIN years y ON mye.year_id = y.id + JOIN engines e ON mye.engine_id = e.id + WHERE b.name = 'ACURA' AND y.year BETWEEN 1986 AND 1997 + ORDER BY y.year DESC, m.name + LIMIT 15 + """) + + results = cursor.fetchall() + for brand, model, year, engine in results: + print(f" {year} {brand} {model} - {engine}") + + except Exception as e: + print(f"Error al agregar datos históricos adicionales de ACURA: {e}") + conn.rollback() + finally: + conn.close() + +if __name__ == "__main__": + add_acura_historical_data() \ No newline at end of file diff --git a/add_alfa_romeo_data.py b/add_alfa_romeo_data.py new file mode 100644 index 0000000..2da47e9 --- /dev/null +++ b/add_alfa_romeo_data.py @@ -0,0 +1,308 @@ +#!/usr/bin/env python3 +""" +Script para agregar datos de Alfa Romeo a la base de datos de vehículos +""" + +import sqlite3 +import os + +def add_alfa_romeo_data(): + # Verificar que la base de datos exista + db_path = "vehicle_database/vehicle_database.db" + if not os.path.exists(db_path): + print(f"Error: Base de datos no encontrada en {db_path}") + return + + conn = sqlite3.connect(db_path) + cursor = conn.cursor() + + print("Agregando datos de Alfa Romeo a la base de datos...") + + try: + # Insertar la marca Alfa Romeo + cursor.execute("INSERT OR IGNORE INTO brands (name) VALUES (?)", ("ALFA ROMEO",)) + cursor.execute("SELECT id FROM brands WHERE name = ?", ("ALFA ROMEO",)) + brand_id = cursor.fetchone()[0] + print(f"Marca ALFA ROMEO tiene ID: {brand_id}") + + # Insertar modelos de Alfa Romeo (incluyendo GIULIA que aparece en los datos) + models = [ + '4C', 'GIULIETTA', 'MITO', '159', '8C', 'BRERA', 'GT', 'SPIDER', + '147', '156', '166', 'SPORTWAGON', '164', 'MILANO', 'GTV-6', + 'STELVIO', 'TONALE', 'GIULIA' # Agregué GIULIA que aparece en los datos + ] + + for model in models: + cursor.execute( + "INSERT OR IGNORE INTO models (brand_id, name) VALUES (?, ?)", + (brand_id, model) + ) + print(f"Agregados {len(models)} modelos de Alfa Romeo") + + # Datos de años y motores para cada modelo + alfa_data = [ + # 2025 + ('GIULIA', 2025, '2.0L L4 Turbocharged'), + ('STELVIO', 2025, '2.0L L4 Turbocharged'), + ('TONALE', 2025, '1.3L L4 ELECTRIC/GAS Turbocharged SOHC'), + ('TONALE', 2025, '2.0L L4 Turbocharged'), + + # 2024 + ('GIULIA', 2024, '2.0L L4 Turbocharged'), + ('STELVIO', 2024, '2.0L L4 Turbocharged'), + ('TONALE', 2024, '1.3L L4 ELECTRIC/GAS Turbocharged'), + ('TONALE', 2024, '2.0L L4 Turbocharged'), + + # 2023 + ('GIULIA', 2023, '2.0L L4 Turbocharged'), + ('STELVIO', 2023, '2.0L L4 Turbocharged'), + ('STELVIO', 2023, '2.9L V6 Turbocharged'), + + # 2022 + ('GIULIA', 2022, '2.0L L4 Turbocharged'), + ('STELVIO', 2022, '2.0L L4 Turbocharged'), + ('STELVIO', 2022, '2.9L V6 Turbocharged'), + + # 2021 + ('GIULIA', 2021, '2.0L L4 Turbocharged'), + ('GIULIA', 2021, '2.9L V6 Turbocharged'), + ('GIULIETTA', 2021, '1.7L L4 Turbocharged'), + ('STELVIO', 2021, '2.0L L4 Turbocharged'), + ('STELVIO', 2021, '2.9L V6 Turbocharged'), + + # 2020 + ('4C', 2020, '1.7L L4 Turbocharged'), + ('GIULIA', 2020, '2.0L L4 Turbocharged'), + ('GIULIA', 2020, '2.9L V6 Turbocharged'), + ('GIULIETTA', 2020, '1.7L L4 Turbocharged'), + ('STELVIO', 2020, '2.0L L4 Turbocharged'), + ('STELVIO', 2020, '2.9L V6 Turbocharged'), + + # 2019 + ('4C', 2019, '1.7L L4 Turbocharged'), + ('GIULIA', 2019, '2.0L L4 Turbocharged'), + ('GIULIA', 2019, '2.9L V6 Turbocharged'), + ('GIULIETTA', 2019, '1.7L L4 Turbocharged'), + ('MITO', 2019, '1.4L L4 Turbocharged'), + ('STELVIO', 2019, '2.0L L4 Turbocharged'), + ('STELVIO', 2019, '2.9L V6 Turbocharged'), + + # 2018 + ('4C', 2018, '1.7L L4 Turbocharged'), + ('GIULIA', 2018, '2.0L L4 Turbocharged'), + ('GIULIA', 2018, '2.9L V6 Turbocharged'), + ('GIULIETTA', 2018, '1.7L L4 Turbocharged'), + ('MITO', 2018, '1.4L L4 Turbocharged'), + ('STELVIO', 2018, '2.0L L4 Turbocharged'), + ('STELVIO', 2018, '2.9L V6 Turbocharged'), + + # 2017 + ('4C', 2017, '1.7L L4 Turbocharged'), + ('GIULIA', 2017, '2.0L L4 Turbocharged'), + ('GIULIA', 2017, '2.9L V6 Turbocharged'), + ('GIULIETTA', 2017, '1.7L L4 Turbocharged'), + ('MITO', 2017, '1.4L L4 Turbocharged'), + ('STELVIO', 2017, '2.0L L4 Turbocharged'), + ('STELVIO', 2017, '2.9L V6 Turbocharged'), + + # 2016 + ('4C', 2016, '1.7L L4 Turbocharged'), + ('GIULIA', 2016, '2.0L L4 Turbocharged'), + ('GIULIA', 2016, '2.9L V6 Turbocharged'), + ('GIULIETTA', 2016, '1.7L L4 Turbocharged'), + ('MITO', 2016, '1.4L L4 Turbocharged'), + ('STELVIO', 2016, '2.0L L4 Turbocharged'), + ('STELVIO', 2016, '2.9L V6 Turbocharged'), + + # 2015 + ('4C', 2015, '1.7L L4 Turbocharged'), + ('GIULIETTA', 2015, '1.7L L4 Turbocharged'), + ('GIULIETTA', 2015, '1.8L L4 Turbocharged'), + ('MITO', 2015, '1.4L L4 Turbocharged'), + + # 2014 + ('GIULIETTA', 2014, '1.7L L4 Turbocharged'), + + # 2013 + ('GIULIETTA', 2013, '1.7L L4 Turbocharged'), + ('MITO', 2013, '1.4L L4 Turbocharged'), + + # 2012 + ('159', 2012, '2.2L L4'), + ('159', 2012, '3.2L V6'), + ('GIULIETTA', 2012, '1.8L L4 Turbocharged'), + ('MITO', 2012, '1.4L L4 Turbocharged'), + + # 2008 + ('159', 2008, '2.2L L4'), + ('8C', 2008, '4.7L V8'), + ('BRERA', 2008, '3.2L V6'), + ('GT', 2008, '2.0L L4'), + ('SPIDER', 2008, '3.2L V6'), + + # 2007 + ('147', 2007, '2.0L L4'), + ('159', 2007, '2.2L L4'), + ('159', 2007, '3.2L V6'), + ('BRERA', 2007, '3.2L V6'), + ('GT', 2007, '2.0L L4'), + ('SPIDER', 2007, '3.2L V6'), + + # 2005 + ('147', 2005, '2.0L L4'), + + # 2004 + ('147', 2004, '2.0L L4'), + + # 2003 + ('147', 2003, '2.0L L4'), + ('166', 2003, '3.0L V6'), + ('GTV', 2003, '3.0L V6'), # Nota: En los datos originales dice 'GTV', pero en modelos dice 'GTV-6' + ('SPIDER', 2003, '3.0L V6'), + + # 2002 + ('147', 2002, '2.0L L4'), + ('156', 2002, '2.0L L4'), + ('166', 2002, '3.0L V6'), + ('SPORTWAGON', 2002, '2.0L L4'), + + # 1995 + ('164', 1995, '3.0L V6'), + + # 1994 + ('164', 1994, '3.0L V6'), + ('SPIDER', 1994, '2.0L L4'), + + # 1993 + ('164', 1993, '3.0L V6'), + ('SPIDER', 1993, '2.0L L4'), + + # 1992 + ('164', 1992, '3.0L V6'), + ('SPIDER', 1992, '2.0L L4'), + + # 1991 + ('164', 1991, '2.0L L4'), + + # 1990 + ('SPIDER', 1990, '2.0L L4'), + + # 1989 + ('MILANO', 1989, '2.5L V6'), + ('MILANO', 1989, '3.0L V6'), + ('SPIDER', 1989, '2.0L L4'), + + # 1988 + ('MILANO', 1988, '2.5L V6'), + ('MILANO', 1988, '3.0L V6'), + ('SPIDER', 1988, '2.0L L4'), + + # 1987 + ('MILANO', 1987, '2.5L V6'), + ('MILANO', 1987, '3.0L V6'), + ('SPIDER', 1987, '2.0L L4'), + + # 1986 + ('GTV-6', 1986, '2.5L V6'), + ('SPIDER', 1986, '2.0L L4'), + + # 1985 + ('GTV-6', 1985, '2.5L V6'), + ('SPIDER', 1985, '2.0L L4'), + + # 1984 + ('GTV-6', 1984, '2.5L V6'), + ('SPIDER', 1984, '2.0L L4'), + + # 1983 + ('GTV-6', 1983, '2.5L V6'), + ('SPIDER', 1983, '2.0L L4'), + + # 1982 + ('GTV-6', 1982, '2.5L V6'), + ('SPIDER', 1982, '2.0L L4'), + + # 1981 + ('GTV-6', 1981, '2.5L V6'), + ('SPIDER', 1981, '2.0L L4'), + + # 1980 + ('SPIDER', 1980, '2.0L L4') + ] + + # Insertar años + years = list(set([data[1] for data in alfa_data])) # Obtener años únicos + for year in years: + cursor.execute("INSERT OR IGNORE INTO years (year) VALUES (?)", (year,)) + print(f"Agregados {len(years)} años") + + # Insertar motores + engines = list(set([data[2] for data in alfa_data])) # Obtener motores únicos + for engine in engines: + cursor.execute("INSERT OR IGNORE INTO engines (name) VALUES (?)", (engine,)) + print(f"Agregados {len(engines)} motores") + + # Crear combinaciones modelo-año-motor + for model_name, year, engine_name in alfa_data: + # Obtener IDs + cursor.execute("SELECT id FROM models WHERE brand_id = ? AND name = ?", (brand_id, model_name)) + model_result = cursor.fetchone() + if model_result: + model_id = model_result[0] + else: + print(f"Advertencia: Modelo {model_name} no encontrado para la marca ALFA ROMEO") + continue + + cursor.execute("SELECT id FROM years WHERE year = ?", (year,)) + year_result = cursor.fetchone() + if year_result: + year_id = year_result[0] + else: + print(f"Advertencia: Año {year} no encontrado") + continue + + cursor.execute("SELECT id FROM engines WHERE name = ?", (engine_name,)) + engine_result = cursor.fetchone() + if engine_result: + engine_id = engine_result[0] + else: + print(f"Advertencia: Motor {engine_name} no encontrado") + continue + + # Insertar la combinación modelo-año-motor + cursor.execute( + """INSERT OR REPLACE INTO model_year_engine + (model_id, year_id, engine_id) VALUES (?, ?, ?)""", + (model_id, year_id, engine_id) + ) + + conn.commit() + print(f"Se agregaron {len(alfa_data)} combinaciones modelo-año-motor para ALFA ROMEO") + print("Datos de ALFA ROMEO agregados exitosamente a la base de datos!") + + # Mostrar algunos resultados + print("\nAlgunos vehículos ALFA ROMEO agregados:") + cursor.execute(""" + SELECT b.name, m.name, y.year, e.name + FROM model_year_engine mye + JOIN models m ON mye.model_id = m.id + JOIN brands b ON m.brand_id = b.id + JOIN years y ON mye.year_id = y.id + JOIN engines e ON mye.engine_id = e.id + WHERE b.name = 'ALFA ROMEO' + ORDER BY y.year DESC, m.name + LIMIT 15 + """) + + results = cursor.fetchall() + for brand, model, year, engine in results: + print(f" {year} {brand} {model} - {engine}") + + except Exception as e: + print(f"Error al agregar datos de ALFA ROMEO: {e}") + conn.rollback() + finally: + conn.close() + +if __name__ == "__main__": + add_alfa_romeo_data() \ No newline at end of file diff --git a/add_american_motors_data.py b/add_american_motors_data.py new file mode 100644 index 0000000..58a33c4 --- /dev/null +++ b/add_american_motors_data.py @@ -0,0 +1,279 @@ +""" +Script to add American Motors Corporation (AMC) vehicle data to the database. +This includes inserting the brand, models, and year/motor combinations. +""" +import sqlite3 +from typing import List, Tuple + + +def connect_db(db_path: str = "vehicle_database/vehicle_database.db") -> sqlite3.Connection: + """Connect to the vehicle database.""" + conn = sqlite3.connect(db_path) + conn.execute("PRAGMA foreign_keys = ON") # Enable foreign key constraints + return conn + + +def insert_brand(conn: sqlite3.Connection, brand_name: str) -> int: + """Insert a brand if it doesn't exist and return its ID.""" + cursor = conn.cursor() + + # Check if brand already exists + cursor.execute("SELECT id FROM brands WHERE name = ?", (brand_name,)) + result = cursor.fetchone() + + if result: + return result[0] # Return existing ID + + # Insert new brand + cursor.execute("INSERT INTO brands (name) VALUES (?)", (brand_name,)) + conn.commit() + return cursor.lastrowid + + +def insert_models(conn: sqlite3.Connection, brand_id: int, models: List[str]) -> None: + """Insert models if they don't exist.""" + cursor = conn.cursor() + + for model_name in models: + # Check if model already exists for this brand + cursor.execute( + "SELECT id FROM models WHERE brand_id = ? AND name = ?", + (brand_id, model_name) + ) + result = cursor.fetchone() + + if not result: + # Insert new model + cursor.execute( + "INSERT INTO models (brand_id, name) VALUES (?, ?)", + (brand_id, model_name) + ) + + conn.commit() + + +def get_or_insert_engine(conn: sqlite3.Connection, engine_name: str) -> int: + """Get engine ID or insert if it doesn't exist.""" + cursor = conn.cursor() + + # Check if engine already exists + cursor.execute("SELECT id FROM engines WHERE name = ?", (engine_name,)) + result = cursor.fetchone() + + if result: + return result[0] # Return existing ID + + # Insert new engine + cursor.execute("INSERT INTO engines (name) VALUES (?)", (engine_name,)) + conn.commit() + return cursor.lastrowid + + +def get_or_insert_year(conn: sqlite3.Connection, year: int) -> int: + """Get year ID or insert if it doesn't exist.""" + cursor = conn.cursor() + + # Check if year already exists + cursor.execute("SELECT id FROM years WHERE year = ?", (year,)) + result = cursor.fetchone() + + if result: + return result[0] # Return existing ID + + # Insert new year + cursor.execute("INSERT INTO years (year) VALUES (?)", (year,)) + conn.commit() + return cursor.lastrowid + + +def insert_versions(conn: sqlite3.Connection, versions: List[Tuple]) -> None: + """Insert vehicle versions if they don't exist.""" + cursor = conn.cursor() + + for model_name, year, engine in versions: + # Get model ID + cursor.execute( + "SELECT m.id FROM models m JOIN brands ma ON m.brand_id = ma.id " + "WHERE ma.name = 'AMERICAN MOTORS' AND m.name = ?", + (model_name,) + ) + model_result = cursor.fetchone() + + if model_result: + model_id = model_result[0] + + # Get or insert year + year_id = get_or_insert_year(conn, year) + + # Get or insert engine + engine_id = get_or_insert_engine(conn, engine) + + # Check if version relationship already exists + cursor.execute( + "SELECT id FROM model_year_engine WHERE model_id = ? AND year_id = ? AND engine_id = ?", + (model_id, year_id, engine_id) + ) + version_result = cursor.fetchone() + + if not version_result: + # Insert new version relationship + cursor.execute( + "INSERT INTO model_year_engine (model_id, year_id, engine_id) VALUES (?, ?, ?)", + (model_id, year_id, engine_id) + ) + + conn.commit() + + +def main(): + """Main function to add American Motors Corporation data.""" + print("Adding American Motors Corporation data...") + + # Connect to database + conn = connect_db() + + # Insert brand + brand_id = insert_brand(conn, "AMERICAN MOTORS") + print(f"Brand 'AMERICAN MOTORS' inserted with ID: {brand_id}") + + # Define models + models = [ + 'GREMLIN', + 'HORNET', + 'MATADOR', + 'PACER', + 'AMX', + 'CONCORD', + 'EAGLE', + 'SPIRIT' + ] + + # Insert models + insert_models(conn, brand_id, models) + print(f"Models inserted for AMERICAN MOTORS: {', '.join(models)}") + + # Define versions (model, year, engine) + versions = [ + # 1988 + ('EAGLE', 1988, '4.2L 258cid L6'), + + # 1987 + ('EAGLE', 1987, '4.2L 258cid L6'), + + # 1986 + ('EAGLE', 1986, '4.2L 258cid L6'), + + # 1985 + ('EAGLE', 1985, '4.2L 258cid L6'), + + # 1984 + ('EAGLE', 1984, '2.5L 150cid L4'), + ('EAGLE', 1984, '4.2L 258cid L6'), + + # 1983 + ('CONCORD', 1983, '2.5L 151cid L4'), + ('CONCORD', 1983, '4.2L 258cid L6'), + ('EAGLE', 1983, '2.5L 151cid L4'), + ('EAGLE', 1983, '4.2L 258cid L6'), + ('SPIRIT', 1983, '2.5L 151cid L4'), + ('SPIRIT', 1983, '4.2L 258cid L6'), + + # 1982 + ('CONCORD', 1982, '2.5L 151cid L4'), + ('CONCORD', 1982, '4.2L 258cid L6'), + ('EAGLE', 1982, '2.5L 151cid L4'), + ('EAGLE', 1982, '4.2L 258cid L6'), + ('SPIRIT', 1982, '2.5L 151cid L4'), + ('SPIRIT', 1982, '4.2L 258cid L6'), + + # 1981 + ('CONCORD', 1981, '2.5L 151cid L4'), + ('CONCORD', 1981, '4.2L 258cid L6'), + ('EAGLE', 1981, '2.5L 151cid L4'), + ('EAGLE', 1981, '4.2L 258cid L6'), + ('SPIRIT', 1981, '2.5L 151cid L4'), + ('SPIRIT', 1981, '4.2L 258cid L6'), + + # 1980 + ('AMX', 1980, '4.2L 258cid L6'), + ('CONCORD', 1980, '2.5L 151cid L4'), + ('CONCORD', 1980, '4.2L 258cid L6'), + ('EAGLE', 1980, '2.5L 151cid L4'), + ('EAGLE', 1980, '4.2L 258cid L6'), + ('PACER', 1980, '4.2L 258cid L6'), + ('SPIRIT', 1980, '2.5L 151cid L4'), + ('SPIRIT', 1980, '4.2L 258cid L6'), + + # 1979 + ('AMX', 1979, '4.2L 258cid L6'), + ('CONCORD', 1979, '2.0L 121cid L4'), + ('CONCORD', 1979, '3.8L 232cid L6'), + ('CONCORD', 1979, '4.2L 258cid L6'), + ('EAGLE', 1979, '2.5L 151cid L4'), + ('EAGLE', 1979, '4.2L 258cid L6'), + ('PACER', 1979, '4.2L 258cid L6'), + ('SPIRIT', 1979, '2.5L 151cid L4'), + ('SPIRIT', 1979, '4.2L 258cid L6'), + + # 1978 + ('AMX', 1978, '4.2L 258cid L6'), + ('CONCORD', 1978, '2.0L 121cid L4'), + ('CONCORD', 1978, '3.8L 232cid L6'), + ('CONCORD', 1978, '4.2L 258cid L6'), + ('EAGLE', 1978, '2.5L 151cid L4'), + ('EAGLE', 1978, '4.2L 258cid L6'), + ('PACER', 1978, '4.2L 258cid L6'), + ('SPIRIT', 1978, '2.5L 151cid L4'), + ('SPIRIT', 1978, '4.2L 258cid L6'), + + # 1977 + ('GREMLIN', 1977, '2.0L 121cid L4'), + ('GREMLIN', 1977, '3.8L 232cid L6'), + ('GREMLIN', 1977, '4.2L 258cid L6'), + ('HORNET', 1977, '3.8L 232cid L6'), + ('HORNET', 1977, '4.2L 258cid L6'), + ('HORNET', 1977, '5.0L 304cid V8'), + ('MATADOR', 1977, '4.2L 258cid L6'), + ('MATADOR', 1977, '5.0L 304cid V8'), + ('MATADOR', 1977, '5.9L 360cid V8'), + ('PACER', 1977, '3.8L 232cid L6'), + ('PACER', 1977, '4.2L 258cid L6'), + + # 1976 + ('GREMLIN', 1976, '3.8L 232cid L6'), + ('GREMLIN', 1976, '4.2L 258cid L6'), + ('GREMLIN', 1976, '5.0L 304cid V8'), + ('HORNET', 1976, '3.8L 232cid L6'), + ('HORNET', 1976, '4.2L 258cid L6'), + ('HORNET', 1976, '5.0L 304cid V8'), + ('MATADOR', 1976, '4.2L 258cid L6'), + ('MATADOR', 1976, '5.0L 304cid V8'), + ('MATADOR', 1976, '5.9L 360cid V8'), + ('PACER', 1976, '3.8L 232cid L6'), + ('PACER', 1976, '4.2L 258cid L6'), + + # 1975 + ('GREMLIN', 1975, '3.8L 232cid L6'), + ('GREMLIN', 1975, '4.2L 258cid L6'), + ('GREMLIN', 1975, '5.0L 304cid V8'), + ('HORNET', 1975, '3.8L 232cid L6'), + ('HORNET', 1975, '4.2L 258cid L6'), + ('HORNET', 1975, '5.0L 304cid V8'), + ('MATADOR', 1975, '4.2L 258cid L6'), + ('MATADOR', 1975, '5.0L 304cid V8'), + ('MATADOR', 1975, '5.9L 360cid V8'), + ('PACER', 1975, '3.8L 232cid L6'), + ('PACER', 1975, '4.2L 258cid L6') + ] + + # Insert versions + insert_versions(conn, versions) + print(f"Inserted {len(versions)} versions for AMERICAN MOTORS vehicles") + + # Close connection + conn.close() + print("American Motors Corporation data added successfully!") + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/add_aston_martin_data.py b/add_aston_martin_data.py new file mode 100644 index 0000000..757b75c --- /dev/null +++ b/add_aston_martin_data.py @@ -0,0 +1,445 @@ +""" +Script to add Aston Martin vehicle data to the database. +This includes inserting the brand, models, and year/motor combinations. +""" +import sqlite3 +from typing import List, Tuple + + +def connect_db(db_path: str = "vehicle_database/vehicle_database.db") -> sqlite3.Connection: + """Connect to the vehicle database.""" + conn = sqlite3.connect(db_path) + conn.execute("PRAGMA foreign_keys = ON") # Enable foreign key constraints + return conn + + +def get_or_insert_brand(conn: sqlite3.Connection, brand_name: str) -> int: + """Get brand ID or insert if it doesn't exist.""" + cursor = conn.cursor() + + # Check if brand already exists + cursor.execute("SELECT id FROM brands WHERE name = ?", (brand_name,)) + result = cursor.fetchone() + + if result: + return result[0] # Return existing ID + + # Insert new brand + cursor.execute("INSERT INTO brands (name) VALUES (?)", (brand_name,)) + conn.commit() + return cursor.lastrowid + + +def get_or_insert_model(conn: sqlite3.Connection, brand_id: int, model_name: str) -> int: + """Get model ID or insert if it doesn't exist.""" + cursor = conn.cursor() + + # Check if model already exists for this brand + cursor.execute( + "SELECT id FROM models WHERE brand_id = ? AND name = ?", + (brand_id, model_name) + ) + result = cursor.fetchone() + + if result: + return result[0] # Return existing ID + + # Insert new model + cursor.execute( + "INSERT INTO models (brand_id, name) VALUES (?, ?)", + (brand_id, model_name) + ) + conn.commit() + return cursor.lastrowid + + +def get_or_insert_engine(conn: sqlite3.Connection, engine_name: str) -> int: + """Get engine ID or insert if it doesn't exist.""" + cursor = conn.cursor() + + # Check if engine already exists + cursor.execute("SELECT id FROM engines WHERE name = ?", (engine_name,)) + result = cursor.fetchone() + + if result: + return result[0] # Return existing ID + + # Insert new engine + cursor.execute("INSERT INTO engines (name) VALUES (?)", (engine_name,)) + conn.commit() + return cursor.lastrowid + + +def get_or_insert_year(conn: sqlite3.Connection, year: int) -> int: + """Get year ID or insert if it doesn't exist.""" + cursor = conn.cursor() + + # Check if year already exists + cursor.execute("SELECT id FROM years WHERE year = ?", (year,)) + result = cursor.fetchone() + + if result: + return result[0] # Return existing ID + + # Insert new year + cursor.execute("INSERT INTO years (year) VALUES (?)", (year,)) + conn.commit() + return cursor.lastrowid + + +def insert_version_relationship(conn: sqlite3.Connection, model_id: int, year_id: int, engine_id: int) -> None: + """Insert a version relationship if it doesn't exist.""" + cursor = conn.cursor() + + # Check if version relationship already exists + cursor.execute( + "SELECT id FROM model_year_engine WHERE model_id = ? AND year_id = ? AND engine_id = ?", + (model_id, year_id, engine_id) + ) + result = cursor.fetchone() + + if not result: + # Insert new version relationship + cursor.execute( + "INSERT INTO model_year_engine (model_id, year_id, engine_id) VALUES (?, ?, ?)", + (model_id, year_id, engine_id) + ) + conn.commit() + + +def main(): + """Main function to add Aston Martin data.""" + print("Adding Aston Martin data...") + + # Connect to database + conn = connect_db() + + # Insert brand + brand_id = get_or_insert_brand(conn, "ASTON MARTIN") + print(f"Brand 'ASTON MARTIN' inserted with ID: {brand_id}") + + # Define models + models = [ + 'LAGONDA', + 'ZAGATO', + 'DB7', + 'VANTAGE', + 'VIRAGE', + 'VANQUISH', + 'DB9', + 'DBS', + 'RAPIDE', + 'ONE-77', + 'V12 VANTAGE', + 'DB11', + 'DBX', + 'DB12', + 'VALHALLA', + 'VALOUR', + 'ARRA' + ] + + # Insert models + for model_name in models: + model_id = get_or_insert_model(conn, brand_id, model_name) + print(f"Models inserted for ASTON MARTIN: {', '.join(models)}") + + # Define versions (model, year, engine) + versions = [ + # 2025 + ('DB12', 2025, '4.0L V8 Turbocharged'), + ('DBX707', 2025, '4.0L V8 Turbocharged'), + ('VALHALLA', 2025, '4.0L V8 ELECTRIC/GAS Turbocharged'), + ('VANQUISH', 2025, '5.2L V12 Turbocharged'), + ('VANTAGE', 2025, '4.0L V8 Turbocharged'), + ('VANTAGE', 2025, '5.2L V12 Turbocharged'), + + # 2024 + ('DB12', 2024, '4.0L V8 Turbocharged'), + ('DBS', 2024, '5.2L V12 Turbocharged'), + ('DBX', 2024, '4.0L V8 Turbocharged'), + ('DBX707', 2024, '4.0L V8 Turbocharged'), + ('VALOUR', 2024, '5.2L V12 Turbocharged'), + ('VANTAGE', 2024, '4.0L V8 Turbocharged'), + ('VANTAGE', 2024, '5.2L V12 Turbocharged'), + + # 2023 + ('DB11', 2023, '4.0L V8 Turbocharged'), + ('DBS', 2023, '5.2L V12 Turbocharged'), + ('DBX', 2023, '4.0L V8 Turbocharged'), + ('DBX707', 2023, '4.0L V8 Turbocharged'), + ('VANTAGE', 2023, '4.0L V8 Turbocharged'), + ('VANTAGE', 2023, '5.2L V12 Turbocharged'), + + # 2022 + ('DB11', 2022, '4.0L V8 Turbocharged'), + ('DBS', 2022, '5.2L V12 Turbocharged'), + ('DBX', 2022, '4.0L V8 Turbocharged'), + ('VANTAGE', 2022, '4.0L V8 Turbocharged'), + ('VANTAGE', 2022, '5.2L V12 Turbocharged'), + + # 2021 + ('DB11', 2021, '4.0L V8 Turbocharged'), + ('DBS', 2021, '5.2L V12 Turbocharged'), + ('DBX', 2021, '4.0L V8 Turbocharged'), + ('VANTAGE', 2021, '4.0L V8 Turbocharged'), + ('VANTAGE', 2021, '5.2L V12 Turbocharged'), + + # 2020 + ('DB11', 2020, '4.0L V8 Turbocharged'), + ('DBS', 2020, '5.2L V12 Turbocharged'), + ('VANTAGE', 2020, '4.0L V8 Turbocharged'), + ('VANTAGE', 2020, '5.2L V12 Turbocharged'), + + # 2019 + ('DB11', 2019, '4.0L V8 Turbocharged'), + ('DBS', 2019, '5.2L V12 Turbocharged'), + ('RAPIDE', 2019, '4.0L V8 Turbocharged'), + ('VANTAGE', 2019, '4.0L V8 Turbocharged'), + + # 2018 + ('DB11', 2018, '4.0L V8 Turbocharged'), + ('RAPIDE', 2018, '4.0L V8 Turbocharged'), + ('VANTAGE', 2018, '4.0L V8 Turbocharged'), + + # 2017 + ('DB11', 2017, '5.2L V12 Turbocharged'), + ('RAPIDE', 2017, '6.0L V12'), + ('V12 VANTAGE', 2017, '6.0L V12'), + ('V8 VANTAGE', 2017, '4.7L V8'), + ('VANQUISH', 2017, '6.0L V12'), + + # 2016 + ('DB9', 2016, '6.0L V12'), + ('RAPIDE', 2016, '6.0L V12'), + ('V12 VANTAGE', 2016, '6.0L V12'), + ('V8 VANTAGE', 2016, '4.7L V8'), + ('VANQUISH', 2016, '6.0L V12'), + + # 2015 + ('DB9', 2015, '6.0L V12'), + ('RAPIDE', 2015, '6.0L V12'), + ('V12 VANTAGE', 2015, '6.0L V12'), + ('V8 VANTAGE', 2015, '4.7L V8'), + ('VANQUISH', 2015, '6.0L V12'), + + # 2014 + ('DB9', 2014, '6.0L V12'), + ('RAPIDE', 2014, '6.0L V12'), + ('V12 VANTAGE', 2014, '6.0L V12'), + ('V8 VANTAGE', 2014, '4.7L V8'), + ('VANQUISH', 2014, '6.0L V12'), + + # 2013 + ('DB9', 2013, '6.0L V12'), + ('DBS', 2013, '6.0L V12'), + ('RAPIDE', 2013, '6.0L V12'), + ('V12 VANTAGE', 2013, '6.0L V12'), + ('V8 VANTAGE', 2013, '4.7L V8'), + ('VANQUISH', 2013, '6.0L V12'), + + # 2012 + ('DB9', 2012, '6.0L V12'), + ('DBS', 2012, '6.0L V12'), + ('RAPIDE', 2012, '6.0L V12'), + ('V12 VANTAGE', 2012, '6.0L V12'), + ('V8 VANTAGE', 2012, '4.7L V8'), + ('VANQUISH', 2012, '6.0L V12'), + + # 2011 + ('DB9', 2011, '6.0L V12'), + ('DBS', 2011, '6.0L V12'), + ('ONE-77', 2011, '7.3L V12'), + ('RAPIDE', 2011, '6.0L V12'), + ('V12 VANTAGE', 2011, '6.0L V12'), + ('V8 VANTAGE', 2011, '4.7L V8'), + ('VANQUISH', 2011, '6.0L V12'), + + # 2010 + ('DB9', 2010, '6.0L V12'), + ('DBS', 2010, '6.0L V12'), + ('RAPIDE', 2010, '6.0L V12'), + ('V12 VANTAGE', 2010, '6.0L V12'), + ('V8 VANTAGE', 2010, '4.7L V8'), + ('VANQUISH', 2010, '6.0L V12'), + + # 2009 + ('DB9', 2009, '6.0L V12'), + ('DBS', 2009, '6.0L V12'), + ('V12 VANTAGE', 2009, '6.0L V12'), + ('V8 VANTAGE', 2009, '4.3L V8'), + ('V8 VANTAGE', 2009, '4.7L V8'), + ('VANQUISH', 2009, '6.0L V12'), + + # 2008 + ('DB9', 2008, '6.0L V12'), + ('DBS', 2008, '6.0L V12'), + ('V12 VANTAGE', 2008, '6.0L V12'), + ('V8 VANTAGE', 2008, '4.3L V8'), + ('V8 VANTAGE', 2008, '4.7L V8'), + ('VANQUISH', 2008, '6.0L V12'), + + # 2007 + ('DB9', 2007, '6.0L V12'), + ('V12 VANTAGE', 2007, '6.0L V12'), + ('V8 VANTAGE', 2007, '4.3L V8'), + ('VANQUISH', 2007, '6.0L V12'), + + # 2006 + ('DB9', 2006, '6.0L V12'), + ('V12 VANTAGE', 2006, '6.0L V12'), + ('V8 VANTAGE', 2006, '4.3L V8'), + ('VANQUISH', 2006, '6.0L V12'), + + # 2005 + ('DB7', 2005, '5.9L V12'), + ('DB9', 2005, '6.0L V12'), + ('VANQUISH', 2005, '6.0L V12'), + + # 2004 + ('DB7', 2004, '5.9L V12'), + ('DB9', 2004, '6.0L V12'), + ('VANQUISH', 2004, '6.0L V12'), + + # 2003 + ('DB7', 2003, '5.9L V12'), + ('DB9', 2003, '6.0L V12'), + ('VANQUISH', 2003, '6.0L V12'), + + # 2002 + ('DB7', 2002, '5.9L V12'), + ('VANQUISH', 2002, '6.0L V12'), + + # 2001 + ('DB7', 2001, '5.9L V12'), + ('VANQUISH', 2001, '6.0L V12'), + + # 2000 + ('DB7', 2000, '3.2L L6 Supercharged'), + ('DB7', 2000, '5.9L V12'), + ('VANQUISH', 2000, '6.0L V12'), + + # 1999 + ('DB7', 1999, '3.2L L6 Supercharged'), + ('DB7', 1999, '5.9L V12'), + + # 1998 + ('DB7', 1998, '3.2L L6 Supercharged'), + ('DB7', 1998, '5.9L V12'), + + # 1997 + ('DB7', 1997, '3.2L L6 Supercharged'), + ('DB7', 1997, '5.9L V12'), + + # 1995 + ('VIRAGE', 1995, '5.3L V8'), + + # 1994 + ('VIRAGE', 1994, '5.3L V8'), + + # 1993 + ('VIRAGE', 1993, '5.3L V8'), + + # 1992 + ('VIRAGE', 1992, '5.3L V8'), + + # 1991 + ('VIRAGE', 1991, '5.3L V8'), + + # 1990 + ('VIRAGE', 1990, '5.3L V8'), + + # 1989 + ('LAGONDA', 1989, '5.3L V8'), + ('VIRAGE', 1989, '5.3L V8'), + + # 1988 + ('LAGONDA', 1988, '5.3L V8'), + ('VIRAGE', 1988, '5.3L V8'), + ('ZAGATO', 1988, '5.3L V8'), + + # 1987 + ('LAGONDA', 1987, '5.3L V8'), + ('VIRAGE', 1987, '5.3L V8'), + ('ZAGATO', 1987, '5.3L V8'), + + # 1986 + ('LAGONDA', 1986, '5.3L V8'), + ('VIRAGE', 1986, '5.3L V8'), + ('ZAGATO', 1986, '5.3L V8'), + + # 1985 + ('LAGONDA', 1985, '5.3L V8'), + ('VIRAGE', 1985, '5.3L V8'), + ('ZAGATO', 1985, '5.3L V8'), + + # 1984 + ('LAGONDA', 1984, '5.3L V8'), + ('VIRAGE', 1984, '5.3L V8'), + ('ZAGATO', 1984, '5.3L V8'), + + # 1983 + ('LAGONDA', 1983, '5.3L V8'), + ('VIRAGE', 1983, '5.3L V8'), + ('ZAGATO', 1983, '5.3L V8'), + + # 1982 + ('LAGONDA', 1982, '5.3L V8'), + ('VIRAGE', 1982, '5.3L V8'), + ('ZAGATO', 1982, '5.3L V8'), + + # 1981 + ('LAGONDA', 1981, '5.3L V8'), + ('VIRAGE', 1981, '5.3L V8'), + ('ZAGATO', 1981, '5.3L V8'), + + # 1980 + ('LAGONDA', 1980, '5.3L V8'), + ('VIRAGE', 1980, '5.3L V8'), + ('ZAGATO', 1980, '5.3L V8'), + + # 1979 + ('LAGONDA', 1979, '5.3L V8'), + ('VIRAGE', 1979, '5.3L V8'), + ('ZAGATO', 1979, '5.3L V8'), + + # 1978 + ('LAGONDA', 1978, '5.3L V8'), + ('VIRAGE', 1978, '5.3L V8'), + ('ZAGATO', 1978, '5.3L V8'), + + # 1977 + ('LAGONDA', 1977, '5.3L V8'), + ('VIRAGE', 1977, '5.3L V8'), + ('ZAGATO', 1977, '5.3L V8'), + + # 1976 + ('LAGONDA', 1976, '5.3L V8'), + ('VIRAGE', 1976, '5.3L V8'), + ('ZAGATO', 1976, '5.3L V8'), + + # 1975 + ('LAGONDA', 1975, '5.3L V8'), + ('VIRAGE', 1975, '5.3L V8'), + ('ZAGATO', 1975, '5.3L V8'), + + # ARRA (modelo italiano) + ('ARRA', 2024, 'ELECTRIC') + ] + + # Insert version relationships + for model_name, year, engine in versions: + model_id = get_or_insert_model(conn, brand_id, model_name) + year_id = get_or_insert_year(conn, year) + engine_id = get_or_insert_engine(conn, engine) + insert_version_relationship(conn, model_id, year_id, engine_id) + + print(f"Inserted {len(versions)} version relationships for ASTON MARTIN vehicles") + + # Close connection + conn.close() + print("Aston Martin data added successfully!") + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/add_asuna_data.py b/add_asuna_data.py new file mode 100644 index 0000000..8d12b76 --- /dev/null +++ b/add_asuna_data.py @@ -0,0 +1,167 @@ +""" +Script to add Asuna vehicle data to the database. +This includes inserting the brand, models, and year/motor combinations. +""" +import sqlite3 +from typing import List, Tuple + + +def connect_db(db_path: str = "vehicle_database/vehicle_database.db") -> sqlite3.Connection: + """Connect to the vehicle database.""" + conn = sqlite3.connect(db_path) + conn.execute("PRAGMA foreign_keys = ON") # Enable foreign key constraints + return conn + + +def get_or_insert_brand(conn: sqlite3.Connection, brand_name: str) -> int: + """Get brand ID or insert if it doesn't exist.""" + cursor = conn.cursor() + + # Check if brand already exists + cursor.execute("SELECT id FROM brands WHERE name = ?", (brand_name,)) + result = cursor.fetchone() + + if result: + return result[0] # Return existing ID + + # Insert new brand + cursor.execute("INSERT INTO brands (name) VALUES (?)", (brand_name,)) + conn.commit() + return cursor.lastrowid + + +def get_or_insert_model(conn: sqlite3.Connection, brand_id: int, model_name: str) -> int: + """Get model ID or insert if it doesn't exist.""" + cursor = conn.cursor() + + # Check if model already exists for this brand + cursor.execute( + "SELECT id FROM models WHERE brand_id = ? AND name = ?", + (brand_id, model_name) + ) + result = cursor.fetchone() + + if result: + return result[0] # Return existing ID + + # Insert new model + cursor.execute( + "INSERT INTO models (brand_id, name) VALUES (?, ?)", + (brand_id, model_name) + ) + conn.commit() + return cursor.lastrowid + + +def get_or_insert_engine(conn: sqlite3.Connection, engine_name: str) -> int: + """Get engine ID or insert if it doesn't exist.""" + cursor = conn.cursor() + + # Check if engine already exists + cursor.execute("SELECT id FROM engines WHERE name = ?", (engine_name,)) + result = cursor.fetchone() + + if result: + return result[0] # Return existing ID + + # Insert new engine + cursor.execute("INSERT INTO engines (name) VALUES (?)", (engine_name,)) + conn.commit() + return cursor.lastrowid + + +def get_or_insert_year(conn: sqlite3.Connection, year: int) -> int: + """Get year ID or insert if it doesn't exist.""" + cursor = conn.cursor() + + # Check if year already exists + cursor.execute("SELECT id FROM years WHERE year = ?", (year,)) + result = cursor.fetchone() + + if result: + return result[0] # Return existing ID + + # Insert new year + cursor.execute("INSERT INTO years (year) VALUES (?)", (year,)) + conn.commit() + return cursor.lastrowid + + +def insert_version_relationship(conn: sqlite3.Connection, model_id: int, year_id: int, engine_id: int) -> None: + """Insert a version relationship if it doesn't exist.""" + cursor = conn.cursor() + + # Check if version relationship already exists + cursor.execute( + "SELECT id FROM model_year_engine WHERE model_id = ? AND year_id = ? AND engine_id = ?", + (model_id, year_id, engine_id) + ) + result = cursor.fetchone() + + if not result: + # Insert new version relationship + cursor.execute( + "INSERT INTO model_year_engine (model_id, year_id, engine_id) VALUES (?, ?, ?)", + (model_id, year_id, engine_id) + ) + conn.commit() + + +def main(): + """Main function to add Asuna data.""" + print("Adding Asuna data...") + + # Connect to database + conn = connect_db() + + # Insert brand + brand_id = get_or_insert_brand(conn, "ASUNA") + print(f"Brand 'ASUNA' inserted with ID: {brand_id}") + + # Define models + models = [ + 'SUNRUNNER', + 'GT', + 'SE', + 'SUNFIRE' + ] + + # Insert models + for model_name in models: + model_id = get_or_insert_model(conn, brand_id, model_name) + print(f"Models inserted for ASUNA: {', '.join(models)}") + + # Define versions (model, year, engine) + versions = [ + # 1995 + ('SUNRUNNER', 1995, '1.6L L4'), + + # 1994 + ('SUNRUNNER', 1994, '1.6L L4'), + + # 1993 + ('GT', 1993, '1.6L L4'), + ('SE', 1993, '1.6L L4'), + ('SUNFIRE', 1993, '1.8L L4'), + ('SUNRUNNER', 1993, '1.6L L4'), + + # 1992 + ('SUNRUNNER', 1992, '1.6L L4') + ] + + # Insert version relationships + for model_name, year, engine in versions: + model_id = get_or_insert_model(conn, brand_id, model_name) + year_id = get_or_insert_year(conn, year) + engine_id = get_or_insert_engine(conn, engine) + insert_version_relationship(conn, model_id, year_id, engine_id) + + print(f"Inserted {len(versions)} version relationships for ASUNA vehicles") + + # Close connection + conn.close() + print("Asuna data added successfully!") + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/add_audi_data.py b/add_audi_data.py new file mode 100644 index 0000000..bc1900b --- /dev/null +++ b/add_audi_data.py @@ -0,0 +1,372 @@ +""" +Script to add Audi vehicle data to the database. +This includes inserting the brand, models, and year/motor combinations. +""" +import sqlite3 +from typing import List, Tuple + + +def connect_db(db_path: str = "vehicle_database/vehicle_database.db") -> sqlite3.Connection: + """Connect to the vehicle database.""" + conn = sqlite3.connect(db_path) + conn.execute("PRAGMA foreign_keys = ON") # Enable foreign key constraints + return conn + + +def get_or_insert_brand(conn: sqlite3.Connection, brand_name: str) -> int: + """Get brand ID or insert if it doesn't exist.""" + cursor = conn.cursor() + + # Check if brand already exists + cursor.execute("SELECT id FROM brands WHERE name = ?", (brand_name,)) + result = cursor.fetchone() + + if result: + return result[0] # Return existing ID + + # Insert new brand + cursor.execute("INSERT INTO brands (name) VALUES (?)", (brand_name,)) + conn.commit() + return cursor.lastrowid + + +def get_or_insert_model(conn: sqlite3.Connection, brand_id: int, model_name: str) -> int: + """Get model ID or insert if it doesn't exist.""" + cursor = conn.cursor() + + # Check if model already exists for this brand + cursor.execute( + "SELECT id FROM models WHERE brand_id = ? AND name = ?", + (brand_id, model_name) + ) + result = cursor.fetchone() + + if result: + return result[0] # Return existing ID + + # Insert new model + cursor.execute( + "INSERT INTO models (brand_id, name) VALUES (?, ?)", + (brand_id, model_name) + ) + conn.commit() + return cursor.lastrowid + + +def get_or_insert_engine(conn: sqlite3.Connection, engine_name: str) -> int: + """Get engine ID or insert if it doesn't exist.""" + cursor = conn.cursor() + + # Check if engine already exists + cursor.execute("SELECT id FROM engines WHERE name = ?", (engine_name,)) + result = cursor.fetchone() + + if result: + return result[0] # Return existing ID + + # Insert new engine + cursor.execute("INSERT INTO engines (name) VALUES (?)", (engine_name,)) + conn.commit() + return cursor.lastrowid + + +def get_or_insert_year(conn: sqlite3.Connection, year: int) -> int: + """Get year ID or insert if it doesn't exist.""" + cursor = conn.cursor() + + # Check if year already exists + cursor.execute("SELECT id FROM years WHERE year = ?", (year,)) + result = cursor.fetchone() + + if result: + return result[0] # Return existing ID + + # Insert new year + cursor.execute("INSERT INTO years (year) VALUES (?)", (year,)) + conn.commit() + return cursor.lastrowid + + +def insert_version_relationship(conn: sqlite3.Connection, model_id: int, year_id: int, engine_id: int) -> None: + """Insert a version relationship if it doesn't exist.""" + cursor = conn.cursor() + + # Check if version relationship already exists + cursor.execute( + "SELECT id FROM model_year_engine WHERE model_id = ? AND year_id = ? AND engine_id = ?", + (model_id, year_id, engine_id) + ) + result = cursor.fetchone() + + if not result: + # Insert new version relationship + cursor.execute( + "INSERT INTO model_year_engine (model_id, year_id, engine_id) VALUES (?, ?, ?)", + (model_id, year_id, engine_id) + ) + conn.commit() + + +def main(): + """Main function to add Audi data.""" + print("Adding Audi data...") + + # Connect to database + conn = connect_db() + + # Insert brand + brand_id = get_or_insert_brand(conn, "AUDI") + print(f"Brand 'AUDI' inserted with ID: {brand_id}") + + # Define models + models = [ + 'A1', + 'A3', + 'A4', + 'A5', + 'A6', + 'A7', + 'A8', + 'Q2', + 'Q3', + 'Q4', + 'Q5', + 'Q6', + 'Q7', + 'Q8', + 'R8', + 'RS3', + 'RS5', + 'RS6', + 'RS7', + 'S3', + 'S4', + 'S5', + 'S6', + 'S7', + 'S8', + 'SQ5', + 'SQ7', + 'SQ8', + 'TT', + 'E-TRON', + 'RS E-TRON GT', + 'RS Q3', + 'RS Q8' + ] + + # Insert models + for model_name in models: + model_id = get_or_insert_model(conn, brand_id, model_name) + print(f"Models inserted for AUDI: {', '.join(models)}") + + # Define versions (model, year, engine) + versions = [ + # 2026 + ('A3', 2026, '2.0L L4 Turbocharged'), + ('A8', 2026, '3.0L V6 Turbocharged'), + ('Q4', 2026, '2.0L L4 Turbocharged'), + ('Q7', 2026, '2.0L L4 Turbocharged'), + ('Q7', 2026, '3.0L V6 Turbocharged'), + ('Q8', 2026, '3.0L V6 Turbocharged'), + ('RS6', 2026, '4.0L V8 Turbocharged'), + ('RS7', 2026, '4.0L V8 Turbocharged'), + ('RS E-TRON GT', 2026, 'ELECTRIC'), + ('RS Q8', 2026, '4.0L V8 Turbocharged'), + ('S3', 2026, '2.0L L4 Turbocharged'), + ('S4', 2026, '3.0L V6 Turbocharged'), + ('S5', 2026, '3.0L V6 Turbocharged'), + ('S6', 2026, '2.9L V6 Turbocharged'), + ('S7', 2026, '2.9L V6 Turbocharged'), + ('S8', 2026, '4.0L V8 Turbocharged'), + ('SQ5', 2026, '3.0L V6 Turbocharged'), + ('SQ7', 2026, '4.0L V8 Turbocharged'), + ('SQ8', 2026, '4.0L V8 Turbocharged'), + ('TT', 2026, '2.0L L4 Turbocharged'), + ('TT', 2026, '2.5L L5 Turbocharged'), + + # 2025 + ('A1', 2025, '1.5L L4 Turbocharged'), + ('A3', 2025, '1.4L L4 Turbocharged'), + ('A3', 2025, '2.0L L4 Turbocharged'), + ('A4', 2025, '2.0L L4 Turbocharged'), + ('A5', 2025, '2.0L L4 Turbocharged'), + ('A6', 2025, '2.0L L4 Turbocharged'), + ('A6', 2025, '3.0L V6 Turbocharged'), + ('A7', 2025, '2.0L L4 Turbocharged'), + ('A8', 2025, '3.0L V6 Turbocharged'), + ('A8', 2025, '4.0L V8 Turbocharged'), + ('Q2', 2025, '1.4L L4 Turbocharged'), + ('Q3', 2025, '1.4L L4 Turbocharged'), + ('Q3', 2025, '2.0L L4 Turbocharged'), + ('Q4', 2025, '2.0L L4 Turbocharged'), + ('Q5', 2025, '2.0L L4 Turbocharged'), + ('Q5', 2025, '2.0L L4 ELECTRIC/GAS Turbocharged'), + ('Q6', 2025, 'ELECTRIC'), + ('Q7', 2025, '2.0L L4 Turbocharged'), + ('Q7', 2025, '3.0L V6 Turbocharged'), + ('Q8', 2025, '3.0L V6 Turbocharged'), + ('R8', 2025, '5.2L V10'), + ('RS3', 2025, '2.5L L5 Turbocharged'), + ('RS5', 2025, '2.9L V6 Turbocharged'), + ('RS6', 2025, '4.0L V8 Turbocharged'), + ('RS7', 2025, '4.0L V8 Turbocharged'), + ('RS E-TRON GT', 2025, 'ELECTRIC'), + ('RS Q3', 2025, '2.5L L5 Turbocharged'), + ('RS Q8', 2025, '4.0L V8 Turbocharged'), + ('S3', 2025, '2.0L L4 Turbocharged'), + ('S4', 2025, '3.0L V6 Turbocharged'), + ('S5', 2025, '3.0L V6 Turbocharged'), + ('S6', 2025, '2.9L V6 Turbocharged'), + ('S7', 2025, '2.9L V6 Turbocharged'), + ('S8', 2025, '4.0L V8 Turbocharged'), + ('SQ5', 2025, '3.0L V6 Turbocharged'), + ('SQ7', 2025, '4.0L V8 Turbocharged'), + ('SQ8', 2025, '4.0L V8 Turbocharged'), + ('TT', 2025, '2.0L L4 Turbocharged'), + ('TT', 2025, '2.5L L5 Turbocharged'), + ('E-TRON', 2025, 'ELECTRIC'), + + # 2024 + ('A1', 2024, '1.5L L4 Turbocharged'), + ('A3', 2024, '1.4L L4 Turbocharged'), + ('A3', 2024, '2.0L L4 Turbocharged'), + ('A4', 2024, '2.0L L4 Turbocharged'), + ('A5', 2024, '2.0L L4 Turbocharged'), + ('A6', 2024, '2.0L L4 Turbocharged'), + ('A6', 2024, '3.0L V6 Turbocharged'), + ('A7', 2024, '2.0L L4 Turbocharged'), + ('A8', 2024, '3.0L V6 Turbocharged'), + ('A8', 2024, '4.0L V8 Turbocharged'), + ('Q2', 2024, '1.4L L4 Turbocharged'), + ('Q3', 2024, '1.4L L4 Turbocharged'), + ('Q3', 2024, '2.0L L4 Turbocharged'), + ('Q4', 2024, '2.0L L4 Turbocharged'), + ('Q5', 2024, '2.0L L4 Turbocharged'), + ('Q5', 2024, '2.0L L4 ELECTRIC/GAS Turbocharged'), + ('Q6', 2024, 'ELECTRIC'), + ('Q7', 2024, '2.0L L4 Turbocharged'), + ('Q7', 2024, '3.0L V6 Turbocharged'), + ('Q8', 2024, '3.0L V6 Turbocharged'), + ('R8', 2024, '5.2L V10'), + ('RS3', 2024, '2.5L L5 Turbocharged'), + ('RS5', 2024, '2.9L V6 Turbocharged'), + ('RS6', 2024, '4.0L V8 Turbocharged'), + ('RS7', 2024, '4.0L V8 Turbocharged'), + ('RS E-TRON GT', 2024, 'ELECTRIC'), + ('RS Q3', 2024, '2.5L L5 Turbocharged'), + ('RS Q8', 2024, '4.0L V8 Turbocharged'), + ('S3', 2024, '2.0L L4 Turbocharged'), + ('S4', 2024, '3.0L V6 Turbocharged'), + ('S5', 2024, '3.0L V6 Turbocharged'), + ('S6', 2024, '2.9L V6 Turbocharged'), + ('S7', 2024, '2.9L V6 Turbocharged'), + ('S8', 2024, '4.0L V8 Turbocharged'), + ('SQ5', 2024, '3.0L V6 Turbocharged'), + ('SQ7', 2024, '4.0L V8 Turbocharged'), + ('SQ8', 2024, '4.0L V8 Turbocharged'), + ('TT', 2024, '2.0L L4 Turbocharged'), + ('TT', 2024, '2.5L L5 Turbocharged'), + ('E-TRON', 2024, 'ELECTRIC'), + + # 2023 + ('A1', 2023, '1.5L L4 Turbocharged'), + ('A3', 2023, '1.4L L4 Turbocharged'), + ('A3', 2023, '2.0L L4 Turbocharged'), + ('A4', 2023, '2.0L L4 Turbocharged'), + ('A5', 2023, '2.0L L4 Turbocharged'), + ('A6', 2023, '2.0L L4 Turbocharged'), + ('A6', 2023, '3.0L V6 Turbocharged'), + ('A7', 2023, '2.0L L4 Turbocharged'), + ('A8', 2023, '3.0L V6 Turbocharged'), + ('A8', 2023, '4.0L V8 Turbocharged'), + ('Q2', 2023, '1.4L L4 Turbocharged'), + ('Q3', 2023, '1.4L L4 Turbocharged'), + ('Q3', 2023, '2.0L L4 Turbocharged'), + ('Q4', 2023, '2.0L L4 Turbocharged'), + ('Q5', 2023, '2.0L L4 Turbocharged'), + ('Q5', 2023, '2.0L L4 ELECTRIC/GAS Turbocharged'), + ('Q6', 2023, 'ELECTRIC'), + ('Q7', 2023, '2.0L L4 Turbocharged'), + ('Q7', 2023, '3.0L V6 Turbocharged'), + ('Q8', 2023, '3.0L V6 Turbocharged'), + ('R8', 2023, '5.2L V10'), + ('RS3', 2023, '2.5L L5 Turbocharged'), + ('RS5', 2023, '2.9L V6 Turbocharged'), + ('RS6', 2023, '4.0L V8 Turbocharged'), + ('RS7', 2023, '4.0L V8 Turbocharged'), + ('RS E-TRON GT', 2023, 'ELECTRIC'), + ('RS Q3', 2023, '2.5L L5 Turbocharged'), + ('RS Q8', 2023, '4.0L V8 Turbocharged'), + ('S3', 2023, '2.0L L4 Turbocharged'), + ('S4', 2023, '3.0L V6 Turbocharged'), + ('S5', 2023, '3.0L V6 Turbocharged'), + ('S6', 2023, '2.9L V6 Turbocharged'), + ('S7', 2023, '2.9L V6 Turbocharged'), + ('S8', 2023, '4.0L V8 Turbocharged'), + ('SQ5', 2023, '3.0L V6 Turbocharged'), + ('SQ7', 2023, '4.0L V8 Turbocharged'), + ('SQ8', 2023, '4.0L V8 Turbocharged'), + ('TT', 2023, '2.0L L4 Turbocharged'), + ('TT', 2023, '2.5L L5 Turbocharged'), + ('E-TRON', 2023, 'ELECTRIC'), + + # 2022 + ('A1', 2022, '1.5L L4 Turbocharged'), + ('A3', 2022, '1.4L L4 Turbocharged'), + ('A3', 2022, '2.0L L4 Turbocharged'), + ('A4', 2022, '2.0L L4 Turbocharged'), + ('A5', 2022, '2.0L L4 Turbocharged'), + ('A6', 2022, '2.0L L4 Turbocharged'), + ('A6', 2022, '3.0L V6 Turbocharged'), + ('A7', 2022, '2.0L L4 Turbocharged'), + ('A8', 2022, '3.0L V6 Turbocharged'), + ('A8', 2022, '4.0L V8 Turbocharged'), + ('Q2', 2022, '1.4L L4 Turbocharged'), + ('Q3', 2022, '1.4L L4 Turbocharged'), + ('Q3', 2022, '2.0L L4 Turbocharged'), + ('Q4', 2022, '2.0L L4 Turbocharged'), + ('Q5', 2022, '2.0L L4 Turbocharged'), + ('Q5', 2022, '2.0L L4 ELECTRIC/GAS Turbocharged'), + ('Q6', 2022, 'ELECTRIC'), + ('Q7', 2022, '2.0L L4 Turbocharged'), + ('Q7', 2022, '3.0L V6 Turbocharged'), + ('Q8', 2022, '3.0L V6 Turbocharged'), + ('R8', 2022, '5.2L V10'), + ('RS3', 2022, '2.5L L5 Turbocharged'), + ('RS5', 2022, '2.9L V6 Turbocharged'), + ('RS6', 2022, '4.0L V8 Turbocharged'), + ('RS7', 2022, '4.0L V8 Turbocharged'), + ('RS E-TRON GT', 2022, 'ELECTRIC'), + ('RS Q3', 2022, '2.5L L5 Turbocharged'), + ('RS Q8', 2022, '4.0L V8 Turbocharged'), + ('S3', 2022, '2.0L L4 Turbocharged'), + ('S4', 2022, '3.0L V6 Turbocharged'), + ('S5', 2022, '3.0L V6 Turbocharged'), + ('S6', 2022, '2.9L V6 Turbocharged'), + ('S7', 2022, '2.9L V6 Turbocharged'), + ('S8', 2022, '4.0L V8 Turbocharged'), + ('SQ5', 2022, '3.0L V6 Turbocharged'), + ('SQ7', 2022, '4.0L V8 Turbocharged'), + ('SQ8', 2022, '4.0L V8 Turbocharged'), + ('TT', 2022, '2.0L L4 Turbocharged'), + ('TT', 2022, '2.5L L5 Turbocharged'), + ('E-TRON', 2022, 'ELECTRIC') + ] + + # Insert version relationships + for model_name, year, engine in versions: + model_id = get_or_insert_model(conn, brand_id, model_name) + year_id = get_or_insert_year(conn, year) + engine_id = get_or_insert_engine(conn, engine) + insert_version_relationship(conn, model_id, year_id, engine_id) + + print(f"Inserted {len(versions)} version relationships for AUDI vehicles") + + # Close connection + conn.close() + print("Audi data added successfully!") + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/add_audi_data_extended.py b/add_audi_data_extended.py new file mode 100644 index 0000000..2aafdde --- /dev/null +++ b/add_audi_data_extended.py @@ -0,0 +1,379 @@ +""" +Script to add extended Audi vehicle data (2017-2021) to the database. +This includes additional models and year/motor combinations. +""" +import sqlite3 +from typing import List, Tuple + + +def connect_db(db_path: str = "vehicle_database/vehicle_database.db") -> sqlite3.Connection: + """Connect to the vehicle database.""" + conn = sqlite3.connect(db_path) + conn.execute("PRAGMA foreign_keys = ON") # Enable foreign key constraints + return conn + + +def get_or_insert_brand(conn: sqlite3.Connection, brand_name: str) -> int: + """Get brand ID or insert if it doesn't exist.""" + cursor = conn.cursor() + + # Check if brand already exists + cursor.execute("SELECT id FROM brands WHERE name = ?", (brand_name,)) + result = cursor.fetchone() + + if result: + return result[0] # Return existing ID + + # Insert new brand + cursor.execute("INSERT INTO brands (name) VALUES (?)", (brand_name,)) + conn.commit() + return cursor.lastrowid + + +def get_or_insert_model(conn: sqlite3.Connection, brand_id: int, model_name: str) -> int: + """Get model ID or insert if it doesn't exist.""" + cursor = conn.cursor() + + # Check if model already exists for this brand + cursor.execute( + "SELECT id FROM models WHERE brand_id = ? AND name = ?", + (brand_id, model_name) + ) + result = cursor.fetchone() + + if result: + return result[0] # Return existing ID + + # Insert new model + cursor.execute( + "INSERT INTO models (brand_id, name) VALUES (?, ?)", + (brand_id, model_name) + ) + conn.commit() + return cursor.lastrowid + + +def get_or_insert_engine(conn: sqlite3.Connection, engine_name: str) -> int: + """Get engine ID or insert if it doesn't exist.""" + cursor = conn.cursor() + + # Check if engine already exists + cursor.execute("SELECT id FROM engines WHERE name = ?", (engine_name,)) + result = cursor.fetchone() + + if result: + return result[0] # Return existing ID + + # Insert new engine + cursor.execute("INSERT INTO engines (name) VALUES (?)", (engine_name,)) + conn.commit() + return cursor.lastrowid + + +def get_or_insert_year(conn: sqlite3.Connection, year: int) -> int: + """Get year ID or insert if it doesn't exist.""" + cursor = conn.cursor() + + # Check if year already exists + cursor.execute("SELECT id FROM years WHERE year = ?", (year,)) + result = cursor.fetchone() + + if result: + return result[0] # Return existing ID + + # Insert new year + cursor.execute("INSERT INTO years (year) VALUES (?)", (year,)) + conn.commit() + return cursor.lastrowid + + +def insert_version_relationship(conn: sqlite3.Connection, model_id: int, year_id: int, engine_id: int) -> None: + """Insert a version relationship if it doesn't exist.""" + cursor = conn.cursor() + + # Check if version relationship already exists + cursor.execute( + "SELECT id FROM model_year_engine WHERE model_id = ? AND year_id = ? AND engine_id = ?", + (model_id, year_id, engine_id) + ) + result = cursor.fetchone() + + if not result: + # Insert new version relationship + cursor.execute( + "INSERT INTO model_year_engine (model_id, year_id, engine_id) VALUES (?, ?, ?)", + (model_id, year_id, engine_id) + ) + conn.commit() + + +def main(): + """Main function to add extended Audi data.""" + print("Adding extended Audi data (2017-2021)...") + + # Connect to database + conn = connect_db() + + # Get brand ID (should already exist) + brand_id = get_or_insert_brand(conn, "AUDI") + print(f"Brand 'AUDI' has ID: {brand_id}") + + # Define additional models that might need to be added + additional_models = [ + 'A4 ALLROAD', + 'A6 ALLROAD', + 'Q2 QUATTRO' + ] + + # Insert additional models if they don't exist + for model_name in additional_models: + model_id = get_or_insert_model(conn, brand_id, model_name) + + # Define versions (model, year, engine) for years 2017-2021 + versions = [ + # 2021 + ('A1', 2021, '1.5L L4 Turbocharged'), + ('A1', 2021, '2.0L L4 Turbocharged'), + ('A1', 2021, '999cc L3 Turbocharged'), + ('A4', 2021, '2.0L L4 ELECTRIC/GAS Turbocharged'), + ('A4', 2021, '2.0L L4 Turbocharged'), + ('A4 ALLROAD', 2021, '2.0L L4 Turbocharged'), + ('A5', 2021, '2.0L L4 Turbocharged'), + ('A6', 2021, '2.0L L4 Turbocharged'), + ('A6', 2021, '3.0L V6 Turbocharged'), + ('A6 ALLROAD', 2021, '3.0L V6 Turbocharged'), + ('A7', 2021, '2.0L L4 ELECTRIC/GAS Turbocharged'), + ('A7', 2021, '2.0L L4 Turbocharged'), + ('A7', 2021, '3.0L V6 Turbocharged'), + ('A8', 2021, '3.0L V6 ELECTRIC/GAS Turbocharged'), + ('A8', 2021, '3.0L V6 Turbocharged'), + ('A8', 2021, '4.0L V8 Turbocharged'), + ('E-TRON', 2021, 'ELECTRIC'), + ('Q2', 2021, '1.4L L4 Turbocharged'), + ('Q3', 2021, '1.4L L4 Turbocharged'), + ('Q3', 2021, '2.0L L4 Turbocharged'), + ('Q5', 2021, '2.0L L4 ELECTRIC/GAS Turbocharged'), + ('Q5', 2021, '2.0L L4 Turbocharged'), + ('Q7', 2021, '2.0L L4 Turbocharged'), + ('Q7', 2021, '3.0L V6 Turbocharged'), + ('Q8', 2021, '3.0L V6 Turbocharged'), + ('R8', 2021, '5.2L V10'), + ('RS3', 2021, '2.5L L5 Turbocharged'), + ('RS5', 2021, '2.9L V6 Turbocharged'), + ('RS6', 2021, '4.0L V8 Turbocharged'), + ('RS7', 2021, '4.0L V8 Turbocharged'), + ('RS Q3', 2021, '2.5L L5 Turbocharged'), + ('RS Q8', 2021, '4.0L V8 Turbocharged'), + ('S3', 2021, '2.0L L4 Turbocharged'), + ('S4', 2021, '3.0L V6 Turbocharged'), + ('S5', 2021, '3.0L V6 Turbocharged'), + ('S6', 2021, '2.9L V6 Turbocharged'), + ('S7', 2021, '2.9L V6 Turbocharged'), + ('S8', 2021, '4.0L V8 Turbocharged'), + ('SQ5', 2021, '3.0L V6 Turbocharged'), + ('SQ7', 2021, '4.0L V8 Turbocharged'), + ('SQ8', 2021, '4.0L V8 Turbocharged'), + ('TT', 2021, '2.0L L4 Turbocharged'), + ('TT', 2021, '2.5L L5 Turbocharged'), + + # 2020 + ('A1', 2020, '1.5L L4 Turbocharged'), + ('A1', 2020, '2.0L L4 Turbocharged'), + ('A1', 2020, '999cc L3 Turbocharged'), + ('A3', 2020, '1.4L L4 Turbocharged'), + ('A3', 2020, '2.0L L4 Turbocharged'), + ('A4', 2020, '2.0L L4 Turbocharged'), + ('A4 ALLROAD', 2020, '2.0L L4 Turbocharged'), + ('A5', 2020, '2.0L L4 Turbocharged'), + ('A6', 2020, '2.0L L4 Turbocharged'), + ('A6', 2020, '3.0L V6 Turbocharged'), + ('A6 ALLROAD', 2020, '3.0L V6 Turbocharged'), + ('A7', 2020, '3.0L V6 Turbocharged'), + ('A8', 2020, '3.0L V6 ELECTRIC/GAS Turbocharged'), + ('A8', 2020, '3.0L V6 Turbocharged'), + ('A8', 2020, '4.0L V8 Turbocharged'), + ('E-TRON', 2020, 'ELECTRIC'), + ('Q2', 2020, '1.4L L4 Turbocharged'), + ('Q2 QUATTRO', 2020, '2.0L L4 Turbocharged'), + ('Q3', 2020, '1.4L L4 Turbocharged'), + ('Q3', 2020, '2.0L L4 Turbocharged'), + ('Q5', 2020, '2.0L L4 ELECTRIC/GAS Turbocharged'), + ('Q5', 2020, '2.0L L4 Turbocharged'), + ('Q7', 2020, '2.0L L4 Turbocharged'), + ('Q7', 2020, '3.0L V6 Turbocharged'), + ('Q8', 2020, '3.0L V6 Turbocharged'), + ('R8', 2020, '5.2L V10'), + ('RS3', 2020, '2.5L L5 Turbocharged'), + ('RS5', 2020, '2.9L V6 Turbocharged'), + ('RS6', 2020, '4.0L V8 Turbocharged'), + ('RS7', 2020, '4.0L V8 Turbocharged'), + ('RS Q3', 2020, '2.5L L5 Turbocharged'), + ('RS Q8', 2020, '4.0L V8 Turbocharged'), + ('S3', 2020, '2.0L L4 Turbocharged'), + ('S4', 2020, '3.0L V6 Turbocharged'), + ('S5', 2020, '3.0L V6 Turbocharged'), + ('S6', 2020, '2.9L V6 Turbocharged'), + ('S7', 2020, '2.9L V6 Turbocharged'), + ('S8', 2020, '4.0L V8 Turbocharged'), + ('SQ5', 2020, '3.0L V6 Turbocharged'), + ('SQ7', 2020, '4.0L V8 Turbocharged'), + ('SQ8', 2020, '4.0L V8 Turbocharged'), + ('TT', 2020, '2.0L L4 Turbocharged'), + ('TT', 2020, '2.5L L5 Turbocharged'), + + # 2019 + ('A3', 2019, '1.4L L4 Turbocharged'), + ('A3', 2019, '2.0L L4 Turbocharged'), + ('A4', 2019, '2.0L L4 Turbocharged'), + ('A4 ALLROAD', 2019, '2.0L L4 Turbocharged'), + ('A5', 2019, '2.0L L4 Turbocharged'), + ('A6', 2019, '2.0L L4 Turbocharged'), + ('A6', 2019, '3.0L V6 Turbocharged'), + ('A7', 2019, '3.0L V6 Turbocharged'), + ('A8', 2019, '3.0L V6 Turbocharged'), + ('A8', 2019, '4.0L V8 Turbocharged'), + ('E-TRON', 2019, 'ELECTRIC'), + ('Q2', 2019, '1.4L L4 Turbocharged'), + ('Q2', 2019, '2.0L L4 Turbocharged'), + ('Q3', 2019, '1.4L L4 Turbocharged'), + ('Q3', 2019, '2.0L L4 Turbocharged'), + ('Q5', 2019, '2.0L L4 Turbocharged'), + ('Q7', 2019, '2.0L L4 Turbocharged'), + ('Q7', 2019, '3.0L V6 DIESEL Turbocharged'), + ('Q7', 2019, '3.0L V6 Supercharged'), + ('Q8', 2019, '3.0L V6 Turbocharged'), + ('R8', 2019, '5.2L V10'), + ('RS3', 2019, '2.5L L5 Turbocharged'), + ('RS5', 2019, '2.9L V6 Turbocharged'), + ('RS6', 2019, '4.0L V8 Turbocharged'), + ('RS7', 2019, '4.0L V8 Turbocharged'), + ('RS Q3', 2019, '2.5L L5 Turbocharged'), + ('RS Q8', 2019, '4.0L V8 Turbocharged'), + ('S3', 2019, '2.0L L4 Turbocharged'), + ('S4', 2019, '3.0L V6 Turbocharged'), + ('S5', 2019, '3.0L V6 Turbocharged'), + ('S6', 2019, '2.9L V6 Turbocharged'), + ('S7', 2019, '2.9L V6 Turbocharged'), + ('S8', 2019, '4.0L V8 Turbocharged'), + ('SQ5', 2019, '3.0L V6 Turbocharged'), + ('SQ7', 2019, '4.0L V8 Turbocharged'), + ('SQ8', 2019, '4.0L V8 Turbocharged'), + ('TT', 2019, '2.0L L4 Turbocharged'), + ('TT', 2019, '2.5L L5 Turbocharged'), + + # 2018 + ('A1', 2018, '1.4L L4 Turbocharged'), + ('A1', 2018, '1.8L L4 Turbocharged'), + ('A3', 2018, '1.4L L4 ELECTRIC/GAS Turbocharged'), + ('A3', 2018, '1.4L L4 Turbocharged'), + ('A3', 2018, '1.8L L4 Turbocharged'), + ('A3', 2018, '2.0L L4 Turbocharged'), + ('A4', 2018, '2.0L L4 Turbocharged'), + ('A4 ALLROAD', 2018, '2.0L L4 Turbocharged'), + ('A5', 2018, '2.0L L4 Turbocharged'), + ('A6', 2018, '1.8L L4 Turbocharged'), + ('A6', 2018, '2.0L L4 Turbocharged'), + ('A6', 2018, '3.0L V6 Supercharged'), + ('A7', 2018, '2.0L L4 Turbocharged'), + ('A7', 2018, '3.0L V6 Supercharged'), + ('A8', 2018, '3.0L V6 Supercharged'), + ('A8', 2018, '4.0L V8 Turbocharged'), + ('A8', 2018, '6.3L W12'), + ('Q2', 2018, '1.4L L4 Turbocharged'), + ('Q2', 2018, '2.0L L4 Turbocharged'), + ('Q3', 2018, '1.4L L4 Turbocharged'), + ('Q3', 2018, '2.0L L4 Turbocharged'), + ('Q5', 2018, '2.0L L4 Turbocharged'), + ('Q7', 2018, '2.0L L4 Turbocharged'), + ('Q7', 2018, '3.0L V6 DIESEL Turbocharged'), + ('Q7', 2018, '3.0L V6 Supercharged'), + ('Q8', 2018, '3.0L V6 Turbocharged'), + ('R8', 2018, '5.2L V10'), + ('RS3', 2018, '2.5L L5 Turbocharged'), + ('RS5', 2018, '2.9L V6 Turbocharged'), + ('RS6', 2018, '4.0L V8 Turbocharged'), + ('RS7', 2018, '4.0L V8 Turbocharged'), + ('RS Q3', 2018, '2.5L L5 Turbocharged'), + ('RS Q8', 2018, '4.0L V8 Turbocharged'), + ('S3', 2018, '2.0L L4 Turbocharged'), + ('S4', 2018, '3.0L V6 Supercharged'), + ('S4', 2018, '3.0L V6 Turbocharged'), + ('S5', 2018, '3.0L V6 Supercharged'), + ('S5', 2018, '3.0L V6 Turbocharged'), + ('S6', 2018, '4.0L V8 Turbocharged'), + ('S7', 2018, '4.0L V8 Turbocharged'), + ('S8', 2018, '4.0L V8 Turbocharged'), + ('SQ5', 2018, '3.0L V6 Supercharged'), + ('SQ5', 2018, '3.0L V6 Turbocharged'), + ('SQ7', 2018, '4.0L V8 Turbocharged'), + ('SQ8', 2018, '4.0L V8 Turbocharged'), + ('TT', 2018, '1.8L L4 Turbocharged'), + ('TT', 2018, '2.0L L4 Turbocharged'), + ('TT', 2018, '2.5L L5 Turbocharged'), + + # 2017 + ('A1', 2017, '1.4L L4 Turbocharged'), + ('A1', 2017, '1.8L L4 Turbocharged'), + ('A3', 2017, '1.4L L4 ELECTRIC/GAS Turbocharged'), + ('A3', 2017, '1.4L L4 Turbocharged'), + ('A3', 2017, '2.0L L4 Turbocharged'), + ('A4', 2017, '2.0L L4 Turbocharged'), + ('A4 ALLROAD', 2017, '2.0L L4 Turbocharged'), + ('A5', 2017, '2.0L L4 Turbocharged'), + ('A6', 2017, '1.8L L4 Turbocharged'), + ('A6', 2017, '2.0L L4 Turbocharged'), + ('A6', 2017, '3.0L V6 Supercharged'), + ('A7', 2017, '2.0L L4 Turbocharged'), + ('A7', 2017, '3.0L V6 Supercharged'), + ('A8', 2017, '3.0L V6 Supercharged'), + ('A8', 2017, '4.0L V8 Turbocharged'), + ('A8', 2017, '6.3L W12'), + ('Q3', 2017, '1.4L L4 Turbocharged'), + ('Q3', 2017, '2.0L L4 Turbocharged'), + ('Q5', 2017, '2.0L L4 Turbocharged'), + ('Q5', 2017, '3.0L V6 DIESEL Turbocharged'), + ('Q5', 2017, '3.0L V6 Supercharged'), + ('Q7', 2017, '2.0L L4 Turbocharged'), + ('Q7', 2017, '3.0L V6 DIESEL Turbocharged'), + ('Q7', 2017, '3.0L V6 Supercharged'), + ('Q8', 2017, '3.0L V6 Turbocharged'), + ('R8', 2017, '5.2L V10'), + ('RS3', 2017, '2.5L L5 Turbocharged'), + ('RS5', 2017, '2.9L V6 Turbocharged'), + ('RS6', 2017, '4.0L V8 Turbocharged'), + ('RS7', 2017, '4.0L V8 Turbocharged'), + ('RS Q3', 2017, '2.5L L5 Turbocharged'), + ('RS Q8', 2017, '4.0L V8 Turbocharged'), + ('S3', 2017, '2.0L L4 Turbocharged'), + ('S4', 2017, '3.0L V6 Supercharged'), + ('S4', 2017, '3.0L V6 Turbocharged'), + ('S5', 2017, '3.0L V6 Supercharged'), + ('S5', 2017, '3.0L V6 Turbocharged'), + ('S6', 2017, '4.0L V8 Turbocharged'), + ('S7', 2017, '4.0L V8 Turbocharged'), + ('S8', 2017, '4.0L V8 Turbocharged'), + ('SQ5', 2017, '3.0L V6 Supercharged'), + ('SQ5', 2017, '3.0L V6 Turbocharged'), + ('SQ7', 2017, '4.0L V8 Turbocharged'), + ('SQ8', 2017, '4.0L V8 Turbocharged'), + ('TT', 2017, '1.8L L4 Turbocharged'), + ('TT', 2017, '2.0L L4 Turbocharged'), + ('TT', 2017, '2.5L L5 Turbocharged') + ] + + # Insert version relationships + for model_name, year, engine in versions: + model_id = get_or_insert_model(conn, brand_id, model_name) + year_id = get_or_insert_year(conn, year) + engine_id = get_or_insert_engine(conn, engine) + insert_version_relationship(conn, model_id, year_id, engine_id) + + print(f"Inserted {len(versions)} additional version relationships for AUDI vehicles (2017-2021)") + + # Close connection + conn.close() + print("Extended Audi data added successfully!") + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/add_audi_data_extended2.py b/add_audi_data_extended2.py new file mode 100644 index 0000000..2c61a58 --- /dev/null +++ b/add_audi_data_extended2.py @@ -0,0 +1,367 @@ +""" +Script to add extended Audi vehicle data (2012-2016) to the database. +This includes additional models and year/motor combinations. +""" +import sqlite3 +from typing import List, Tuple + + +def connect_db(db_path: str = "vehicle_database/vehicle_database.db") -> sqlite3.Connection: + """Connect to the vehicle database.""" + conn = sqlite3.connect(db_path) + conn.execute("PRAGMA foreign_keys = ON") # Enable foreign key constraints + return conn + + +def get_or_insert_brand(conn: sqlite3.Connection, brand_name: str) -> int: + """Get brand ID or insert if it doesn't exist.""" + cursor = conn.cursor() + + # Check if brand already exists + cursor.execute("SELECT id FROM brands WHERE name = ?", (brand_name,)) + result = cursor.fetchone() + + if result: + return result[0] # Return existing ID + + # Insert new brand + cursor.execute("INSERT INTO brands (name) VALUES (?)", (brand_name,)) + conn.commit() + return cursor.lastrowid + + +def get_or_insert_model(conn: sqlite3.Connection, brand_id: int, model_name: str) -> int: + """Get model ID or insert if it doesn't exist.""" + cursor = conn.cursor() + + # Check if model already exists for this brand + cursor.execute( + "SELECT id FROM models WHERE brand_id = ? AND name = ?", + (brand_id, model_name) + ) + result = cursor.fetchone() + + if result: + return result[0] # Return existing ID + + # Insert new model + cursor.execute( + "INSERT INTO models (brand_id, name) VALUES (?, ?)", + (brand_id, model_name) + ) + conn.commit() + return cursor.lastrowid + + +def get_or_insert_engine(conn: sqlite3.Connection, engine_name: str) -> int: + """Get engine ID or insert if it doesn't exist.""" + cursor = conn.cursor() + + # Check if engine already exists + cursor.execute("SELECT id FROM engines WHERE name = ?", (engine_name,)) + result = cursor.fetchone() + + if result: + return result[0] # Return existing ID + + # Insert new engine + cursor.execute("INSERT INTO engines (name) VALUES (?)", (engine_name,)) + conn.commit() + return cursor.lastrowid + + +def get_or_insert_year(conn: sqlite3.Connection, year: int) -> int: + """Get year ID or insert if it doesn't exist.""" + cursor = conn.cursor() + + # Check if year already exists + cursor.execute("SELECT id FROM years WHERE year = ?", (year,)) + result = cursor.fetchone() + + if result: + return result[0] # Return existing ID + + # Insert new year + cursor.execute("INSERT INTO years (year) VALUES (?)", (year,)) + conn.commit() + return cursor.lastrowid + + +def insert_version_relationship(conn: sqlite3.Connection, model_id: int, year_id: int, engine_id: int) -> None: + """Insert a version relationship if it doesn't exist.""" + cursor = conn.cursor() + + # Check if version relationship already exists + cursor.execute( + "SELECT id FROM model_year_engine WHERE model_id = ? AND year_id = ? AND engine_id = ?", + (model_id, year_id, engine_id) + ) + result = cursor.fetchone() + + if not result: + # Insert new version relationship + cursor.execute( + "INSERT INTO model_year_engine (model_id, year_id, engine_id) VALUES (?, ?, ?)", + (model_id, year_id, engine_id) + ) + conn.commit() + + +def main(): + """Main function to add extended Audi data (2012-2016).""" + print("Adding extended Audi data (2012-2016)...") + + # Connect to database + conn = connect_db() + + # Get brand ID (should already exist) + brand_id = get_or_insert_brand(conn, "AUDI") + print(f"Brand 'AUDI' has ID: {brand_id}") + + # Define additional models that might need to be added + additional_models = [ + 'ALLROAD' + ] + + # Insert additional models if they don't exist + for model_name in additional_models: + model_id = get_or_insert_model(conn, brand_id, model_name) + + # Define versions (model, year, engine) for years 2012-2016 + versions = [ + # 2016 + ('A1', 2016, '1.4L L4 Turbocharged'), + ('A1', 2016, '1.8L L4 Turbocharged'), + ('A3', 2016, '1.4L L4 ELECTRIC/GAS Turbocharged'), + ('A3', 2016, '1.4L L4 Turbocharged'), + ('A3', 2016, '1.8L L4 Turbocharged'), + ('A3', 2016, '2.0L L4 DIESEL Turbocharged'), + ('A3', 2016, '2.0L L4 Turbocharged'), + ('A4', 2016, '1.8L L4 Turbocharged'), + ('A4', 2016, '2.0L L4 Turbocharged'), + ('A4 ALLROAD', 2016, '2.0L L4 Turbocharged'), + ('A5', 2016, '2.0L L4 Turbocharged'), + ('A5', 2016, '3.0L V6 Supercharged'), + ('A6', 2016, '1.8L L4 Turbocharged'), + ('A6', 2016, '2.0L L4 Turbocharged'), + ('A6', 2016, '3.0L V6 DIESEL Turbocharged'), + ('A6', 2016, '3.0L V6 Supercharged'), + ('A7', 2016, '2.0L L4 Turbocharged'), + ('A7', 2016, '3.0L V6 DIESEL Turbocharged'), + ('A7', 2016, '3.0L V6 Supercharged'), + ('A8', 2016, '3.0L V6 DIESEL Turbocharged'), + ('A8', 2016, '3.0L V6 Supercharged'), + ('A8', 2016, '4.0L V8 Turbocharged'), + ('A8', 2016, '6.3L W12 Turbocharged'), + ('ALLROAD', 2016, '2.0L L4 Turbocharged'), + ('Q3', 2016, '1.4L L4 Turbocharged'), + ('Q3', 2016, '2.0L L4 Turbocharged'), + ('Q5', 2016, '2.0L L4 ELECTRIC/GAS Turbocharged'), + ('Q5', 2016, '2.0L L4 Turbocharged'), + ('Q5', 2016, '3.0L V6 DIESEL Turbocharged'), + ('Q7', 2016, '2.0L L4 Turbocharged'), + ('Q7', 2016, '3.0L V6 DIESEL Turbocharged'), + ('Q7', 2016, '3.0L V6 Supercharged'), + ('Q8', 2016, '3.0L V6 Turbocharged'), + ('R8', 2016, '4.2L V8'), + ('R8', 2016, '5.2L V10'), + ('RS5', 2016, '4.2L V8'), + ('RS7', 2016, '4.0L V8 Turbocharged'), + ('S3', 2016, '2.0L L4 Turbocharged'), + ('S4', 2016, '3.0L V6 Supercharged'), + ('S5', 2016, '3.0L V6 Supercharged'), + ('S6', 2016, '4.0L V8 Turbocharged'), + ('S7', 2016, '4.0L V8 Turbocharged'), + ('S8', 2016, '4.0L V8 Turbocharged'), + ('SQ5', 2016, '3.0L V6 Supercharged'), + ('TT', 2016, '1.8L L4 Turbocharged'), + ('TT', 2016, '2.0L L4 Turbocharged'), + + # 2015 + ('A1', 2015, '1.4L L4 Turbocharged'), + ('A3', 2015, '1.4L L4 Turbocharged'), + ('A3', 2015, '1.8L L4 Turbocharged'), + ('A3', 2015, '2.0L L4 DIESEL Turbocharged'), + ('A3', 2015, '2.0L L4 Turbocharged'), + ('A4', 2015, '1.8L L4 Turbocharged'), + ('A4', 2015, '2.0L L4 Turbocharged'), + ('A4 ALLROAD', 2015, '2.0L L4 Turbocharged'), + ('A5', 2015, '1.8L L4 Turbocharged'), + ('A5', 2015, '2.0L L4 Turbocharged'), + ('A5', 2015, '3.0L V6 Supercharged'), + ('A6', 2015, '2.0L L4 Turbocharged'), + ('A6', 2015, '3.0L V6 DIESEL Turbocharged'), + ('A6', 2015, '3.0L V6 Supercharged'), + ('A7', 2015, '2.0L L4 Turbocharged'), + ('A7', 2015, '3.0L V6 DIESEL Turbocharged'), + ('A7', 2015, '3.0L V6 Supercharged'), + ('A8', 2015, '3.0L V6 DIESEL Turbocharged'), + ('A8', 2015, '3.0L V6 Supercharged'), + ('A8', 2015, '4.0L V8 Turbocharged'), + ('A8', 2015, '6.3L W12'), + ('ALLROAD', 2015, '2.0L L4 Turbocharged'), + ('Q3', 2015, '2.0L L4 Turbocharged'), + ('Q5', 2015, '2.0L L4 ELECTRIC/GAS Turbocharged'), + ('Q5', 2015, '2.0L L4 Turbocharged'), + ('Q5', 2015, '3.0L V6 DIESEL Turbocharged'), + ('Q7', 2015, '2.0L L4 Turbocharged'), + ('Q7', 2015, '3.0L V6 DIESEL Turbocharged'), + ('Q7', 2015, '3.0L V6 Supercharged'), + ('Q8', 2015, '3.0L V6 Turbocharged'), + ('R8', 2015, '4.2L V8'), + ('R8', 2015, '5.2L V10'), + ('RS5', 2015, '4.2L V8'), + ('RS7', 2015, '4.0L V8 Turbocharged'), + ('S3', 2015, '2.0L L4 Turbocharged'), + ('S4', 2015, '3.0L V6 Supercharged'), + ('S5', 2015, '3.0L V6 Supercharged'), + ('S6', 2015, '4.0L V8 Turbocharged'), + ('S7', 2015, '4.0L V8 Turbocharged'), + ('S8', 2015, '4.0L V8 Turbocharged'), + ('SQ5', 2015, '3.0L V6 Supercharged'), + ('TT', 2015, '1.8L L4 Turbocharged'), + ('TT', 2015, '2.0L L4 Turbocharged'), + + # 2014 + ('A1', 2014, '1.4L L4 Turbocharged'), + ('A3', 2014, '1.4L L4 Turbocharged'), + ('A3', 2014, '1.8L L4 Turbocharged'), + ('A4', 2014, '1.8L L4 Turbocharged'), + ('A4', 2014, '2.0L L4 Turbocharged'), + ('A4 ALLROAD', 2014, '2.0L L4 Turbocharged'), + ('A5', 2014, '1.8L L4 Turbocharged'), + ('A5', 2014, '2.0L L4 Turbocharged'), + ('A5', 2014, '3.0L V6 Supercharged'), + ('A6', 2014, '2.0L L4 Turbocharged'), + ('A6', 2014, '3.0L V6 DIESEL Turbocharged'), + ('A6', 2014, '3.0L V6 Supercharged'), + ('A7', 2014, '2.0L L4 Turbocharged'), + ('A7', 2014, '3.0L V6 DIESEL Turbocharged'), + ('A7', 2014, '3.0L V6 Supercharged'), + ('A8', 2014, '3.0L V6 DIESEL Turbocharged'), + ('A8', 2014, '3.0L V6 Supercharged'), + ('A8', 2014, '4.0L V8 Turbocharged'), + ('A8', 2014, '6.3L W12'), + ('ALLROAD', 2014, '2.0L L4 Turbocharged'), + ('Q3', 2014, '2.0L L4 Turbocharged'), + ('Q5', 2014, '2.0L L4 Turbocharged'), + ('Q5', 2014, '3.0L V6 DIESEL Turbocharged'), + ('Q7', 2014, '2.0L L4 Turbocharged'), + ('Q7', 2014, '3.0L V6 DIESEL Turbocharged'), + ('Q7', 2014, '3.0L V6 Supercharged'), + ('Q8', 2014, '3.0L V6 Turbocharged'), + ('R8', 2014, '4.2L V8'), + ('R8', 2014, '5.2L V10'), + ('RS5', 2014, '4.2L V8'), + ('RS7', 2014, '4.0L V8 Turbocharged'), + ('S3', 2014, '2.0L L4 Turbocharged'), + ('S4', 2014, '3.0L V6 Supercharged'), + ('S5', 2014, '3.0L V6 Supercharged'), + ('S6', 2014, '4.0L V8 Turbocharged'), + ('S7', 2014, '4.0L V8 Turbocharged'), + ('S8', 2014, '4.0L V8 Turbocharged'), + ('SQ5', 2014, '3.0L V6 Supercharged'), + ('TT', 2014, '1.8L L4 Turbocharged'), + ('TT', 2014, '2.0L L4 Turbocharged'), + + # 2013 + ('A1', 2013, '1.4L L4 Turbocharged'), + ('A3', 2013, '1.4L L4 Turbocharged'), + ('A3', 2013, '1.8L L4 Turbocharged'), + ('A3', 2013, '2.0L L4 DIESEL Turbocharged'), + ('A3', 2013, '2.0L L4 Turbocharged'), + ('A4', 2013, '1.8L L4 Turbocharged'), + ('A4', 2013, '2.0L L4 Turbocharged'), + ('A5', 2013, '1.8L L4 Turbocharged'), + ('A5', 2013, '2.0L L4 Turbocharged'), + ('A5', 2013, '3.0L V6 Supercharged'), + ('A6', 2013, '2.0L L4 Turbocharged'), + ('A6', 2013, '2.8L V6'), + ('A6', 2013, '3.0L V6 DIESEL Turbocharged'), + ('A6', 2013, '3.0L V6 Supercharged'), + ('A7', 2013, '3.0L V6 Supercharged'), + ('A8', 2013, '3.0L V6 Supercharged'), + ('A8', 2013, '4.0L V8 Turbocharged'), + ('A8', 2013, '6.3L W12'), + ('ALLROAD', 2013, '2.0L L4 Turbocharged'), + ('Q3', 2013, '2.0L L4 Turbocharged'), + ('Q5', 2013, '2.0L L4 ELECTRIC/GAS Turbocharged'), + ('Q5', 2013, '2.0L L4 Turbocharged'), + ('Q5', 2013, '3.0L V6 DIESEL Turbocharged'), + ('Q7', 2013, '2.0L L4 Turbocharged'), + ('Q7', 2013, '3.0L V6 DIESEL Turbocharged'), + ('Q7', 2013, '3.0L V6 Supercharged'), + ('Q8', 2013, '3.0L V6 Turbocharged'), + ('R8', 2013, '4.2L V8'), + ('R8', 2013, '5.2L V10'), + ('RS5', 2013, '4.2L V8'), + ('RS7', 2013, '4.0L V8 Turbocharged'), + ('S3', 2013, '2.0L L4 Turbocharged'), + ('S4', 2013, '3.0L V6 Supercharged'), + ('S5', 2013, '3.0L V6 Supercharged'), + ('S6', 2013, '4.0L V8 Turbocharged'), + ('S7', 2013, '4.0L V8 Turbocharged'), + ('S8', 2013, '4.0L V8 Turbocharged'), + ('SQ5', 2013, '3.0L V6 Supercharged'), + ('TT', 2013, '1.8L L4 Turbocharged'), + ('TT', 2013, '2.0L L4 Turbocharged'), + ('TT', 2013, '2.5L L5 Turbocharged'), + + # 2012 + ('A1', 2012, '1.4L L4 Turbocharged'), + ('A3', 2012, '1.4L L4 Turbocharged'), + ('A3', 2012, '1.8L L4 Turbocharged'), + ('A3', 2012, '2.0L L4 DIESEL Turbocharged'), + ('A3', 2012, '2.0L L4 Turbocharged'), + ('A4', 2012, '1.8L L4 Turbocharged'), + ('A4', 2012, '2.0L L4 Turbocharged'), + ('A5', 2012, '1.8L L4 Turbocharged'), + ('A5', 2012, '2.0L L4 Turbocharged'), + ('A5', 2012, '3.0L V6 Supercharged'), + ('A6', 2012, '2.0L L4 Turbocharged'), + ('A6', 2012, '2.8L V6'), + ('A6', 2012, '3.0L V6 DIESEL Turbocharged'), + ('A6', 2012, '3.0L V6 Supercharged'), + ('A7', 2012, '3.0L V6 Supercharged'), + ('A8', 2012, '3.0L V6 Supercharged'), + ('A8', 2012, '4.0L V8 Turbocharged'), + ('A8', 2012, '6.3L W12'), + ('Q3', 2012, '2.0L L4 Turbocharged'), + ('Q5', 2012, '2.0L L4 Turbocharged'), + ('Q5', 2012, '3.0L V6 DIESEL Turbocharged'), + ('Q7', 2012, '2.0L L4 Turbocharged'), + ('Q7', 2012, '3.0L V6 DIESEL Turbocharged'), + ('Q7', 2012, '3.0L V6 Supercharged'), + ('Q8', 2012, '3.0L V6 Turbocharged'), + ('R8', 2012, '4.2L V8'), + ('R8', 2012, '5.2L V10'), + ('RS5', 2012, '4.2L V8'), + ('RS7', 2012, '4.0L V8 Turbocharged'), + ('S3', 2012, '2.0L L4 Turbocharged'), + ('S4', 2012, '3.0L V6 Supercharged'), + ('S5', 2012, '3.0L V6 Supercharged'), + ('S6', 2012, '4.0L V8 Turbocharged'), + ('S7', 2012, '4.0L V8 Turbocharged'), + ('S8', 2012, '4.0L V8 Turbocharged'), + ('SQ5', 2012, '3.0L V6 Supercharged'), + ('TT', 2012, '1.8L L4 Turbocharged'), + ('TT', 2012, '2.0L L4 Turbocharged'), + ('TT', 2012, '2.5L L5 Turbocharged') + ] + + # Insert version relationships + for model_name, year, engine in versions: + model_id = get_or_insert_model(conn, brand_id, model_name) + year_id = get_or_insert_year(conn, year) + engine_id = get_or_insert_engine(conn, engine) + insert_version_relationship(conn, model_id, year_id, engine_id) + + print(f"Inserted {len(versions)} additional version relationships for AUDI vehicles (2012-2016)") + + # Close connection + conn.close() + print("Extended Audi data (2012-2016) added successfully!") + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/add_crosley_data.py b/add_crosley_data.py new file mode 100644 index 0000000..494a2d4 --- /dev/null +++ b/add_crosley_data.py @@ -0,0 +1,177 @@ +#!/usr/bin/env python3 +""" +Script para agregar datos de Crosley a la base de datos de vehículos +""" + +import sqlite3 +import os + +def add_crosley_data(): + # Verificar que la base de datos exista + db_path = "vehicle_database/vehicle_database.db" + if not os.path.exists(db_path): + print(f"Error: Base de datos no encontrada en {db_path}") + return + + conn = sqlite3.connect(db_path) + cursor = conn.cursor() + + print("Agregando datos de Crosley a la base de datos...") + + try: + # Insertar la marca Crosley + cursor.execute("INSERT OR IGNORE INTO brands (name) VALUES (?)", ("CROSLEY",)) + cursor.execute("SELECT id FROM brands WHERE name = ?", ("CROSLEY",)) + brand_id = cursor.fetchone()[0] + print(f"Marca CROSLEY tiene ID: {brand_id}") + + # Insertar modelos de Crosley + models = [ + 'FARM-O-ROAD', 'HOT SHOT', 'PANEL DELIVERY', 'PICKUP', + 'STANDARD', 'SUPER', 'SUPER SPORTS', 'CROSLEY' # Agregué 'CROSLEY' porque aparece en los datos + ] + + for model in models: + cursor.execute( + "INSERT OR IGNORE INTO models (brand_id, name) VALUES (?, ?)", + (brand_id, model) + ) + print(f"Agregados {len(models)} modelos de Crosley") + + # Datos de años y motores para cada modelo + crosley_data = [ + # 1952 + ('FARM-O-ROAD', 1952, '724cc 44cid L4'), + ('HOT SHOT', 1952, '724cc 44cid L4'), + ('PANEL DELIVERY', 1952, '724cc 44cid L4'), + ('PICKUP', 1952, '724cc 44cid L4'), + ('STANDARD', 1952, '724cc 44cid L4'), + ('SUPER', 1952, '0.7L 44cid L4'), + ('SUPER SHOT', 1952, '0.7L 44cid L4'), + ('SUPER SPORTS', 1952, '0.7L 44cid L4'), + + # 1951 + ('FARM-O-ROAD', 1951, '724cc 44cid L4'), + ('HOT SHOT', 1951, '724cc 44cid L4'), + ('PANEL DELIVERY', 1951, '724cc 44cid L4'), + ('PICKUP', 1951, '724cc 44cid L4'), + ('STANDARD', 1951, '724cc 44cid L4'), + ('SUPER', 1951, '0.7L 44cid L4'), + ('SUPER SPORTS', 1951, '0.7L 44cid L4'), + + # 1950 + ('CROSLEY', 1950, '724cc 44cid L4'), + ('FARM-O-ROAD', 1950, '724cc 44cid L4'), + ('HOT SHOT', 1950, '724cc 44cid L4'), + ('PANEL DELIVERY', 1950, '724cc 44cid L4'), + ('PICKUP', 1950, '724cc 44cid L4'), + ('STANDARD', 1950, '724cc 44cid L4'), + ('SUPER', 1950, '0.7L 44cid L4'), + + # 1949 + ('CROSLEY', 1949, '724cc 44cid L4'), + ('HOT SHOT', 1949, '724cc 44cid L4'), + ('PANEL DELIVERY', 1949, '724cc 44cid L4'), + ('PICKUP', 1949, '724cc 44cid L4'), + + # 1948 + ('CROSLEY', 1948, '724cc 44cid L4'), + ('PANEL DELIVERY', 1948, '724cc 44cid L4'), + ('PICKUP', 1948, '724cc 44cid L4'), + + # 1947 + ('CROSLEY', 1947, '724cc 44cid L4'), + ('PICKUP', 1947, '724cc 44cid L4'), + + # 1946 + ('CROSLEY', 1946, '724cc 44cid L4'), + + # 1942 + ('CROSLEY', 1942, '724cc 44cid L4'), + + # 1941 + ('CROSLEY', 1941, '579cc 35cid L2'), + + # 1940 + ('CROSLEY', 1940, '579cc 35cid L2'), + + # 1939 + ('CROSLEY', 1939, '638cc 39cid L2') + ] + + # Insertar años + years = list(set([data[1] for data in crosley_data])) # Obtener años únicos + for year in years: + cursor.execute("INSERT OR IGNORE INTO years (year) VALUES (?)", (year,)) + print(f"Agregados {len(years)} años") + + # Insertar motores + engines = list(set([data[2] for data in crosley_data])) # Obtener motores únicos + for engine in engines: + cursor.execute("INSERT OR IGNORE INTO engines (name) VALUES (?)", (engine,)) + print(f"Agregados {len(engines)} motores") + + # Crear combinaciones modelo-año-motor + for model_name, year, engine_name in crosley_data: + # Obtener IDs + cursor.execute("SELECT id FROM models WHERE brand_id = ? AND name = ?", (brand_id, model_name)) + model_result = cursor.fetchone() + if model_result: + model_id = model_result[0] + else: + print(f"Advertencia: Modelo {model_name} no encontrado para la marca CROSLEY") + continue + + cursor.execute("SELECT id FROM years WHERE year = ?", (year,)) + year_result = cursor.fetchone() + if year_result: + year_id = year_result[0] + else: + print(f"Advertencia: Año {year} no encontrado") + continue + + cursor.execute("SELECT id FROM engines WHERE name = ?", (engine_name,)) + engine_result = cursor.fetchone() + if engine_result: + engine_id = engine_result[0] + else: + print(f"Advertencia: Motor {engine_name} no encontrado") + continue + + # Insertar la combinación modelo-año-motor + cursor.execute( + """INSERT OR REPLACE INTO model_year_engine + (model_id, year_id, engine_id) VALUES (?, ?, ?)""", + (model_id, year_id, engine_id) + ) + + conn.commit() + print(f"Se agregaron {len(crosley_data)} combinaciones modelo-año-motor para CROSLEY") + print("Datos de CROSLEY agregados exitosamente a la base de datos!") + + # Mostrar algunos resultados + print("\nAlgunos vehículos CROSLEY agregados:") + cursor.execute(""" + SELECT b.name, m.name, y.year, e.name + FROM model_year_engine mye + JOIN models m ON mye.model_id = m.id + JOIN brands b ON m.brand_id = b.id + JOIN years y ON mye.year_id = y.id + JOIN engines e ON mye.engine_id = e.id + WHERE b.name = 'CROSLEY' + ORDER BY y.year DESC, m.name + LIMIT 15 + """) + + results = cursor.fetchall() + for brand, model, year, engine in results: + print(f" {year} {brand} {model} - {engine}") + + except Exception as e: + print(f"Error al agregar datos de CROSLEY: {e}") + conn.rollback() + finally: + conn.close() + +if __name__ == "__main__": + add_crosley_data() \ No newline at end of file diff --git a/add_historical_data.py b/add_historical_data.py new file mode 100644 index 0000000..ffb6a7e --- /dev/null +++ b/add_historical_data.py @@ -0,0 +1,255 @@ +#!/usr/bin/env python3 +""" +Script para agregar datos históricos de varias marcas a la base de datos de vehículos +""" + +import sqlite3 +import os + +def add_historical_data(): + # Verificar que la base de datos exista + db_path = "vehicle_database/vehicle_database.db" + if not os.path.exists(db_path): + print(f"Error: Base de datos no encontrada en {db_path}") + return + + conn = sqlite3.connect(db_path) + cursor = conn.cursor() + + print("Agregando datos históricos de varias marcas a la base de datos...") + + try: + # Insertar marcas + brands = ['CONTINENTAL', 'CORD', 'COLE', 'CLEVELAND', 'CISITALIA', 'CHIREY'] + for brand in brands: + cursor.execute("INSERT OR IGNORE INTO brands (name) VALUES (?)", (brand,)) + + print(f"Agregadas {len(brands)} marcas históricas") + + # Insertar modelos por marca + # CONTINENTAL + continental_models = ['BEACON', 'ACE', 'FLYER'] + for model in continental_models: + cursor.execute("SELECT id FROM brands WHERE name = ?", ("CONTINENTAL",)) + brand_id = cursor.fetchone()[0] + cursor.execute( + "INSERT OR IGNORE INTO models (brand_id, name) VALUES (?, ?)", + (brand_id, model) + ) + + # CORD + cord_models = ['L-29', '810', '812'] + for model in cord_models: + cursor.execute("SELECT id FROM brands WHERE name = ?", ("CORD",)) + brand_id = cursor.fetchone()[0] + cursor.execute( + "INSERT OR IGNORE INTO models (brand_id, name) VALUES (?, ?)", + (brand_id, model) + ) + + # COLE + cole_models = ['SERIES 890', 'AERO EIGHT', 'SERIES 870', '8-50', '4-40', '6-50', '6-60', + 'FOUR', 'SIX', 'FIFTY', 'FORTY', 'SIXTY', 'MODEL 30'] + for model in cole_models: + cursor.execute("SELECT id FROM brands WHERE name = ?", ("COLE",)) + brand_id = cursor.fetchone()[0] + cursor.execute( + "INSERT OR IGNORE INTO models (brand_id, name) VALUES (?, ?)", + (brand_id, model) + ) + + # CLEVELAND + cleveland_models = ['MODEL 31', 'MODEL 43', 'MODEL 42', 'MODEL 41', 'MODEL 40'] + for model in cleveland_models: + cursor.execute("SELECT id FROM brands WHERE name = ?", ("CLEVELAND",)) + brand_id = cursor.fetchone()[0] + cursor.execute( + "INSERT OR IGNORE INTO models (brand_id, name) VALUES (?, ?)", + (brand_id, model) + ) + + # CISITALIA + cisitalia_models = ['202'] + for model in cisitalia_models: + cursor.execute("SELECT id FROM brands WHERE name = ?", ("CISITALIA",)) + brand_id = cursor.fetchone()[0] + cursor.execute( + "INSERT OR IGNORE INTO models (brand_id, name) VALUES (?, ?)", + (brand_id, model) + ) + + # CHIREY + chirey_models = ['ARRIZO 8', 'TIGGO 4PRO', 'TIGGO 7PRO', 'TIGGO 8PRO', 'OMODA 5'] + for model in chirey_models: + cursor.execute("SELECT id FROM brands WHERE name = ?", ("CHIREY",)) + brand_id = cursor.fetchone()[0] + cursor.execute( + "INSERT OR IGNORE INTO models (brand_id, name) VALUES (?, ?)", + (brand_id, model) + ) + + print("Agregados modelos para todas las marcas") + + # Datos históricos de años y motores para cada marca + historical_data = [ + # CONTINENTAL + ('CONTINENTAL', 'BEACON', 1934, '4cyl'), + ('CONTINENTAL', 'ACE', 1933, '6cyl'), + ('CONTINENTAL', 'BEACON', 1933, '4cyl'), + ('CONTINENTAL', 'FLYER', 1933, '6cyl'), + + # CORD + ('CORD', 'L-29', 1933, '4.7L 289cid V8'), + ('CORD', '812', 1936, '4.7L 289cid V8'), + ('CORD', '810', 1936, '4.7L 289cid V8'), + ('CORD', '812', 1936, '8cyl'), + ('CORD', 'L-29', 1932, '4.9L 298cid L8'), + ('CORD', 'L-29', 1931, '4.9L 298cid L8'), + ('CORD', 'L-29', 1930, '4.9L 298cid L8'), + ('CORD', 'L-29', 1929, '8cyl'), + + # COLE + ('COLE', 'SERIES 890', 1925, '8cyl'), + ('COLE', 'SERIES 890', 1924, '8cyl'), + ('COLE', 'SERIES 890', 1923, '8cyl'), + ('COLE', 'AERO EIGHT', 1922, '8cyl'), + ('COLE', 'AERO EIGHT', 1921, '8cyl'), + ('COLE', 'AERO EIGHT', 1920, '8cyl'), + ('COLE', 'SERIES 870', 1919, '8cyl'), + ('COLE', 'SERIES 870', 1918, '8cyl'), + ('COLE', '8-50', 1916, '8cyl'), + ('COLE', '4-40', 1915, '4cyl'), + ('COLE', '6-50', 1915, '6cyl'), + ('COLE', '6-60', 1915, '6cyl'), + ('COLE', 'FOUR', 1914, '4cyl'), + ('COLE', 'SIX', 1914, '6cyl'), + ('COLE', 'FIFTY', 1913, '4cyl'), + ('COLE', 'FORTY', 1913, '4cyl'), + ('COLE', 'SIXTY', 1913, '6cyl'), + ('COLE', 'FORTY', 1912, '4cyl'), + ('COLE', 'MODEL 30', 1911, '4cyl'), + ('COLE', 'MODEL 30', 1910, '4cyl'), + + # CLEVELAND + ('CLEVELAND', 'MODEL 31', 1926, '6cyl'), + ('CLEVELAND', 'MODEL 43', 1926, '6cyl'), + ('CLEVELAND', 'MODEL 31', 1925, '6cyl'), + ('CLEVELAND', 'MODEL 43', 1925, '6cyl'), + ('CLEVELAND', 'MODEL 42', 1924, '6cyl'), + ('CLEVELAND', 'MODEL 42', 1923, '6cyl'), + ('CLEVELAND', 'MODEL 41', 1922, '6cyl'), + ('CLEVELAND', 'MODEL 41', 1921, '6cyl'), + ('CLEVELAND', 'MODEL 40', 1920, '6cyl'), + ('CLEVELAND', 'MODEL 40', 1919, '6cyl'), + + # CISITALIA + ('CISITALIA', '202', 1952, '1.1L L4'), + ('CISITALIA', '202', 1951, '1.1L L4'), + ('CISITALIA', '202', 1950, '1.1L L4'), + ('CISITALIA', '202', 1949, '1.1L 66cid L4'), + ('CISITALIA', '202', 1948, '1.1L 66cid L4'), + ('CISITALIA', '202', 1947, '1.1L 66cid L4'), + + # CHIREY + ('CHIREY', 'ARRIZO 8', 2025, '1.6L L4 Turbocharged'), + ('CHIREY', 'TIGGO 4PRO', 2025, '1.5L L4 Turbocharged'), + ('CHIREY', 'TIGGO 7PRO', 2025, '1.5L L4 Turbocharged'), + ('CHIREY', 'TIGGO 8PRO', 2025, '1.6L L4 Turbocharged'), + ('CHIREY', 'ARRIZO 8', 2024, '1.6L L4 Turbocharged'), + ('CHIREY', 'TIGGO 4PRO', 2024, '1.5L L4 Turbocharged'), + ('CHIREY', 'TIGGO 7PRO', 2024, '1.5L L4 ELECTRIC/GAS Turbocharged'), + ('CHIREY', 'TIGGO 7PRO', 2024, '1.5L L4 Turbocharged'), + ('CHIREY', 'TIGGO 8PRO', 2024, '1.6L L4 Turbocharged'), + ('CHIREY', 'TIGGO 8PRO', 2024, '1.5L L4 ELECTRIC/GAS Turbocharged'), + ('CHIREY', 'TIGGO 8PRO', 2024, '2.0L L4 Turbocharged'), + ('CHIREY', 'OMODA 5', 2023, '1.5L L4'), + ('CHIREY', 'TIGGO 4PRO', 2023, '1.5L L4'), + ('CHIREY', 'TIGGO 7PRO', 2023, '1.5L L4'), + ('CHIREY', 'TIGGO 8PRO', 2023, '1.5L L4 ELECTRIC/GAS Turbocharged'), + ('CHIREY', 'TIGGO 8PRO', 2023, '1.6L L4 Turbocharged'), + ('CHIREY', 'TIGGO 8PRO', 2023, '2.0L L4 Turbocharged') + ] + + # Insertar años + years = list(set([data[2] for data in historical_data])) # Obtener años únicos + for year in years: + cursor.execute("INSERT OR IGNORE INTO years (year) VALUES (?)", (year,)) + print(f"Agregados {len(years)} años históricos") + + # Insertar motores + engines = list(set([data[3] for data in historical_data])) # Obtener motores únicos + for engine in engines: + cursor.execute("INSERT OR IGNORE INTO engines (name) VALUES (?)", (engine,)) + print(f"Agregados {len(engines)} motores históricos") + + # Crear combinaciones modelo-año-motor + for brand_name, model_name, year, engine_name in historical_data: + # Obtener IDs + cursor.execute("SELECT id FROM brands WHERE name = ?", (brand_name,)) + brand_result = cursor.fetchone() + if brand_result: + brand_id = brand_result[0] + else: + print(f"Advertencia: Marca {brand_name} no encontrada") + continue + + cursor.execute("SELECT id FROM models WHERE brand_id = ? AND name = ?", (brand_id, model_name)) + model_result = cursor.fetchone() + if model_result: + model_id = model_result[0] + else: + print(f"Advertencia: Modelo {model_name} no encontrado para la marca {brand_name}") + continue + + cursor.execute("SELECT id FROM years WHERE year = ?", (year,)) + year_result = cursor.fetchone() + if year_result: + year_id = year_result[0] + else: + print(f"Advertencia: Año {year} no encontrado") + continue + + cursor.execute("SELECT id FROM engines WHERE name = ?", (engine_name,)) + engine_result = cursor.fetchone() + if engine_result: + engine_id = engine_result[0] + else: + print(f"Advertencia: Motor {engine_name} no encontrado") + continue + + # Insertar la combinación modelo-año-motor + cursor.execute( + """INSERT OR REPLACE INTO model_year_engine + (model_id, year_id, engine_id) VALUES (?, ?, ?)""", + (model_id, year_id, engine_id) + ) + + conn.commit() + print(f"Se agregaron {len(historical_data)} combinaciones modelo-año-motor históricas") + print("Datos históricos agregados exitosamente a la base de datos!") + + # Mostrar algunos resultados + print("\nAlgunos vehículos históricos agregados:") + cursor.execute(""" + SELECT b.name, m.name, y.year, e.name + FROM model_year_engine mye + JOIN models m ON mye.model_id = m.id + JOIN brands b ON m.brand_id = b.id + JOIN years y ON mye.year_id = y.id + JOIN engines e ON mye.engine_id = e.id + ORDER BY y.year DESC + LIMIT 20 + """) + + results = cursor.fetchall() + for brand, model, year, engine in results: + print(f" {year} {brand} {model} - {engine}") + + except Exception as e: + print(f"Error al agregar datos históricos: {e}") + conn.rollback() + finally: + conn.close() + +if __name__ == "__main__": + add_historical_data() \ No newline at end of file diff --git a/check_and_remove_brands.py b/check_and_remove_brands.py new file mode 100644 index 0000000..f42438a --- /dev/null +++ b/check_and_remove_brands.py @@ -0,0 +1,127 @@ +#!/usr/bin/env python3 +""" +Script para eliminar marcas específicas y limpiar marcas sin vehículos +""" + +import sqlite3 +import os + +def remove_brands_and_cleanup(): + # Verificar que la base de datos exista + db_path = "vehicle_database/vehicle_database.db" + if not os.path.exists(db_path): + print(f"Error: Base de datos no encontrada en {db_path}") + return + + conn = sqlite3.connect(db_path) + cursor = conn.cursor() + + print("Verificando marcas en la base de datos...") + + # Mostrar todas las marcas + cursor.execute("SELECT id, name FROM brands ORDER BY name") + all_brands = cursor.fetchall() + print("Todas las marcas en la base de datos:") + for brand_id, brand_name in all_brands: + print(f" ID: {brand_id}, Nombre: '{brand_name}'") + + # Marcas a eliminar (ahora con las mayúsculas exactas según lo que vimos en el resultado anterior) + brands_to_remove = ['Ford', 'Toyota', 'Nissan', 'Honda', 'Chevrolet'] + + print(f"\nMarcas a eliminar: {', '.join(brands_to_remove)}") + + try: + # Contar cuántos vehículos tenemos para estas marcas antes de eliminar + placeholders = ','.join(['?' for _ in brands_to_remove]) + cursor.execute(f""" + SELECT COUNT(*) + FROM model_year_engine mye + JOIN models m ON mye.model_id = m.id + JOIN brands b ON m.brand_id = b.id + WHERE b.name IN ({placeholders}) + """, brands_to_remove) + count_to_delete = cursor.fetchone()[0] + + print(f"Se eliminarán {count_to_delete} vehículos de las marcas especificadas") + + if count_to_delete > 0: + print("Procediendo con la eliminación...") + + # Eliminar las combinaciones modelo-año-motor para los modelos de las marcas especificadas + cursor.execute(f""" + DELETE FROM model_year_engine + WHERE model_id IN ( + SELECT m.id FROM models m + JOIN brands b ON m.brand_id = b.id + WHERE b.name IN ({placeholders}) + ) + """, brands_to_remove) + + # Eliminar los modelos de las marcas especificadas + cursor.execute(f""" + DELETE FROM models + WHERE brand_id IN ( + SELECT id FROM brands WHERE name IN ({placeholders}) + ) + """, brands_to_remove) + + # Eliminar las marcas especificadas + cursor.execute(f""" + DELETE FROM brands + WHERE name IN ({placeholders}) + """, brands_to_remove) + + # Eliminar marcas que no tienen modelos asociados + print("Eliminando marcas sin vehículos...") + cursor.execute(""" + DELETE FROM brands + WHERE id NOT IN ( + SELECT DISTINCT brand_id FROM models + ) + """) + + conn.commit() + + # Mostrar un resumen de la base de datos actualizada + print("\nResumen de la base de datos actualizada:") + + # Contar vehículos restantes + cursor.execute(""" + SELECT COUNT(*) + FROM model_year_engine mye + JOIN models m ON mye.model_id = m.id + JOIN brands b ON m.brand_id = b.id + """) + total_vehicles = cursor.fetchone()[0] + print(f"Total de vehículos restantes: {total_vehicles}") + + # Contar marcas restantes + cursor.execute("SELECT COUNT(*) FROM brands") + total_brands = cursor.fetchone()[0] + print(f"Total de marcas restantes: {total_brands}") + + # Mostrar marcas restantes + cursor.execute("SELECT name FROM brands ORDER BY name") + remaining_brands = cursor.fetchall() + print(f"Marcas restantes: {[brand[0] for brand in remaining_brands]}") + + # Mostrar rango de años actual + cursor.execute(""" + SELECT MIN(y.year), MAX(y.year) + FROM years y + JOIN model_year_engine mye ON y.id = mye.year_id + """) + min_year, max_year = cursor.fetchone() + if min_year and max_year: + print(f"Rango de años actual: {min_year} - {max_year}") + else: + print("No hay años con vehículos registrados") + + except Exception as e: + print(f"Error al eliminar marcas y limpiar la base de datos: {e}") + conn.rollback() + finally: + conn.close() + +if __name__ == "__main__": + remove_brands_and_cleanup() \ No newline at end of file diff --git a/dashboard/README.md b/dashboard/README.md new file mode 100644 index 0000000..f4aea11 --- /dev/null +++ b/dashboard/README.md @@ -0,0 +1,79 @@ +# Vehicle Database Dashboard + +A web-based dashboard for searching and filtering vehicle data from your database. + +## Features + +- Filter vehicles by brand, model, year, and engine +- Responsive web interface with Bootstrap +- Real-time filtering and search +- Detailed vehicle information display +- Modern UI with cards and badges + +## Prerequisites + +- Python 3.x +- Flask (installed via `sudo apt-get install python3-flask`) +- SQLite database with vehicle data (created in the vehicle_database directory) + +## Setup + +1. Make sure you have the vehicle database created in the `../vehicle_database/vehicle_database.db` path +2. Install Flask: `sudo apt-get install python3-flask` +3. Run the dashboard server: `python3 server.py` + +## Usage + +1. Start the server: + ```bash + cd dashboard + python3 server.py + ``` + +2. Open your web browser and navigate to `http://localhost:5000` + +3. Use the filters on the left panel to search for vehicles: + - Select a brand from the dropdown + - Select a model (based on the selected brand) + - Select a year + - Select an engine type + - Click "Search Vehicles" to apply filters + +4. The results will appear in the right panel with detailed information + +## API Endpoints + +The dashboard uses the following API endpoints: + +- `GET /api/brands` - Get all vehicle brands +- `GET /api/models?brand=[brand]` - Get models for a specific brand +- `GET /api/years` - Get all years +- `GET /api/engines` - Get all engines +- `GET /api/vehicles?[filters]` - Search vehicles with optional filters + +## File Structure + +``` +dashboard/ +├── index.html # Main dashboard page +├── dashboard.js # Frontend JavaScript +├── server.py # Flask backend +├── requirements.txt # Python dependencies +├── start_dashboard.sh # Startup script +└── README.md # This file +``` + +## Customization + +You can customize the dashboard by: + +- Modifying the CSS styles in index.html +- Adding more filters in the JavaScript +- Changing the layout in index.html +- Adding more vehicle details in the display + +## Troubleshooting + +- If the server won't start, make sure the vehicle database exists +- If filters don't populate, check that the database has data +- If the page doesn't load, verify that Flask is installed correctly \ No newline at end of file diff --git a/dashboard/dashboard.js b/dashboard/dashboard.js new file mode 100644 index 0000000..787ec35 --- /dev/null +++ b/dashboard/dashboard.js @@ -0,0 +1,440 @@ +// Vehicle Dashboard JavaScript - Navegación por tarjetas +class VehicleDashboard { + constructor() { + this.currentView = 'brands'; // brands, models, vehicles + this.selectedBrand = null; + this.selectedModel = null; + this.allVehicles = []; + this.filteredVehicles = []; + this.stats = { brands: 0, models: 0, vehicles: 0 }; + this.init(); + } + + async init() { + await this.loadStats(); + await this.showBrands(); + this.bindFilterEvents(); + } + + async loadStats() { + try { + const [brandsRes, vehiclesRes] = await Promise.all([ + fetch('/api/brands'), + fetch('/api/vehicles') + ]); + + if (brandsRes.ok && vehiclesRes.ok) { + const brands = await brandsRes.json(); + const vehicles = await vehiclesRes.json(); + + // Contar modelos únicos + const uniqueModels = new Set(vehicles.map(v => `${v.brand}-${v.model}`)); + + this.stats.brands = brands.length; + this.stats.models = uniqueModels.size; + this.stats.vehicles = vehicles.length; + + document.getElementById('totalBrands').textContent = this.stats.brands; + document.getElementById('totalModels').textContent = this.stats.models; + document.getElementById('totalVehicles').textContent = this.stats.vehicles; + } + } catch (error) { + console.error('Error loading stats:', error); + } + } + + updateBreadcrumb() { + const breadcrumb = document.getElementById('breadcrumb'); + let html = ''; + + if (this.currentView === 'brands') { + html = ``; + } else if (this.currentView === 'models') { + html = ` + + + `; + } else if (this.currentView === 'vehicles') { + html = ` + + + + `; + } + + breadcrumb.innerHTML = html; + } + + async showBrands() { + this.currentView = 'brands'; + this.selectedBrand = null; + this.selectedModel = null; + this.updateBreadcrumb(); + this.hideFilters(); + + const container = document.getElementById('mainContent'); + container.innerHTML = ` +
+ +

Cargando marcas...

+
+ `; + + try { + const [brandsRes, vehiclesRes] = await Promise.all([ + fetch('/api/brands'), + fetch('/api/vehicles') + ]); + + if (!brandsRes.ok || !vehiclesRes.ok) { + throw new Error('Error al cargar datos'); + } + + const brands = await brandsRes.json(); + const vehicles = await vehiclesRes.json(); + + // Contar modelos y vehículos por marca + const brandStats = {}; + brands.forEach(brand => { + brandStats[brand] = { models: new Set(), vehicles: 0 }; + }); + + vehicles.forEach(v => { + if (brandStats[v.brand]) { + brandStats[v.brand].models.add(v.model); + brandStats[v.brand].vehicles++; + } + }); + + if (brands.length === 0) { + container.innerHTML = ` +
+ +

No hay marcas disponibles

+

Agrega algunas marcas a la base de datos

+
+ `; + return; + } + + container.innerHTML = `
+ ${brands.map(brand => ` +
+
+ +
+
${brand}
+
+ ${brandStats[brand].models.size} modelos +
+
+ ${brandStats[brand].vehicles} vehículos +
+
+ `).join('')} +
`; + + } catch (error) { + console.error('Error:', error); + container.innerHTML = ` +
+ +

Error al cargar marcas

+

${error.message}

+
+ `; + } + } + + async goToModels(brand) { + this.currentView = 'models'; + this.selectedBrand = brand; + this.selectedModel = null; + this.updateBreadcrumb(); + this.hideFilters(); + + const container = document.getElementById('mainContent'); + container.innerHTML = ` +
+ +

Cargando modelos de ${brand}...

+
+ `; + + try { + const [modelsRes, vehiclesRes] = await Promise.all([ + fetch(`/api/models?brand=${encodeURIComponent(brand)}`), + fetch(`/api/vehicles?brand=${encodeURIComponent(brand)}`) + ]); + + if (!modelsRes.ok || !vehiclesRes.ok) { + throw new Error('Error al cargar datos'); + } + + const models = await modelsRes.json(); + const vehicles = await vehiclesRes.json(); + + // Contar vehículos y años por modelo + const modelStats = {}; + models.forEach(model => { + modelStats[model] = { years: new Set(), vehicles: 0, engines: new Set() }; + }); + + vehicles.forEach(v => { + if (modelStats[v.model]) { + modelStats[v.model].years.add(v.year); + modelStats[v.model].vehicles++; + modelStats[v.model].engines.add(v.engine); + } + }); + + if (models.length === 0) { + container.innerHTML = ` +
+ +

No hay modelos para ${brand}

+

Esta marca no tiene modelos registrados

+ +
+ `; + return; + } + + container.innerHTML = `
+ ${models.map(model => { + const stats = modelStats[model]; + const yearsArray = Array.from(stats.years).sort((a, b) => b - a); + const yearRange = yearsArray.length > 0 + ? (yearsArray.length > 1 + ? `${yearsArray[yearsArray.length - 1]} - ${yearsArray[0]}` + : `${yearsArray[0]}`) + : 'N/A'; + + return ` +
+
${model}
+
+ ${yearRange} +
+
+ ${stats.engines.size} motores +
+
+ ${stats.vehicles} variantes +
+
+ `; + }).join('')} +
`; + + } catch (error) { + console.error('Error:', error); + container.innerHTML = ` +
+ +

Error al cargar modelos

+

${error.message}

+ +
+ `; + } + } + + async goToVehicles(brand, model) { + this.currentView = 'vehicles'; + this.selectedBrand = brand; + this.selectedModel = model; + this.updateBreadcrumb(); + this.showFilters(); + + const container = document.getElementById('mainContent'); + container.innerHTML = ` +
+ +

Cargando vehículos...

+
+ `; + + try { + const response = await fetch( + `/api/vehicles?brand=${encodeURIComponent(brand)}&model=${encodeURIComponent(model)}` + ); + + if (!response.ok) { + throw new Error('Error al cargar vehículos'); + } + + this.allVehicles = await response.json(); + this.filteredVehicles = [...this.allVehicles]; + + // Poblar filtros + await this.populateFilters(brand, model); + + this.displayVehicles(); + + } catch (error) { + console.error('Error:', error); + container.innerHTML = ` +
+ +

Error al cargar vehículos

+

${error.message}

+ +
+ `; + } + } + + async populateFilters(brand, model) { + try { + const [yearsRes, enginesRes] = await Promise.all([ + fetch(`/api/years?brand=${encodeURIComponent(brand)}&model=${encodeURIComponent(model)}`), + fetch(`/api/engines?brand=${encodeURIComponent(brand)}&model=${encodeURIComponent(model)}`) + ]); + + if (yearsRes.ok) { + const years = await yearsRes.json(); + const yearFilter = document.getElementById('yearFilter'); + yearFilter.innerHTML = ''; + years.forEach(year => { + yearFilter.innerHTML += ``; + }); + } + + if (enginesRes.ok) { + const engines = await enginesRes.json(); + const engineFilter = document.getElementById('engineFilter'); + engineFilter.innerHTML = ''; + engines.forEach(engine => { + engineFilter.innerHTML += ``; + }); + } + + } catch (error) { + console.error('Error populating filters:', error); + } + } + + bindFilterEvents() { + document.getElementById('yearFilter').addEventListener('change', () => { + this.applyFilters(); + }); + + document.getElementById('engineFilter').addEventListener('change', () => { + this.applyFilters(); + }); + } + + applyFilters() { + const year = document.getElementById('yearFilter').value; + const engine = document.getElementById('engineFilter').value; + + this.filteredVehicles = this.allVehicles.filter(v => { + return (!year || v.year.toString() === year) && + (!engine || v.engine === engine); + }); + + this.displayVehicles(); + } + + displayVehicles() { + const container = document.getElementById('mainContent'); + const resultCount = document.getElementById('resultCount'); + + resultCount.textContent = `${this.filteredVehicles.length} resultado${this.filteredVehicles.length !== 1 ? 's' : ''}`; + + if (this.filteredVehicles.length === 0) { + container.innerHTML = ` +
+ +

No se encontraron vehículos

+

Intenta ajustar los filtros

+
+ `; + return; + } + + container.innerHTML = `
+ ${this.filteredVehicles.map(v => ` +
+
+
${v.year} ${v.brand} ${v.model}
+
${v.engine}
+
+
+
+
+ +
${v.fuel_type || 'N/A'}
+
+
+ +
${v.power_hp || 0} HP
+
+
+ +
${v.transmission || 'N/A'}
+
+
+ +
${v.drivetrain || 'N/A'}
+
+
+ +
${v.cylinders || 0} cil.
+
+
+ +
${v.displacement_cc || 0} cc
+
+
+ ${v.trim_level && v.trim_level !== 'unknown' ? ` +
+ ${v.trim_level} +
+ ` : ''} +
+
+ `).join('')} +
`; + } + + goToBrands() { + this.showBrands(); + } + + showFilters() { + document.getElementById('filtersBar').style.display = 'block'; + // Reset filters + document.getElementById('yearFilter').value = ''; + document.getElementById('engineFilter').value = ''; + } + + hideFilters() { + document.getElementById('filtersBar').style.display = 'none'; + } +} + +// Initialize dashboard globally +let dashboard; +document.addEventListener('DOMContentLoaded', () => { + dashboard = new VehicleDashboard(); +}); diff --git a/dashboard/index.html b/dashboard/index.html new file mode 100644 index 0000000..a4849da --- /dev/null +++ b/dashboard/index.html @@ -0,0 +1,367 @@ + + + + + + Base de Datos de Vehículos + + + + + +
+
+
+
+

Base de Datos de Vehículos

+

Explora vehículos por marca, modelo y especificaciones

+
+
+
+
+
0
+
Marcas
+
+
+
0
+
Modelos
+
+
+
0
+
Vehículos
+
+
+
+
+
+
+ +
+ + + + + + + +
+
+ +

Cargando...

+
+
+
+ + + + + diff --git a/dashboard/requirements.txt b/dashboard/requirements.txt new file mode 100644 index 0000000..cc35792 --- /dev/null +++ b/dashboard/requirements.txt @@ -0,0 +1 @@ +Flask==2.3.3 \ No newline at end of file diff --git a/dashboard/server.py b/dashboard/server.py new file mode 100644 index 0000000..f8414af --- /dev/null +++ b/dashboard/server.py @@ -0,0 +1,248 @@ +from flask import Flask, render_template, jsonify, request, send_from_directory +import sqlite3 +import os + +app = Flask(__name__, static_folder='.') + +# Database path - adjust as needed +DATABASE_PATH = '../vehicle_database/vehicle_database.db' + +def get_db_connection(): + """Get a connection to the vehicle database""" + conn = sqlite3.connect(DATABASE_PATH) + conn.row_factory = sqlite3.Row # This enables column access by name + return conn + +def get_all_brands(): + """Get all unique brands from the database""" + conn = get_db_connection() + cursor = conn.cursor() + cursor.execute("SELECT DISTINCT name FROM brands ORDER BY name") + brands = [row['name'] for row in cursor.fetchall()] + conn.close() + return brands + +def get_all_years(): + """Get all unique years from the database""" + conn = get_db_connection() + cursor = conn.cursor() + cursor.execute("SELECT DISTINCT year FROM years ORDER BY year DESC") + years = [row['year'] for row in cursor.fetchall()] + conn.close() + return years + +def get_all_engines(): + """Get all unique engines from the database""" + conn = get_db_connection() + cursor = conn.cursor() + cursor.execute("SELECT DISTINCT name FROM engines ORDER BY name") + engines = [row['name'] for row in cursor.fetchall()] + conn.close() + return engines + +def get_models_by_brand(brand_name=None): + """Get all models, optionally filtered by brand""" + conn = get_db_connection() + cursor = conn.cursor() + + if brand_name: + cursor.execute(""" + SELECT DISTINCT m.name + FROM models m + JOIN brands b ON m.brand_id = b.id + WHERE b.name = ? + ORDER BY m.name + """, (brand_name,)) + else: + cursor.execute("SELECT DISTINCT name FROM models ORDER BY name") + + models = [row['name'] for row in cursor.fetchall()] + conn.close() + return models + +def search_vehicles(brand=None, model=None, year=None, engine=None): + """Search for vehicles based on filters""" + conn = get_db_connection() + cursor = conn.cursor() + + query = """ + SELECT + b.name AS brand, + m.name AS model, + y.year, + e.name AS engine, + e.power_hp, + e.displacement_cc, + e.cylinders, + e.fuel_type, + mye.trim_level, + mye.drivetrain, + mye.transmission + FROM model_year_engine mye + JOIN models m ON mye.model_id = m.id + JOIN brands b ON m.brand_id = b.id + JOIN years y ON mye.year_id = y.id + JOIN engines e ON mye.engine_id = e.id + WHERE 1=1 + """ + + params = [] + if brand: + query += " AND b.name = ?" + params.append(brand) + if model: + query += " AND m.name = ?" + params.append(model) + if year: + query += " AND y.year = ?" + params.append(int(year)) + if engine: + query += " AND e.name = ?" + params.append(engine) + + query += " ORDER BY b.name, m.name, y.year" + + cursor.execute(query, params) + results = cursor.fetchall() + conn.close() + + # Convert to list of dictionaries + vehicles = [] + for row in results: + vehicle = { + 'brand': row['brand'], + 'model': row['model'], + 'year': row['year'], + 'engine': row['engine'], + 'power_hp': row['power_hp'] or 0, + 'displacement_cc': row['displacement_cc'] or 0, + 'cylinders': row['cylinders'] or 0, + 'fuel_type': row['fuel_type'] or 'unknown', + 'trim_level': row['trim_level'] or 'unknown', + 'drivetrain': row['drivetrain'] or 'unknown', + 'transmission': row['transmission'] or 'unknown' + } + vehicles.append(vehicle) + + return vehicles + +@app.route('/') +def index(): + """Serve the main dashboard page""" + return send_from_directory('.', 'index.html') + +@app.route('/') +def static_files(path): + """Serve static files""" + return send_from_directory('.', path) + +@app.route('/api/brands') +def api_brands(): + """API endpoint to get all brands""" + brands = get_all_brands() + return jsonify(brands) + +@app.route('/api/years') +def api_years(): + """API endpoint to get years, optionally filtered by brand and/or model""" + brand = request.args.get('brand') + model = request.args.get('model') + + conn = get_db_connection() + cursor = conn.cursor() + + query = """ + SELECT DISTINCT y.year + FROM years y + JOIN model_year_engine mye ON y.id = mye.year_id + JOIN models m ON mye.model_id = m.id + JOIN brands b ON m.brand_id = b.id + WHERE 1=1 + """ + + params = [] + if brand: + query += " AND b.name = ?" + params.append(brand) + if model: + query += " AND m.name = ?" + params.append(model) + + query += " ORDER BY y.year DESC" + + cursor.execute(query, params) + results = cursor.fetchall() + conn.close() + + years = [row['year'] for row in results] + return jsonify(years) + +@app.route('/api/engines') +def api_engines(): + """API endpoint to get engines, optionally filtered by brand, model, and/or year""" + brand = request.args.get('brand') + model = request.args.get('model') + year = request.args.get('year') + + conn = get_db_connection() + cursor = conn.cursor() + + query = """ + SELECT DISTINCT e.name + FROM engines e + JOIN model_year_engine mye ON e.id = mye.engine_id + JOIN models m ON mye.model_id = m.id + JOIN brands b ON m.brand_id = b.id + JOIN years y ON mye.year_id = y.id + WHERE 1=1 + """ + + params = [] + if brand: + query += " AND b.name = ?" + params.append(brand) + if model: + query += " AND m.name = ?" + params.append(model) + if year: + query += " AND y.year = ?" + params.append(int(year)) + + query += " ORDER BY e.name" + + cursor.execute(query, params) + results = cursor.fetchall() + conn.close() + + engines = [row['name'] for row in results] + return jsonify(engines) + +@app.route('/api/models') +def api_models(): + """API endpoint to get models, optionally filtered by brand""" + brand = request.args.get('brand') + models = get_models_by_brand(brand) + return jsonify(models) + +@app.route('/api/vehicles') +def api_vehicles(): + """API endpoint to search for vehicles""" + brand = request.args.get('brand') + model = request.args.get('model') + year = request.args.get('year') + engine = request.args.get('engine') + + vehicles = search_vehicles(brand, model, year, engine) + return jsonify(vehicles) + +if __name__ == '__main__': + # Check if database exists + if not os.path.exists(DATABASE_PATH): + print(f"Database not found at {DATABASE_PATH}") + print("Please make sure the vehicle database is created first.") + exit(1) + + print("Starting Vehicle Dashboard Server...") + print("Visit http://localhost:5000 to access the dashboard locally") + print("Visit http://192.168.10.198:5000 to access the dashboard from other computers on the network") + app.run(debug=True, host='0.0.0.0', port=5000) \ No newline at end of file diff --git a/dashboard/start_dashboard.sh b/dashboard/start_dashboard.sh new file mode 100755 index 0000000..30db1dc --- /dev/null +++ b/dashboard/start_dashboard.sh @@ -0,0 +1,30 @@ +#!/bin/bash +# Startup script for Vehicle Dashboard + +echo "Vehicle Dashboard Startup Script" +echo "================================" + +# Check if the vehicle database exists +if [ ! -f "../vehicle_database/vehicle_database.db" ]; then + echo "Error: Vehicle database not found!" + echo "Please make sure you have created the vehicle database first." + exit 1 +fi + +echo "Vehicle database found." + +# Check if Flask is available +if python3 -c "import flask" &> /dev/null; then + echo "Flask is available." +else + echo "Installing Flask..." + sudo apt-get update + sudo apt-get install -y python3-flask +fi + +echo "Starting Vehicle Dashboard Server..." +echo "Access the dashboard at: http://localhost:5000" +echo "Press Ctrl+C to stop the server." + +cd /home/Autopartes/dashboard +python3 server.py \ No newline at end of file diff --git a/docs/API.md b/docs/API.md new file mode 100644 index 0000000..1505d02 --- /dev/null +++ b/docs/API.md @@ -0,0 +1,270 @@ +# API Reference - Autoparts DB + +Documentación completa de la API REST del sistema Autoparts DB. + +## Base URL + +``` +http://localhost:5000/api +``` + +## Endpoints + +### GET /api/brands + +Obtiene la lista de todas las marcas de vehículos disponibles. + +**Request:** +```bash +curl http://localhost:5000/api/brands +``` + +**Response:** +```json +{ + "brands": [ + { + "id": 1, + "name": "Toyota", + "country": "Japan", + "founded_year": 1937 + }, + { + "id": 2, + "name": "Honda", + "country": "Japan", + "founded_year": 1948 + } + ] +} +``` + +--- + +### GET /api/models + +Obtiene modelos de vehículos, opcionalmente filtrados por marca. + +**Parámetros:** +| Parámetro | Tipo | Requerido | Descripción | +|-----------|------|-----------|-------------| +| brand | string | No | Nombre de la marca para filtrar | + +**Request:** +```bash +# Todos los modelos +curl http://localhost:5000/api/models + +# Modelos de Toyota +curl "http://localhost:5000/api/models?brand=Toyota" +``` + +**Response:** +```json +{ + "models": [ + { + "id": 1, + "name": "Camry", + "brand": "Toyota", + "body_type": "Sedan", + "generation": "XV70", + "production_start_year": 2017, + "production_end_year": null + } + ] +} +``` + +--- + +### GET /api/years + +Obtiene todos los años disponibles en la base de datos. + +**Request:** +```bash +curl http://localhost:5000/api/years +``` + +**Response:** +```json +{ + "years": [1990, 1991, 1992, ..., 2024, 2025] +} +``` + +--- + +### GET /api/engines + +Obtiene todos los motores disponibles. + +**Request:** +```bash +curl http://localhost:5000/api/engines +``` + +**Response:** +```json +{ + "engines": [ + { + "id": 1, + "name": "2.5L 4-Cylinder", + "displacement_cc": 2500, + "cylinders": 4, + "fuel_type": "Gasoline", + "power_hp": 203, + "torque_nm": 250, + "engine_code": "A25A-FKS" + } + ] +} +``` + +--- + +### GET /api/vehicles + +Búsqueda de vehículos con múltiples filtros. + +**Parámetros:** +| Parámetro | Tipo | Requerido | Descripción | +|-----------|------|-----------|-------------| +| brand | string | No | Marca del vehículo | +| model | string | No | Modelo del vehículo | +| year | integer | No | Año del vehículo | +| engine | string | No | Nombre/código del motor | +| limit | integer | No | Límite de resultados (default: 100) | + +**Request:** +```bash +# Búsqueda completa +curl "http://localhost:5000/api/vehicles?brand=Toyota&model=Camry&year=2020" + +# Solo por marca +curl "http://localhost:5000/api/vehicles?brand=Honda" + +# Con límite +curl "http://localhost:5000/api/vehicles?brand=Ford&limit=50" +``` + +**Response:** +```json +{ + "vehicles": [ + { + "brand": "Toyota", + "model": "Camry", + "year": 2020, + "body_type": "Sedan", + "generation": "XV70", + "engine_name": "2.5L 4-Cylinder", + "displacement_cc": 2500, + "cylinders": 4, + "fuel_type": "Gasoline", + "power_hp": 203, + "torque_nm": 250, + "engine_code": "A25A-FKS", + "trim_level": "LE", + "drivetrain": "FWD", + "transmission": "8-Speed Automatic" + } + ], + "total": 1 +} +``` + +--- + +## Códigos de Respuesta + +| Código | Descripción | +|--------|-------------| +| 200 | OK - Solicitud exitosa | +| 400 | Bad Request - Parámetros inválidos | +| 404 | Not Found - Recurso no encontrado | +| 500 | Internal Server Error - Error del servidor | + +## Manejo de Errores + +Todas las respuestas de error siguen el formato: + +```json +{ + "error": "Descripción del error", + "code": 400 +} +``` + +## Ejemplos de Uso + +### Python + +```python +import requests + +BASE_URL = "http://localhost:5000/api" + +# Obtener marcas +response = requests.get(f"{BASE_URL}/brands") +brands = response.json()["brands"] + +# Buscar vehículos +params = { + "brand": "Toyota", + "year": 2020, + "limit": 50 +} +response = requests.get(f"{BASE_URL}/vehicles", params=params) +vehicles = response.json()["vehicles"] +``` + +### JavaScript + +```javascript +const BASE_URL = 'http://localhost:5000/api'; + +// Obtener marcas +async function getBrands() { + const response = await fetch(`${BASE_URL}/brands`); + const data = await response.json(); + return data.brands; +} + +// Buscar vehículos +async function searchVehicles(filters) { + const params = new URLSearchParams(filters); + const response = await fetch(`${BASE_URL}/vehicles?${params}`); + const data = await response.json(); + return data.vehicles; +} + +// Uso +const vehicles = await searchVehicles({ + brand: 'Toyota', + year: 2020 +}); +``` + +### cURL + +```bash +# Obtener todas las marcas +curl -X GET http://localhost:5000/api/brands + +# Buscar vehículos Toyota del 2020 +curl -X GET "http://localhost:5000/api/vehicles?brand=Toyota&year=2020" + +# Obtener modelos de Honda +curl -X GET "http://localhost:5000/api/models?brand=Honda" +``` + +## Rate Limiting + +Actualmente no hay límites de tasa implementados. Para uso en producción, se recomienda implementar rate limiting. + +## CORS + +El servidor está configurado para aceptar solicitudes desde cualquier origen (CORS habilitado). Para producción, configure los orígenes permitidos apropiadamente. diff --git a/docs/DATABASE.md b/docs/DATABASE.md new file mode 100644 index 0000000..1ce6ad9 --- /dev/null +++ b/docs/DATABASE.md @@ -0,0 +1,425 @@ +# Documentación de Base de Datos - Autoparts DB + +## Resumen + +La base de datos utiliza SQLite 3 y está diseñada con un esquema normalizado para gestionar información de vehículos de manera eficiente. + +**Archivo:** `vehicle_database/vehicle_database.db` + +## Estadísticas Actuales + +| Tabla | Registros | +|-------|-----------| +| brands | 12 | +| models | 10,923 | +| engines | 10,919 | +| years | ~35 | +| model_year_engine | 12,075 | + +--- + +## Diagrama Entidad-Relación + +``` +┌──────────────┐ +│ brands │ +├──────────────┤ +│ id (PK) │ +│ name │ +│ country │ +│ founded_year │ +│ created_at │ +└──────┬───────┘ + │ + │ 1:N + │ +┌──────▼───────────────┐ +│ models │ +├──────────────────────┤ +│ id (PK) │ +│ brand_id (FK) │───────────────────────┐ +│ name │ │ +│ body_type │ │ +│ generation │ │ +│ production_start_year│ │ +│ production_end_year │ │ +│ created_at │ │ +└──────────────────────┘ │ + │ +┌──────────────────────┐ │ +│ years │ │ +├──────────────────────┤ │ +│ id (PK) │───────────────┐ │ +│ year │ │ │ +│ created_at │ │ │ +└──────────────────────┘ │ │ + │ │ +┌──────────────────────┐ │ │ +│ engines │ │ │ +├──────────────────────┤ │ │ +│ id (PK) │───────┐ │ │ +│ name │ │ │ │ +│ displacement_cc │ │ │ │ +│ cylinders │ │ │ │ +│ fuel_type │ │ │ │ +│ power_hp │ │ │ │ +│ torque_nm │ │ │ │ +│ engine_code │ │ │ │ +│ created_at │ │ │ │ +└──────────────────────┘ │ │ │ + │ │ │ + │ N:1 │ N:1 │ N:1 + │ │ │ + ┌──────▼───────▼───────▼──────┐ + │ model_year_engine │ + ├─────────────────────────────┤ + │ id (PK) │ + │ model_id (FK) │ + │ year_id (FK) │ + │ engine_id (FK) │ + │ trim_level │ + │ drivetrain │ + │ transmission │ + │ created_at │ + └─────────────────────────────┘ +``` + +--- + +## Definición de Tablas + +### Tabla: brands + +Almacena información de los fabricantes de vehículos. + +```sql +CREATE TABLE brands ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL UNIQUE, + country TEXT, + founded_year INTEGER, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); +``` + +| Campo | Tipo | Restricciones | Descripción | +|-------|------|---------------|-------------| +| id | INTEGER | PK, AUTO | Identificador único | +| name | TEXT | NOT NULL, UNIQUE | Nombre de la marca | +| country | TEXT | - | País de origen | +| founded_year | INTEGER | - | Año de fundación | +| created_at | TIMESTAMP | DEFAULT NOW | Fecha de creación | + +**Índices:** +- `idx_brands_name` en `name` + +--- + +### Tabla: models + +Almacena información de los modelos de vehículos. + +```sql +CREATE TABLE models ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + brand_id INTEGER NOT NULL, + name TEXT NOT NULL, + body_type TEXT, + generation TEXT, + production_start_year INTEGER, + production_end_year INTEGER, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (brand_id) REFERENCES brands(id), + UNIQUE(brand_id, name, generation) +); +``` + +| Campo | Tipo | Restricciones | Descripción | +|-------|------|---------------|-------------| +| id | INTEGER | PK, AUTO | Identificador único | +| brand_id | INTEGER | FK, NOT NULL | Referencia a brands | +| name | TEXT | NOT NULL | Nombre del modelo | +| body_type | TEXT | - | Tipo de carrocería | +| generation | TEXT | - | Generación del modelo | +| production_start_year | INTEGER | - | Año inicio producción | +| production_end_year | INTEGER | - | Año fin producción | +| created_at | TIMESTAMP | DEFAULT NOW | Fecha de creación | + +**Valores de body_type:** +- Sedan, Coupe, Hatchback, SUV, Crossover, Truck, Van, Wagon, Convertible + +**Índices:** +- `idx_models_brand_id` en `brand_id` +- `idx_models_name` en `name` + +--- + +### Tabla: engines + +Almacena especificaciones técnicas de los motores. + +```sql +CREATE TABLE engines ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL, + displacement_cc INTEGER, + cylinders INTEGER, + fuel_type TEXT, + power_hp INTEGER, + torque_nm INTEGER, + engine_code TEXT, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + UNIQUE(name, engine_code) +); +``` + +| Campo | Tipo | Restricciones | Descripción | +|-------|------|---------------|-------------| +| id | INTEGER | PK, AUTO | Identificador único | +| name | TEXT | NOT NULL | Nombre descriptivo | +| displacement_cc | INTEGER | - | Cilindrada en cc | +| cylinders | INTEGER | - | Número de cilindros | +| fuel_type | TEXT | - | Tipo de combustible | +| power_hp | INTEGER | - | Potencia en HP | +| torque_nm | INTEGER | - | Torque en Nm | +| engine_code | TEXT | - | Código del fabricante | +| created_at | TIMESTAMP | DEFAULT NOW | Fecha de creación | + +**Valores de fuel_type:** +- Gasoline, Diesel, Hybrid, Electric, Plug-in Hybrid, Flex Fuel + +**Índices:** +- `idx_engines_name` en `name` +- `idx_engines_code` en `engine_code` + +--- + +### Tabla: years + +Tabla de referencia para años de producción. + +```sql +CREATE TABLE years ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + year INTEGER NOT NULL UNIQUE, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); +``` + +| Campo | Tipo | Restricciones | Descripción | +|-------|------|---------------|-------------| +| id | INTEGER | PK, AUTO | Identificador único | +| year | INTEGER | NOT NULL, UNIQUE | Año | +| created_at | TIMESTAMP | DEFAULT NOW | Fecha de creación | + +--- + +### Tabla: model_year_engine + +Tabla de unión que relaciona modelos, años y motores. + +```sql +CREATE TABLE model_year_engine ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + model_id INTEGER NOT NULL, + year_id INTEGER NOT NULL, + engine_id INTEGER NOT NULL, + trim_level TEXT, + drivetrain TEXT, + transmission TEXT, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (model_id) REFERENCES models(id), + FOREIGN KEY (year_id) REFERENCES years(id), + FOREIGN KEY (engine_id) REFERENCES engines(id), + UNIQUE(model_id, year_id, engine_id, trim_level) +); +``` + +| Campo | Tipo | Restricciones | Descripción | +|-------|------|---------------|-------------| +| id | INTEGER | PK, AUTO | Identificador único | +| model_id | INTEGER | FK, NOT NULL | Referencia a models | +| year_id | INTEGER | FK, NOT NULL | Referencia a years | +| engine_id | INTEGER | FK, NOT NULL | Referencia a engines | +| trim_level | TEXT | - | Nivel de equipamiento | +| drivetrain | TEXT | - | Tipo de tracción | +| transmission | TEXT | - | Tipo de transmisión | +| created_at | TIMESTAMP | DEFAULT NOW | Fecha de creación | + +**Valores de drivetrain:** +- FWD (Front-Wheel Drive), RWD (Rear-Wheel Drive), AWD (All-Wheel Drive), 4WD (Four-Wheel Drive) + +**Valores de transmission:** +- Manual, Automatic, CVT, DCT, AMT + +**Índices:** +- `idx_mye_model_id` en `model_id` +- `idx_mye_year_id` en `year_id` +- `idx_mye_engine_id` en `engine_id` + +--- + +## Consultas Comunes + +### Obtener todos los vehículos de una marca + +```sql +SELECT + b.name AS brand, + m.name AS model, + y.year, + e.name AS engine, + mye.trim_level, + mye.drivetrain, + mye.transmission +FROM model_year_engine mye +JOIN models m ON mye.model_id = m.id +JOIN brands b ON m.brand_id = b.id +JOIN years y ON mye.year_id = y.id +JOIN engines e ON mye.engine_id = e.id +WHERE b.name = 'Toyota' +ORDER BY m.name, y.year; +``` + +### Buscar vehículos por año + +```sql +SELECT + b.name AS brand, + m.name AS model, + e.name AS engine, + e.power_hp, + mye.trim_level +FROM model_year_engine mye +JOIN models m ON mye.model_id = m.id +JOIN brands b ON m.brand_id = b.id +JOIN years y ON mye.year_id = y.id +JOIN engines e ON mye.engine_id = e.id +WHERE y.year = 2020; +``` + +### Obtener especificaciones de motor + +```sql +SELECT + name, + displacement_cc, + cylinders, + fuel_type, + power_hp, + torque_nm, + engine_code +FROM engines +WHERE cylinders = 6 +ORDER BY power_hp DESC; +``` + +### Contar modelos por marca + +```sql +SELECT + b.name AS brand, + COUNT(DISTINCT m.id) AS model_count +FROM brands b +LEFT JOIN models m ON b.id = m.brand_id +GROUP BY b.id +ORDER BY model_count DESC; +``` + +### Buscar motores por potencia + +```sql +SELECT * +FROM engines +WHERE power_hp >= 300 +ORDER BY power_hp DESC; +``` + +--- + +## Mantenimiento + +### Backup de la Base de Datos + +```bash +# Crear backup +sqlite3 vehicle_database.db ".backup 'backup.db'" + +# O usando cp +cp vehicle_database.db vehicle_database_backup_$(date +%Y%m%d).db +``` + +### Optimización + +```sql +-- Analizar tablas para optimizar consultas +ANALYZE; + +-- Reconstruir índices +REINDEX; + +-- Compactar base de datos +VACUUM; +``` + +### Verificar Integridad + +```sql +PRAGMA integrity_check; +PRAGMA foreign_key_check; +``` + +--- + +## Conexión desde Python + +```python +import sqlite3 + +# Conectar a la base de datos +conn = sqlite3.connect('vehicle_database/vehicle_database.db') +conn.row_factory = sqlite3.Row # Permite acceso por nombre de columna + +# Crear cursor +cursor = conn.cursor() + +# Ejecutar consulta +cursor.execute(""" + SELECT b.name, m.name, y.year + FROM model_year_engine mye + JOIN models m ON mye.model_id = m.id + JOIN brands b ON m.brand_id = b.id + JOIN years y ON mye.year_id = y.id + WHERE b.name = ? +""", ('Toyota',)) + +# Obtener resultados +for row in cursor.fetchall(): + print(f"{row['name']} - {row['name']} ({row['year']})") + +# Cerrar conexión +conn.close() +``` + +--- + +## Migración de Datos + +### Exportar a CSV + +```bash +sqlite3 -header -csv vehicle_database.db "SELECT * FROM brands;" > brands_export.csv +sqlite3 -header -csv vehicle_database.db "SELECT * FROM models;" > models_export.csv +sqlite3 -header -csv vehicle_database.db "SELECT * FROM engines;" > engines_export.csv +``` + +### Importar desde CSV + +```bash +sqlite3 vehicle_database.db <=2.28.0 +beautifulsoup4>=4.11.0 +lxml>=4.9.0 +``` + +### Instalación Individual + +```bash +# Flask - Framework web +pip install flask + +# Requests - Cliente HTTP +pip install requests + +# BeautifulSoup4 - Parser HTML +pip install beautifulsoup4 + +# lxml - Parser XML/HTML rápido +pip install lxml +``` + +--- + +## Configuración + +### Variables de Entorno (Opcional) + +```bash +# Puerto del servidor (default: 5000) +export FLASK_PORT=5000 + +# Modo debug (solo desarrollo) +export FLASK_DEBUG=1 + +# Ruta de la base de datos +export DATABASE_PATH=/path/to/vehicle_database.db +``` + +### Configuración del Servidor + +Editar `dashboard/server.py`: + +```python +# Cambiar puerto +app.run(host='0.0.0.0', port=5000, debug=False) + +# Para producción +app.run(host='0.0.0.0', port=8080, debug=False) +``` + +--- + +## Verificación de la Instalación + +### 1. Verificar Python + +```bash +python3 --version +# Debería mostrar: Python 3.8.x o superior +``` + +### 2. Verificar Dependencias + +```bash +python3 -c "import flask; print(f'Flask: {flask.__version__}')" +python3 -c "import requests; print(f'Requests: {requests.__version__}')" +python3 -c "import bs4; print(f'BeautifulSoup: {bs4.__version__}')" +``` + +### 3. Verificar Base de Datos + +```bash +cd vehicle_database +python3 -c " +import sqlite3 +conn = sqlite3.connect('vehicle_database.db') +cursor = conn.execute('SELECT COUNT(*) FROM brands') +print(f'Marcas en BD: {cursor.fetchone()[0]}') +conn.close() +" +``` + +### 4. Probar el Dashboard + +```bash +cd dashboard +python3 server.py & +curl http://localhost:5000/api/brands +``` + +--- + +## Solución de Problemas + +### Error: ModuleNotFoundError + +```bash +# Verificar que el entorno virtual está activado +which python3 + +# Reinstalar dependencias +pip install --force-reinstall flask requests beautifulsoup4 lxml +``` + +### Error: Database is locked + +```bash +# Cerrar todas las conexiones a la base de datos +# Reiniciar el servidor +pkill -f server.py +python3 dashboard/server.py +``` + +### Error: Port already in use + +```bash +# Encontrar proceso usando el puerto +lsof -i :5000 + +# Matar el proceso +kill -9 [PID] + +# O usar otro puerto +python3 server.py --port 5001 +``` + +### Error: Permission denied + +```bash +# Dar permisos de ejecución a scripts +chmod +x vehicle_database/setup.sh +chmod +x dashboard/start_dashboard.sh +chmod +x QUICK_START.sh +``` + +--- + +## Instalación en Producción + +### Usando Gunicorn (Recomendado) + +```bash +# Instalar Gunicorn +pip install gunicorn + +# Iniciar servidor +cd dashboard +gunicorn -w 4 -b 0.0.0.0:8080 server:app +``` + +### Usando systemd + +Crear archivo `/etc/systemd/system/autoparts-db.service`: + +```ini +[Unit] +Description=Autoparts DB Dashboard +After=network.target + +[Service] +User=www-data +WorkingDirectory=/path/to/Autoparts-DB/dashboard +ExecStart=/usr/bin/python3 server.py +Restart=always + +[Install] +WantedBy=multi-user.target +``` + +Habilitar e iniciar: + +```bash +sudo systemctl enable autoparts-db +sudo systemctl start autoparts-db +``` + +### Usando Docker (Opcional) + +```dockerfile +FROM python:3.11-slim + +WORKDIR /app +COPY . /app + +RUN pip install flask requests beautifulsoup4 lxml + +EXPOSE 5000 + +CMD ["python3", "dashboard/server.py"] +``` + +```bash +docker build -t autoparts-db . +docker run -p 5000:5000 autoparts-db +``` + +--- + +## Actualizaciones + +```bash +# Obtener últimos cambios +git pull origin main + +# Actualizar dependencias +pip install --upgrade -r requirements.txt +``` + +--- + +## Desinstalación + +```bash +# Desactivar entorno virtual +deactivate + +# Eliminar directorio del proyecto +rm -rf Autoparts-DB + +# Eliminar entorno virtual (si está separado) +rm -rf venv +``` diff --git a/fix_arra_data.py b/fix_arra_data.py new file mode 100644 index 0000000..3297d0a --- /dev/null +++ b/fix_arra_data.py @@ -0,0 +1,180 @@ +""" +Script to fix the ARRA vehicle data in the database. +This will remove the incorrect entry under Aston Martin and add the correct ARRA entry. +""" +import sqlite3 + + +def connect_db(db_path: str = "vehicle_database/vehicle_database.db") -> sqlite3.Connection: + """Connect to the vehicle database.""" + conn = sqlite3.connect(db_path) + conn.execute("PRAGMA foreign_keys = ON") # Enable foreign key constraints + return conn + + +def get_or_insert_brand(conn: sqlite3.Connection, brand_name: str) -> int: + """Get brand ID or insert if it doesn't exist.""" + cursor = conn.cursor() + + # Check if brand already exists + cursor.execute("SELECT id FROM brands WHERE name = ?", (brand_name,)) + result = cursor.fetchone() + + if result: + return result[0] # Return existing ID + + # Insert new brand + cursor.execute("INSERT INTO brands (name) VALUES (?)", (brand_name,)) + conn.commit() + return cursor.lastrowid + + +def get_or_insert_model(conn: sqlite3.Connection, brand_id: int, model_name: str) -> int: + """Get model ID or insert if it doesn't exist.""" + cursor = conn.cursor() + + # Check if model already exists for this brand + cursor.execute( + "SELECT id FROM models WHERE brand_id = ? AND name = ?", + (brand_id, model_name) + ) + result = cursor.fetchone() + + if result: + return result[0] # Return existing ID + + # Insert new model + cursor.execute( + "INSERT INTO models (brand_id, name) VALUES (?, ?)", + (brand_id, model_name) + ) + conn.commit() + return cursor.lastrowid + + +def get_or_insert_engine(conn: sqlite3.Connection, engine_name: str) -> int: + """Get engine ID or insert if it doesn't exist.""" + cursor = conn.cursor() + + # Check if engine already exists + cursor.execute("SELECT id FROM engines WHERE name = ?", (engine_name,)) + result = cursor.fetchone() + + if result: + return result[0] # Return existing ID + + # Insert new engine + cursor.execute("INSERT INTO engines (name) VALUES (?)", (engine_name,)) + conn.commit() + return cursor.lastrowid + + +def get_or_insert_year(conn: sqlite3.Connection, year: int) -> int: + """Get year ID or insert if it doesn't exist.""" + cursor = conn.cursor() + + # Check if year already exists + cursor.execute("SELECT id FROM years WHERE year = ?", (year,)) + result = cursor.fetchone() + + if result: + return result[0] # Return existing ID + + # Insert new year + cursor.execute("INSERT INTO years (year) VALUES (?)", (year,)) + conn.commit() + return cursor.lastrowid + + +def remove_incorrect_arr_entry(conn: sqlite3.Connection) -> None: + """Remove the incorrect ARRA entry under Aston Martin.""" + cursor = conn.cursor() + + # Find the model ID for ARRA under Aston Martin + cursor.execute(""" + SELECT m.id + FROM models m + JOIN brands b ON m.brand_id = b.id + WHERE b.name = 'ASTON MARTIN' AND m.name = 'ARRA' + """) + arra_model_result = cursor.fetchone() + + if arra_model_result: + arra_model_id = arra_model_result[0] + + # Remove the incorrect relationship from model_year_engine + cursor.execute( + "DELETE FROM model_year_engine WHERE model_id = ?", + (arra_model_id,) + ) + + # Remove the ARRA model from models table + cursor.execute( + "DELETE FROM models WHERE id = ?", + (arra_model_id,) + ) + + print(f"Removed incorrect ARRA model with ID {arra_model_id} from Aston Martin") + + conn.commit() + + +def insert_correct_arr_entry(conn: sqlite3.Connection) -> None: + """Insert the correct ARRA EW-1 entry.""" + cursor = conn.cursor() + + # Get or create ARRA brand + arra_brand_id = get_or_insert_brand(conn, "ARRA") + print(f"ARRA brand ID: {arra_brand_id}") + + # Get or create EW-1 model under ARRA brand + ew1_model_id = get_or_insert_model(conn, arra_brand_id, "EW-1") + print(f"EW-1 model ID: {ew1_model_id}") + + # Get or create Electric engine + engine_id = get_or_insert_engine(conn, "Electric") + print(f"Electric engine ID: {engine_id}") + + # Get or create 2024 year + year_id = get_or_insert_year(conn, 2024) + print(f"Year 2024 ID: {year_id}") + + # Check if the correct relationship already exists + cursor.execute( + "SELECT id FROM model_year_engine WHERE model_id = ? AND year_id = ? AND engine_id = ?", + (ew1_model_id, year_id, engine_id) + ) + result = cursor.fetchone() + + if not result: + # Insert the correct relationship + cursor.execute( + "INSERT INTO model_year_engine (model_id, year_id, engine_id) VALUES (?, ?, ?)", + (ew1_model_id, year_id, engine_id) + ) + conn.commit() + print("Added correct ARRA EW-1 2024 Electric entry") + else: + print("Correct ARRA EW-1 2024 Electric entry already exists") + + +def main(): + """Main function to fix ARRA data.""" + print("Fixing ARRA vehicle data...") + + # Connect to database + conn = connect_db() + + # Remove the incorrect entry + remove_incorrect_arr_entry(conn) + + # Insert the correct entry + insert_correct_arr_entry(conn) + + # Close connection + conn.close() + print("ARRA data fixed successfully!") + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/integration_demo.py b/integration_demo.py new file mode 100644 index 0000000..90369d8 --- /dev/null +++ b/integration_demo.py @@ -0,0 +1,95 @@ +#!/usr/bin/env python3 +""" +RockAuto Data Integration Demo +Shows how to integrate manually extracted RockAuto data into the vehicle database +""" + +from vehicle_scraper.manual_input_simple import ManualDataInput +import sqlite3 + + +def demonstrate_integration(): + """Demonstrate the integration of RockAuto data into the database""" + print("RockAuto Data Integration Demo") + print("==============================") + + # Create input tool with correct database path + input_tool = ManualDataInput(db_path="vehicle_database/vehicle_database.db") + + # Example of data you might extract from RockAuto manually + print("\nExample: Adding data that could be extracted from RockAuto") + + # Add some example vehicles that might be found on RockAuto + rockauto_vehicles = [ + {"make": "Toyota", "model": "RAV4", "year": 2019, "engine": "2.5L 4-Cylinder"}, + {"make": "Honda", "model": "CR-V", "year": 2020, "engine": "1.5L Turbo"}, + {"make": "Ford", "model": "Escape", "year": 2021, "engine": "2.0L Turbo"}, + {"make": "Nissan", "model": "Rogue", "year": 2018, "engine": "2.5L 4-Cylinder"}, + {"make": "Chevrolet", "model": "Equinox", "year": 2020, "engine": "1.5L Turbo"}, + ] + + print(f"\nAdding {len(rockauto_vehicles)} vehicles to database:") + for vehicle in rockauto_vehicles: + print(f" - {vehicle['year']} {vehicle['make']} {vehicle['model']} ({vehicle['engine']})") + + input_tool.add_multiple_vehicles(rockauto_vehicles) + + # Show the data in the database + print("\nCurrent data in database:") + conn = sqlite3.connect("vehicle_database/vehicle_database.db") + cursor = conn.cursor() + + cursor.execute(''' + SELECT b.name as brand, m.name as model, y.year, e.name as engine + FROM model_year_engine mye + JOIN models m ON mye.model_id = m.id + JOIN brands b ON m.brand_id = b.id + JOIN years y ON mye.year_id = y.id + JOIN engines e ON mye.engine_id = e.id + ORDER BY b.name, m.name, y.year + LIMIT 15 + ''') + + results = cursor.fetchall() + for i, (brand, model, year, engine) in enumerate(results, 1): + print(f" {i:2d}. {year} {brand} {model} - {engine}") + + if len(results) == 15: + print(" ... (showing first 15 results)") + + conn.close() + + print(f"\nDatabase contains {len(results)} vehicle entries") + + # Show how to query specific data + print("\nExample queries you can perform:") + print("1. Find all Toyota models:") + print(" In query interface: Search brand='Toyota'") + + print("2. Find all 2020 model year vehicles:") + print(" In query interface: Search year=2020") + + print("3. Find all vehicles with 'Turbo' engines:") + print(" In query interface: Search engine='Turbo'") + + print("\nTo continue adding RockAuto data:") + print("1. Visit https://www.rockauto.com/catalog/") + print("2. Browse manufacturers and note models/years/engines") + print("3. Use ManualDataInput to add to your database") + + +def main(): + demonstrate_integration() + + print("\n" + "="*50) + print("INTEGRATION DEMO COMPLETE") + print("="*50) + print("\nNext steps:") + print("1. Manually browse RockAuto.com for vehicle data") + print("2. Use the ManualDataInput class to add data") + print("3. Query your database using the query interface") + print("4. Build your comprehensive vehicle database") + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/remove_brands_and_cleanup.py b/remove_brands_and_cleanup.py new file mode 100644 index 0000000..168ed18 --- /dev/null +++ b/remove_brands_and_cleanup.py @@ -0,0 +1,141 @@ +#!/usr/bin/env python3 +""" +Script para eliminar vehículos de marcas específicas y ocultar marcas sin vehículos +""" + +import sqlite3 +import os + +def remove_brands_and_cleanup(): + # Verificar que la base de datos exista + db_path = "vehicle_database/vehicle_database.db" + if not os.path.exists(db_path): + print(f"Error: Base de datos no encontrada en {db_path}") + return + + conn = sqlite3.connect(db_path) + cursor = conn.cursor() + + print("Eliminando vehículos de marcas específicas y limpiando marcas sin vehículos...") + + try: + # Marcas a eliminar + brands_to_remove = ['FORD', 'TOYOTA', 'NISSAN', 'HONDA', 'CHEVROLET'] + + print(f"Marcas a eliminar: {', '.join(brands_to_remove)}") + + # Contar cuántos vehículos vamos a eliminar antes de hacerlo + placeholders = ','.join(['?' for _ in brands_to_remove]) + cursor.execute(f""" + SELECT COUNT(*) + FROM model_year_engine mye + JOIN models m ON mye.model_id = m.id + JOIN brands b ON m.brand_id = b.id + WHERE b.name IN ({placeholders}) + """, brands_to_remove) + count_to_delete = cursor.fetchone()[0] + + print(f"Se eliminarán {count_to_delete} vehículos de las marcas especificadas") + + if count_to_delete > 0: + print("Procediendo con la eliminación...") + + # Eliminar las combinaciones modelo-año-motor para los modelos de las marcas especificadas + cursor.execute(f""" + DELETE FROM model_year_engine + WHERE model_id IN ( + SELECT m.id FROM models m + JOIN brands b ON m.brand_id = b.id + WHERE b.name IN ({placeholders}) + ) + """, brands_to_remove) + + # Eliminar los modelos de las marcas especificadas + cursor.execute(f""" + DELETE FROM models + WHERE brand_id IN ( + SELECT id FROM brands WHERE name IN ({placeholders}) + ) + """, brands_to_remove) + + # Eliminar las marcas especificadas + cursor.execute(f""" + DELETE FROM brands + WHERE name IN ({placeholders}) + """, brands_to_remove) + + conn.commit() + print(f"Se eliminaron {count_to_delete} registros de vehículos de las marcas especificadas") + + else: + print("No hay vehículos de las marcas especificadas para eliminar") + + # Ahora vamos a limpiar las marcas sin vehículos (aunque en este caso ya se eliminaron) + # pero también limpiaremos cualquier marca que haya quedado sin vehículos + print("\nLimpiando marcas sin vehículos...") + + # Encontrar marcas que no tienen modelos asociados + cursor.execute(""" + SELECT b.id, b.name + FROM brands b + LEFT JOIN models m ON b.id = m.brand_id + WHERE m.brand_id IS NULL + """) + brands_without_models = cursor.fetchall() + + if brands_without_models: + print(f"Marcas sin modelos para eliminar: {[brand[1] for brand in brands_without_models]}") + brand_ids = [str(brand[0]) for brand in brands_without_models] + placeholders = ','.join(brand_ids) + + # Eliminar estas marcas + cursor.execute(f""" + DELETE FROM brands + WHERE id IN ({placeholders}) + """) + else: + print("No hay marcas sin modelos para eliminar") + + # Mostrar un resumen de la base de datos actualizada + print("\nResumen de la base de datos actualizada:") + + # Contar vehículos restantes + cursor.execute(""" + SELECT COUNT(*) + FROM model_year_engine mye + JOIN models m ON mye.model_id = m.id + JOIN brands b ON m.brand_id = b.id + """) + total_vehicles = cursor.fetchone()[0] + print(f"Total de vehículos restantes: {total_vehicles}") + + # Contar marcas restantes + cursor.execute("SELECT COUNT(*) FROM brands") + total_brands = cursor.fetchone()[0] + print(f"Total de marcas restantes: {total_brands}") + + # Mostrar marcas restantes + cursor.execute("SELECT name FROM brands ORDER BY name") + remaining_brands = cursor.fetchall() + print(f"Marcas restantes: {[brand[0] for brand in remaining_brands]}") + + # Mostrar rango de años actual + cursor.execute(""" + SELECT MIN(y.year), MAX(y.year) + FROM years y + JOIN model_year_engine mye ON y.id = mye.year_id + """) + min_year, max_year = cursor.fetchone() + if min_year and max_year: + print(f"Rango de años actual: {min_year} - {max_year}") + else: + print("No hay años con vehículos registrados") + + except Exception as e: + print(f"Error al eliminar marcas y limpiar la base de datos: {e}") + conn.rollback() + finally: + conn.close() + +if __name__ == "__main__": + remove_brands_and_cleanup() \ No newline at end of file diff --git a/remove_brands_and_cleanup_final.py b/remove_brands_and_cleanup_final.py new file mode 100644 index 0000000..67b2817 --- /dev/null +++ b/remove_brands_and_cleanup_final.py @@ -0,0 +1,105 @@ +#!/usr/bin/env python3 +""" +Script para eliminar marcas específicas y limpiar marcas sin vehículos +""" + +import sqlite3 +import os + +def remove_brands_and_cleanup(): + # Verificar que la base de datos exista + db_path = "vehicle_database/vehicle_database.db" + if not os.path.exists(db_path): + print(f"Error: Base de datos no encontrada en {db_path}") + return + + conn = sqlite3.connect(db_path) + cursor = conn.cursor() + + print("Eliminando marcas específicas y limpiando marcas sin vehículos...") + + try: + # Marcas a eliminar + brands_to_remove = ['FORD', 'TOYOTA', 'NISSAN', 'HONDA', 'CHEVROLET'] + + print(f"Marcas a eliminar: {', '.join(brands_to_remove)}") + + # Eliminar modelos y combinaciones modelo-año-motor para las marcas especificadas + placeholders = ','.join(['?' for _ in brands_to_remove]) + cursor.execute(f""" + DELETE FROM model_year_engine + WHERE model_id IN ( + SELECT m.id FROM models m + JOIN brands b ON m.brand_id = b.id + WHERE b.name IN ({placeholders}) + ) + """, brands_to_remove) + + # Eliminar modelos de las marcas especificadas + cursor.execute(f""" + DELETE FROM models + WHERE brand_id IN ( + SELECT id FROM brands WHERE name IN ({placeholders}) + ) + """, brands_to_remove) + + # Eliminar las marcas especificadas + cursor.execute(f""" + DELETE FROM brands + WHERE name IN ({placeholders}) + """, brands_to_remove) + + # También eliminar marcas que no tienen modelos asociados + print("Eliminando marcas sin vehículos...") + cursor.execute(""" + DELETE FROM brands + WHERE id NOT IN ( + SELECT DISTINCT brand_id FROM models + ) + """) + + conn.commit() + + # Mostrar un resumen de la base de datos actualizada + print("\nResumen de la base de datos actualizada:") + + # Contar vehículos restantes + cursor.execute(""" + SELECT COUNT(*) + FROM model_year_engine mye + JOIN models m ON mye.model_id = m.id + JOIN brands b ON m.brand_id = b.id + """) + total_vehicles = cursor.fetchone()[0] + print(f"Total de vehículos restantes: {total_vehicles}") + + # Contar marcas restantes + cursor.execute("SELECT COUNT(*) FROM brands") + total_brands = cursor.fetchone()[0] + print(f"Total de marcas restantes: {total_brands}") + + # Mostrar marcas restantes + cursor.execute("SELECT name FROM brands ORDER BY name") + remaining_brands = cursor.fetchall() + print(f"Marcas restantes: {[brand[0] for brand in remaining_brands]}") + + # Mostrar rango de años actual + cursor.execute(""" + SELECT MIN(y.year), MAX(y.year) + FROM years y + JOIN model_year_engine mye ON y.id = mye.year_id + """) + min_year, max_year = cursor.fetchone() + if min_year and max_year: + print(f"Rango de años actual: {min_year} - {max_year}") + else: + print("No hay años con vehículos registrados") + + except Exception as e: + print(f"Error al eliminar marcas y limpiar la base de datos: {e}") + conn.rollback() + finally: + conn.close() + +if __name__ == "__main__": + remove_brands_and_cleanup() \ No newline at end of file diff --git a/remove_old_vehicles.py b/remove_old_vehicles.py new file mode 100644 index 0000000..38d3ae3 --- /dev/null +++ b/remove_old_vehicles.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python3 +""" +Script para eliminar vehículos de 1975 hacia abajo de la base de datos +""" + +import sqlite3 +import os + +def remove_old_vehicles(): + # Verificar que la base de datos exista + db_path = "vehicle_database/vehicle_database.db" + if not os.path.exists(db_path): + print(f"Error: Base de datos no encontrada en {db_path}") + return + + conn = sqlite3.connect(db_path) + cursor = conn.cursor() + + print("Eliminando vehículos de 1975 hacia abajo de la base de datos...") + + try: + # Contar cuántos vehículos vamos a eliminar antes de hacerlo + cursor.execute(""" + SELECT COUNT(*) + FROM model_year_engine mye + JOIN years y ON mye.year_id = y.id + WHERE y.year <= 1975 + """) + count_to_delete = cursor.fetchone()[0] + + print(f"Se eliminarán {count_to_delete} vehículos de 1975 hacia abajo") + + if count_to_delete > 0: + # Confirmar antes de eliminar + confirm = input("¿Deseas continuar con la eliminación? (s/N): ") + if confirm.lower() != 's': + print("Operación cancelada.") + return + + # Eliminar las combinaciones modelo-año-motor para años <= 1975 + cursor.execute(""" + DELETE FROM model_year_engine + WHERE year_id IN ( + SELECT id FROM years WHERE year <= 1975 + ) + """) + + # Eliminar los años <= 1975 que ya no tienen registros asociados + cursor.execute(""" + DELETE FROM years + WHERE year <= 1975 + AND id NOT IN ( + SELECT DISTINCT year_id FROM model_year_engine + ) + """) + + conn.commit() + print(f"Se eliminaron {count_to_delete} registros de vehículos de 1975 hacia abajo") + + # Mostrar algunos de los años que se eliminaron + print("\nAños eliminados:") + cursor.execute(""" + SELECT DISTINCT y.year + FROM years y + WHERE y.year <= 1975 + ORDER BY y.year DESC + """) + deleted_years = cursor.fetchall() + if deleted_years: + for year in deleted_years: + print(f" - {year[0]}") + else: + print(" (No quedan años <= 1975 en la base de datos)") + + else: + print("No hay vehículos de 1975 hacia abajo para eliminar") + + # Mostrar un resumen de la base de datos actualizada + print("\nResumen de la base de datos actualizada:") + + # Contar vehículos restantes + cursor.execute(""" + SELECT COUNT(*) + FROM model_year_engine mye + JOIN years y ON mye.year_id = y.id + """) + total_vehicles = cursor.fetchone()[0] + print(f"Total de vehículos restantes: {total_vehicles}") + + # Mostrar rango de años actual + cursor.execute("SELECT MIN(year), MAX(year) FROM years") + min_year, max_year = cursor.fetchone() + print(f"Rango de años actual: {min_year} - {max_year}") + + except Exception as e: + print(f"Error al eliminar vehículos antiguos: {e}") + conn.rollback() + finally: + conn.close() + +if __name__ == "__main__": + remove_old_vehicles() \ No newline at end of file diff --git a/remove_old_vehicles_auto.py b/remove_old_vehicles_auto.py new file mode 100644 index 0000000..356485e --- /dev/null +++ b/remove_old_vehicles_auto.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python3 +""" +Script para eliminar vehículos de 1975 hacia abajo de la base de datos +""" + +import sqlite3 +import os + +def remove_old_vehicles(): + # Verificar que la base de datos exista + db_path = "vehicle_database/vehicle_database.db" + if not os.path.exists(db_path): + print(f"Error: Base de datos no encontrada en {db_path}") + return + + conn = sqlite3.connect(db_path) + cursor = conn.cursor() + + print("Eliminando vehículos de 1975 hacia abajo de la base de datos...") + + try: + # Contar cuántos vehículos vamos a eliminar antes de hacerlo + cursor.execute(""" + SELECT COUNT(*) + FROM model_year_engine mye + JOIN years y ON mye.year_id = y.id + WHERE y.year <= 1975 + """) + count_to_delete = cursor.fetchone()[0] + + print(f"Se eliminarán {count_to_delete} vehículos de 1975 hacia abajo") + + if count_to_delete > 0: + print("Procediendo con la eliminación...") + + # Eliminar las combinaciones modelo-año-motor para años <= 1975 + cursor.execute(""" + DELETE FROM model_year_engine + WHERE year_id IN ( + SELECT id FROM years WHERE year <= 1975 + ) + """) + + # Eliminar los años <= 1975 que ya no tienen registros asociados + cursor.execute(""" + DELETE FROM years + WHERE year <= 1975 + AND id NOT IN ( + SELECT DISTINCT year_id FROM model_year_engine + ) + """) + + conn.commit() + print(f"Se eliminaron {count_to_delete} registros de vehículos de 1975 hacia abajo") + + # Mostrar algunos de los años que se eliminaron + print("\nAños eliminados:") + cursor.execute(""" + SELECT DISTINCT y.year + FROM years y + WHERE y.year <= 1975 + ORDER BY y.year DESC + """) + deleted_years = cursor.fetchall() + if deleted_years: + for year in deleted_years: + print(f" - {year[0]}") + else: + print(" (No quedan años <= 1975 en la base de datos)") + + else: + print("No hay vehículos de 1975 hacia abajo para eliminar") + + # Mostrar un resumen de la base de datos actualizada + print("\nResumen de la base de datos actualizada:") + + # Contar vehículos restantes + cursor.execute(""" + SELECT COUNT(*) + FROM model_year_engine mye + JOIN years y ON mye.year_id = y.id + """) + total_vehicles = cursor.fetchone()[0] + print(f"Total de vehículos restantes: {total_vehicles}") + + # Mostrar rango de años actual + cursor.execute("SELECT MIN(year), MAX(year) FROM years") + min_year, max_year = cursor.fetchone() + print(f"Rango de años actual: {min_year} - {max_year}") + + except Exception as e: + print(f"Error al eliminar vehículos antiguos: {e}") + conn.rollback() + finally: + conn.close() + +if __name__ == "__main__": + remove_old_vehicles() \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..1fbfeab --- /dev/null +++ b/requirements.txt @@ -0,0 +1,4 @@ +flask==2.3.3 +requests>=2.28.0 +beautifulsoup4>=4.11.0 +lxml>=4.9.0 diff --git a/scrape_toyota_windows.py b/scrape_toyota_windows.py new file mode 100644 index 0000000..6724e4b --- /dev/null +++ b/scrape_toyota_windows.py @@ -0,0 +1,185 @@ +import requests +from bs4 import BeautifulSoup +import sqlite3 +import time +import re +import os +from urllib.parse import unquote + +# Base de datos - ruta directa desde C:\Autopartes +DB_PATH = "vehicle_database/vehicle_database.db" + +BASE_URL = "https://www.rockauto.com/en/catalog" + +MISSING_YEARS = [ + 2003, 2002, 2001, 2000, 1999, 1998, 1997, 1996, 1995, 1994, + 1993, 1992, 1991, 1990, 1989, 1988, 1987, 1986, 1985, 1984, + 1983, 1982, 1981, 1980, 1979, 1978, 1977, 1976, 1975 +] + +session = requests.Session() +session.headers.update({ + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36', + 'Accept': 'text/html,application/xhtml+xml', +}) + +def clean_name(name): + name = unquote(name.replace('+', ' ')) + return re.sub(r'\s+', ' ', name).strip().upper() + +def get_soup(url, retries=3): + for attempt in range(retries): + try: + time.sleep(0.5) + response = session.get(url, timeout=15) + if response.status_code == 200: + return BeautifulSoup(response.content, 'html.parser') + elif response.status_code == 403: + print("\n [!] BLOQUEADO (403) - Cambia el VPN!") + return None + except Exception as e: + if attempt < retries - 1: + time.sleep(3) + else: + print(f"\n Error: {e}") + return None + +def get_models(brand, year): + brand_url = brand.lower() + soup = get_soup(f"{BASE_URL}/{brand_url},{year}") + if not soup: + return [] + + models = set() + for link in soup.find_all('a', href=True): + match = re.search(rf'/catalog/{brand_url},{year},([^,/]+)', link['href'], re.I) + if match: + model = clean_name(match.group(1)) + if model and not model.isdigit() and len(model) > 1: + models.add(model) + return sorted(models) + +def get_engines(brand, year, model): + brand_url = brand.lower() + model_url = model.lower().replace(' ', '+') + soup = get_soup(f"{BASE_URL}/{brand_url},{year},{model_url}") + if not soup: + return ['STANDARD'] + + engines = set() + for link in soup.find_all('a', href=True): + match = re.search(rf'/catalog/{brand_url},{year},{re.escape(model_url)},([^,/]+)', link['href'], re.I) + if match: + engine = clean_name(match.group(1)) + if engine and re.search(r'\d+\.?\d*L|V\d|I\d|HYBRID|ELECTRIC|DIESEL', engine, re.I): + engines.add(engine) + return sorted(engines) if engines else ['STANDARD'] + +def save_to_db(conn, brand, year, model, engine): + cursor = conn.cursor() + try: + cursor.execute("INSERT OR IGNORE INTO brands (name) VALUES (?)", (brand,)) + cursor.execute("SELECT id FROM brands WHERE name = ?", (brand,)) + brand_id = cursor.fetchone()[0] + + cursor.execute("INSERT OR IGNORE INTO years (year) VALUES (?)", (year,)) + cursor.execute("SELECT id FROM years WHERE year = ?", (year,)) + year_id = cursor.fetchone()[0] + + cursor.execute("INSERT OR IGNORE INTO engines (name) VALUES (?)", (engine,)) + cursor.execute("SELECT id FROM engines WHERE name = ?", (engine,)) + engine_id = cursor.fetchone()[0] + + cursor.execute("INSERT OR IGNORE INTO models (brand_id, name) VALUES (?, ?)", (brand_id, model)) + cursor.execute("SELECT id FROM models WHERE brand_id = ? AND name = ?", (brand_id, model)) + model_id = cursor.fetchone()[0] + + cursor.execute( + "INSERT OR IGNORE INTO model_year_engine (model_id, year_id, engine_id) VALUES (?, ?, ?)", + (model_id, year_id, engine_id) + ) + return cursor.rowcount > 0 + except: + return False + +def get_existing_years(conn, brand): + cursor = conn.cursor() + cursor.execute(""" + SELECT DISTINCT y.year FROM years y + JOIN model_year_engine mye ON y.id = mye.year_id + JOIN models m ON mye.model_id = m.id + JOIN brands b ON m.brand_id = b.id + WHERE b.name = ? + """, (brand,)) + return set(row[0] for row in cursor.fetchall()) + +def main(): + brand = "TOYOTA" + + print("=" * 50) + print(" SCRAPER TOYOTA - WINDOWS") + print(" 3 anos por lote, 60 seg pausa para VPN") + print("=" * 50) + + if not os.path.exists(DB_PATH): + print(f"\n[ERROR] No encuentro: {DB_PATH}") + print("Asegurate de correr desde C:\\Autopartes") + input("Presiona ENTER para salir...") + return + + conn = sqlite3.connect(DB_PATH) + existing = get_existing_years(conn, brand) + years_to_do = [y for y in MISSING_YEARS if y not in existing] + + if not years_to_do: + print("\nTodos los anos ya estan!") + conn.close() + return + + print(f"\nFaltan {len(years_to_do)} anos: {years_to_do[0]} a {years_to_do[-1]}") + + batches = [years_to_do[i:i+3] for i in range(0, len(years_to_do), 3)] + print(f"Lotes: {len(batches)}") + input("\nPresiona ENTER para comenzar...") + + total_saved = 0 + + for batch_num, batch in enumerate(batches, 1): + print(f"\n{'='*50}") + print(f"LOTE {batch_num}/{len(batches)}: {batch}") + print("="*50) + + for year in batch: + print(f"\n[{year}] ", end="", flush=True) + models = get_models(brand, year) + print(f"{len(models)} modelos") + + for model in models: + engines = get_engines(brand, year, model) + for engine in engines: + if save_to_db(conn, brand, year, model, engine): + total_saved += 1 + print(f" {model} - {engine}") + + conn.commit() + print(f"\n>> Guardado! Total nuevos: {total_saved}") + + if batch_num < len(batches): + print(f"\n{'*'*50}") + print(" PAUSA 60 SEG - CAMBIA EL VPN AHORA!") + print(f" Faltan {len(batches) - batch_num} lotes") + print("*"*50) + + for s in range(60, 0, -1): + print(f"\r Continua en {s}s... ", end="", flush=True) + time.sleep(1) + print() + + conn.close() + print(f"\n{'='*50}") + print(f" LISTO! Guardados: {total_saved} vehiculos nuevos") + print("="*50) + input("Presiona ENTER para salir...") + +if __name__ == "__main__": + main() diff --git a/test_dashboard.sh b/test_dashboard.sh new file mode 100755 index 0000000..19dfcaa --- /dev/null +++ b/test_dashboard.sh @@ -0,0 +1,54 @@ +#!/bin/bash +# Test script for the Vehicle Dashboard + +echo "Testing Vehicle Dashboard Setup" +echo "================================" + +# Check if the dashboard directory exists +if [ ! -d "/home/Autopartes/dashboard" ]; then + echo "Error: Dashboard directory not found!" + exit 1 +fi + +echo "Dashboard directory found." + +# Check if required files exist +files=("index.html" "dashboard.js" "server.py" "requirements.txt") +for file in "${files[@]}"; do + if [ ! -f "/home/Autopartes/dashboard/$file" ]; then + echo "Error: $file not found in dashboard directory!" + exit 1 + fi +done + +echo "All required files found." + +# Check if the vehicle database exists +if [ ! -f "/home/Autopartes/vehicle_database/vehicle_database.db" ]; then + echo "Error: Vehicle database not found!" + exit 1 +fi + +echo "Vehicle database found." + +# Check if Flask is available +if python3 -c "import flask" &> /dev/null; then + echo "Flask is available." +else + echo "Flask is not available. Installing..." + sudo apt-get update + sudo apt-get install -y python3-flask +fi + +# Check if the server is running +if pgrep -f "server.py" > /dev/null; then + echo "Dashboard server is running on http://localhost:5000" + echo "You can access the dashboard now!" +else + echo "Dashboard server is not running." + echo "To start it, run: cd /home/Autopartes/dashboard && python3 server.py" +fi + +echo "" +echo "Dashboard Test Complete!" +echo "Access the dashboard at: http://localhost:5000" \ No newline at end of file diff --git a/vehicle_database/GETTING_STARTED.md b/vehicle_database/GETTING_STARTED.md new file mode 100644 index 0000000..a7a019d --- /dev/null +++ b/vehicle_database/GETTING_STARTED.md @@ -0,0 +1,82 @@ +# Vehicle Database - Getting Started Guide + +## Overview +This project provides a comprehensive database system for storing information about vehicle brands, models, years, and engines. The database is built using SQLite and managed through Python scripts. + +## Database Structure +The database consists of five main tables: +- **brands**: Vehicle manufacturers (Toyota, Ford, etc.) +- **models**: Vehicle models (Camry, F-150, etc.) +- **engines**: Engine specifications (2JZ-GTE, EcoBoost, etc.) +- **years**: Calendar years for vehicle production +- **model_year_engine**: Junction table linking all entities with trim levels and specifications + +## Setup and Usage + +### 1. Initial Setup +Run the setup script to initialize the database: +```bash +cd vehicle_database +./setup.sh +``` + +### 2. Querying the Database +Use the interactive query interface: +```bash +python3 scripts/query_interface.py +``` + +The query interface allows you to: +- Search for vehicles by brand, model, year, or engine +- Browse all available brands +- View models for specific brands +- See production years for specific models + +### 3. Managing the Database +Use the database manager for programmatic access: +```bash +python3 scripts/database_manager.py +``` + +### 4. Importing Data +To import data from CSV files: +1. Prepare your data in the required CSV format +2. Use the CSV importer functionality in your own scripts + +Sample CSV files are provided in the `data/` directory. + +## Example Queries + +The system supports various search options: +- Find all vehicles by a specific brand +- Search for a specific model across all years +- Filter by engine type or specifications +- Look up trim levels and drivetrain configurations + +## Extending the Database + +To add more data: +1. Use the Python API in `scripts/database_manager.py` +2. Directly execute SQL commands on the SQLite database +3. Import data from CSV files using the structure provided + +## File Structure +``` +vehicle_database/ +├── sql/ +│ └── schema.sql # Database schema +├── scripts/ +│ ├── database_manager.py # Main database manager +│ ├── query_interface.py # Interactive query interface +│ └── csv_importer.py # CSV import functionality +├── data/ # Sample CSV data files +├── vehicle_database.db # SQLite database file +├── setup.sh # Setup script +└── README.md # Project documentation +``` + +## Next Steps +1. Explore the database using the query interface +2. Add your own vehicle data +3. Customize the schema if needed for your specific requirements +4. Extend the Python scripts with additional functionality \ No newline at end of file diff --git a/vehicle_database/README.md b/vehicle_database/README.md new file mode 100644 index 0000000..a89ed71 --- /dev/null +++ b/vehicle_database/README.md @@ -0,0 +1,106 @@ +# Vehicle Database + +A comprehensive database system for storing information about vehicle brands, models, years, and engines. + +## Overview + +This project provides a structured database for vehicle information with the following entities: + +- **Brands**: Vehicle manufacturers with details like country of origin and founding year +- **Models**: Vehicle models with body type, generation, and production years +- **Years**: Calendar years for vehicle production +- **Engines**: Engine specifications including displacement, cylinders, power, and fuel type +- **Model-Year-Engine**: Junction table linking all entities with trim levels and specifications + +## Database Schema + +The database uses SQLite and consists of the following tables: + +### `brands` +- `id`: Primary key +- `name`: Brand name (e.g., Toyota, Ford) +- `country`: Country of origin +- `founded_year`: Year the company was founded + +### `engines` +- `id`: Primary key +- `name`: Engine name +- `displacement_cc`: Engine displacement in cubic centimeters +- `cylinders`: Number of cylinders +- `fuel_type`: Type of fuel (gasoline, diesel, electric, hybrid) +- `power_hp`: Horsepower +- `torque_nm`: Torque in Newton meters +- `engine_code`: Manufacturer engine code + +### `models` +- `id`: Primary key +- `brand_id`: Foreign key to brands table +- `name`: Model name (e.g., Camry, Civic) +- `body_type`: Body style (sedan, SUV, truck, etc.) +- `generation`: Model generation +- `production_start_year`: Year production started +- `production_end_year`: Year production ended (NULL if still in production) + +### `years` +- `id`: Primary key +- `year`: Calendar year + +### `model_year_engine` +- `id`: Primary key +- `model_id`: Foreign key to models table +- `year_id`: Foreign key to years table +- `engine_id`: Foreign key to engines table +- `trim_level`: Trim level (e.g., base, luxury, sport) +- `drivetrain`: Drive system (FWD, RWD, AWD, 4WD) +- `transmission`: Transmission type (manual, automatic, CVT) + +## Setup + +1. Install Python 3.x if not already installed +2. Clone or download this repository +3. Run the database manager script: + +```bash +cd vehicle_database +python scripts/database_manager.py +``` + +This will create the database, populate it with sample data, and run example queries. + +## Usage + +The `VehicleDatabaseManager` class provides methods to: + +- Create and manage the database schema +- Insert new brands, models, engines, and years +- Query vehicle information +- Link models, years, and engines with trim levels and specifications + +## Sample Queries + +The script demonstrates several query patterns: + +- Get all brands +- Get models for a specific brand +- Search for specific vehicles by brand, model, year, or engine +- Retrieve comprehensive vehicle information + +## Extending the Database + +To add more data, you can: + +1. Use the provided Python API +2. Directly execute SQL commands on the SQLite database +3. Import data from CSV files using the provided structure + +## File Structure + +``` +vehicle_database/ +├── sql/ +│ └── schema.sql # Database schema +├── scripts/ +│ └── database_manager.py # Python database manager +├── data/ # Directory for data files +└── README.md # This file +``` \ No newline at end of file diff --git a/vehicle_database/data/brands.csv b/vehicle_database/data/brands.csv new file mode 100644 index 0000000..e8393e4 --- /dev/null +++ b/vehicle_database/data/brands.csv @@ -0,0 +1,6 @@ +name,country,founded_year +Mercedes-Benz,Germany,1926 +Audi,Germany,1909 +Nissan,Japan,1933 +Chevrolet,USA,1911 +Volkswagen,Germany,1937 diff --git a/vehicle_database/data/engines.csv b/vehicle_database/data/engines.csv new file mode 100644 index 0000000..da2befa --- /dev/null +++ b/vehicle_database/data/engines.csv @@ -0,0 +1,5 @@ +name,displacement_cc,cylinders,fuel_type,power_hp,torque_nm,engine_code +VQ35DE,3500,6,gasoline,280,260,VQ35DE +LS3,6200,8,gasoline,430,424,LS3 +EA888,2000,4,gasoline,228,258,EA888 +M274,2000,4,gasoline,241,273,M274 diff --git a/vehicle_database/data/models.csv b/vehicle_database/data/models.csv new file mode 100644 index 0000000..a83bda6 --- /dev/null +++ b/vehicle_database/data/models.csv @@ -0,0 +1,5 @@ +brand_name,name,body_type,generation,production_start_year,production_end_year +Toyota,Corolla,sedan,E210,2018, +Honda,Accord,sedan,X,2018, +Ford,F-150,truck,13th Gen,2015, +BMW,3 Series,sedan,G20,2018, diff --git a/vehicle_database/scripts/csv_importer.py b/vehicle_database/scripts/csv_importer.py new file mode 100644 index 0000000..64a800a --- /dev/null +++ b/vehicle_database/scripts/csv_importer.py @@ -0,0 +1,152 @@ +#!/usr/bin/env python3 +""" +CSV Import Script for Vehicle Database +This script allows importing vehicle data from CSV files +""" + +import csv +import sqlite3 +import os +from typing import Dict, List + + +class CSVImporter: + def __init__(self, db_path: str = "vehicle_database.db"): + self.db_path = db_path + + def import_brands_from_csv(self, csv_file: str): + """Import brands from a CSV file""" + conn = sqlite3.connect(self.db_path) + cursor = conn.cursor() + + with open(csv_file, 'r', newline='', encoding='utf-8') as file: + reader = csv.DictReader(file) + for row in reader: + cursor.execute( + "INSERT OR IGNORE INTO brands (name, country, founded_year) VALUES (?, ?, ?)", + (row['name'], row.get('country'), row.get('founded_year')) + ) + + conn.commit() + conn.close() + print(f"Imported brands from {csv_file}") + + def import_engines_from_csv(self, csv_file: str): + """Import engines from a CSV file""" + conn = sqlite3.connect(self.db_path) + cursor = conn.cursor() + + with open(csv_file, 'r', newline='', encoding='utf-8') as file: + reader = csv.DictReader(file) + for row in reader: + cursor.execute( + """INSERT OR IGNORE INTO engines + (name, displacement_cc, cylinders, fuel_type, power_hp, torque_nm, engine_code) + VALUES (?, ?, ?, ?, ?, ?, ?)""", + ( + row['name'], + row.get('displacement_cc'), + row.get('cylinders'), + row.get('fuel_type'), + row.get('power_hp'), + row.get('torque_nm'), + row.get('engine_code') + ) + ) + + conn.commit() + conn.close() + print(f"Imported engines from {csv_file}") + + def import_models_from_csv(self, csv_file: str): + """Import models from a CSV file""" + conn = sqlite3.connect(self.db_path) + cursor = conn.cursor() + + with open(csv_file, 'r', newline='', encoding='utf-8') as file: + reader = csv.DictReader(file) + + # First, create a mapping of brand names to IDs + cursor.execute("SELECT id, name FROM brands") + brand_map = {row[1]: row[0] for row in cursor.fetchall()} + + for row in reader: + brand_id = brand_map.get(row['brand_name']) + if brand_id is None: + print(f"Warning: Brand '{row['brand_name']}' not found in database") + continue + + cursor.execute( + """INSERT OR IGNORE INTO models + (brand_id, name, body_type, generation, production_start_year, production_end_year) + VALUES (?, ?, ?, ?, ?, ?)""", + ( + brand_id, + row['name'], + row.get('body_type'), + row.get('generation'), + row.get('production_start_year'), + row.get('production_end_year') + ) + ) + + conn.commit() + conn.close() + print(f"Imported models from {csv_file}") + + def create_sample_csv_files(self): + """Create sample CSV files with example data""" + os.makedirs("data", exist_ok=True) + + # Sample brands CSV + with open("data/brands.csv", "w", newline="", encoding="utf-8") as csvfile: + fieldnames = ["name", "country", "founded_year"] + writer = csv.DictWriter(csvfile, fieldnames=fieldnames) + writer.writeheader() + writer.writerows([ + {"name": "Mercedes-Benz", "country": "Germany", "founded_year": 1926}, + {"name": "Audi", "country": "Germany", "founded_year": 1909}, + {"name": "Nissan", "country": "Japan", "founded_year": 1933}, + {"name": "Chevrolet", "country": "USA", "founded_year": 1911}, + {"name": "Volkswagen", "country": "Germany", "founded_year": 1937} + ]) + + # Sample engines CSV + with open("data/engines.csv", "w", newline="", encoding="utf-8") as csvfile: + fieldnames = ["name", "displacement_cc", "cylinders", "fuel_type", "power_hp", "torque_nm", "engine_code"] + writer = csv.DictWriter(csvfile, fieldnames=fieldnames) + writer.writeheader() + writer.writerows([ + {"name": "VQ35DE", "displacement_cc": 3500, "cylinders": 6, "fuel_type": "gasoline", "power_hp": 280, "torque_nm": 260, "engine_code": "VQ35DE"}, + {"name": "LS3", "displacement_cc": 6200, "cylinders": 8, "fuel_type": "gasoline", "power_hp": 430, "torque_nm": 424, "engine_code": "LS3"}, + {"name": "EA888", "displacement_cc": 2000, "cylinders": 4, "fuel_type": "gasoline", "power_hp": 228, "torque_nm": 258, "engine_code": "EA888"}, + {"name": "M274", "displacement_cc": 2000, "cylinders": 4, "fuel_type": "gasoline", "power_hp": 241, "torque_nm": 273, "engine_code": "M274"} + ]) + + # Sample models CSV + with open("data/models.csv", "w", newline="", encoding="utf-8") as csvfile: + fieldnames = ["brand_name", "name", "body_type", "generation", "production_start_year", "production_end_year"] + writer = csv.DictWriter(csvfile, fieldnames=fieldnames) + writer.writeheader() + writer.writerows([ + {"brand_name": "Toyota", "name": "Corolla", "body_type": "sedan", "generation": "E210", "production_start_year": 2018, "production_end_year": None}, + {"brand_name": "Honda", "name": "Accord", "body_type": "sedan", "generation": "X", "production_start_year": 2018, "production_end_year": None}, + {"brand_name": "Ford", "name": "F-150", "body_type": "truck", "generation": "13th Gen", "production_start_year": 2015, "production_end_year": None}, + {"brand_name": "BMW", "name": "3 Series", "body_type": "sedan", "generation": "G20", "production_start_year": 2018, "production_end_year": None} + ]) + + print("Sample CSV files created in data/ directory") + + +def main(): + importer = CSVImporter() + + # Create sample CSV files + importer.create_sample_csv_files() + + # Import sample data (these would be imported by the main script, but showing the functionality) + print("CSV import functionality created. Sample CSV files are in the data/ directory.") + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/vehicle_database/scripts/database_manager.py b/vehicle_database/scripts/database_manager.py new file mode 100644 index 0000000..bb83814 --- /dev/null +++ b/vehicle_database/scripts/database_manager.py @@ -0,0 +1,323 @@ +#!/usr/bin/env python3 +""" +Vehicle Database Manager +A script to initialize and manage the vehicle database with brands, years, models, and engines +""" + +import sqlite3 +import os +from datetime import datetime +from typing import List, Tuple + + +class VehicleDatabaseManager: + def __init__(self, db_path: str = "vehicle_database.db"): + self.db_path = db_path + self.connection = None + + def connect(self): + """Connect to the SQLite database""" + self.connection = sqlite3.connect(self.db_path) + self.connection.row_factory = sqlite3.Row # Enable column access by name + print(f"Connected to database: {self.db_path}") + + def disconnect(self): + """Close the database connection""" + if self.connection: + self.connection.close() + print("Disconnected from database") + + def create_tables(self, schema_file: str = "sql/schema.sql"): + """Create tables from schema file""" + if not os.path.exists(schema_file): + raise FileNotFoundError(f"Schema file not found: {schema_file}") + + with open(schema_file, 'r') as f: + schema = f.read() + + if self.connection: + cursor = self.connection.cursor() + cursor.executescript(schema) + self.connection.commit() + print("Tables created successfully") + + def insert_brand(self, name: str, country: str = None, founded_year: int = None) -> int: + """Insert a new brand and return its ID""" + cursor = self.connection.cursor() + cursor.execute( + "INSERT OR IGNORE INTO brands (name, country, founded_year) VALUES (?, ?, ?)", + (name, country, founded_year) + ) + self.connection.commit() + # Return the ID of the inserted or existing brand + cursor.execute("SELECT id FROM brands WHERE name = ?", (name,)) + return cursor.fetchone()[0] + + def insert_engine(self, name: str, displacement_cc: float = None, cylinders: int = None, + fuel_type: str = None, power_hp: int = None, torque_nm: int = None, + engine_code: str = None) -> int: + """Insert a new engine and return its ID""" + cursor = self.connection.cursor() + cursor.execute( + """INSERT OR IGNORE INTO engines + (name, displacement_cc, cylinders, fuel_type, power_hp, torque_nm, engine_code) + VALUES (?, ?, ?, ?, ?, ?, ?)""", + (name, displacement_cc, cylinders, fuel_type, power_hp, torque_nm, engine_code) + ) + self.connection.commit() + # Return the ID of the inserted or existing engine + cursor.execute("SELECT id FROM engines WHERE name = ?", (name,)) + return cursor.fetchone()[0] + + def insert_model(self, brand_id: int, name: str, body_type: str = None, + generation: str = None, production_start_year: int = None, + production_end_year: int = None) -> int: + """Insert a new model and return its ID""" + cursor = self.connection.cursor() + cursor.execute( + """INSERT OR IGNORE INTO models + (brand_id, name, body_type, generation, production_start_year, production_end_year) + VALUES (?, ?, ?, ?, ?, ?)""", + (brand_id, name, body_type, generation, production_start_year, production_end_year) + ) + self.connection.commit() + # Return the ID of the inserted or existing model + cursor.execute("SELECT id FROM models WHERE brand_id = ? AND name = ?", (brand_id, name)) + return cursor.fetchone()[0] + + def insert_year(self, year: int) -> int: + """Insert a new year and return its ID""" + cursor = self.connection.cursor() + cursor.execute("INSERT OR IGNORE INTO years (year) VALUES (?)", (year,)) + self.connection.commit() + # Return the ID of the inserted or existing year + cursor.execute("SELECT id FROM years WHERE year = ?", (year,)) + return cursor.fetchone()[0] + + def insert_model_year_engine(self, model_id: int, year_id: int, engine_id: int, + trim_level: str = None, drivetrain: str = None, + transmission: str = None): + """Insert a model-year-engine combination""" + cursor = self.connection.cursor() + cursor.execute( + """INSERT OR REPLACE INTO model_year_engine + (model_id, year_id, engine_id, trim_level, drivetrain, transmission) + VALUES (?, ?, ?, ?, ?, ?)""", + (model_id, year_id, engine_id, trim_level, drivetrain, transmission) + ) + self.connection.commit() + + def get_brands(self) -> List[Tuple]: + """Retrieve all brands""" + cursor = self.connection.cursor() + cursor.execute("SELECT * FROM brands ORDER BY name") + return cursor.fetchall() + + def get_models_by_brand(self, brand_id: int) -> List[Tuple]: + """Retrieve all models for a specific brand""" + cursor = self.connection.cursor() + cursor.execute(""" + SELECT m.*, b.name as brand_name + FROM models m + JOIN brands b ON m.brand_id = b.id + WHERE m.brand_id = ? + ORDER BY m.name + """, (brand_id,)) + return cursor.fetchall() + + def get_vehicle_info(self, brand_name: str = None, model_name: str = None, + year: int = None, engine_name: str = None) -> List[Tuple]: + """Get comprehensive vehicle information based on filters""" + query = """ + SELECT + b.name AS brand, + m.name AS model, + y.year, + e.name AS engine, + e.displacement_cc, + e.cylinders, + e.fuel_type, + e.power_hp, + e.torque_nm, + e.engine_code, + mye.trim_level, + mye.drivetrain, + mye.transmission + FROM model_year_engine mye + JOIN models m ON mye.model_id = m.id + JOIN brands b ON m.brand_id = b.id + JOIN years y ON mye.year_id = y.id + JOIN engines e ON mye.engine_id = e.id + WHERE 1=1 + """ + + params = [] + if brand_name: + query += " AND b.name LIKE ?" + params.append(f"%{brand_name}%") + if model_name: + query += " AND m.name LIKE ?" + params.append(f"%{model_name}%") + if year: + query += " AND y.year = ?" + params.append(year) + if engine_name: + query += " AND e.name LIKE ?" + params.append(f"%{engine_name}%") + + query += " ORDER BY b.name, m.name, y.year" + + cursor = self.connection.cursor() + cursor.execute(query, params) + return cursor.fetchall() + + +def populate_sample_data(db_manager: VehicleDatabaseManager): + """Populate the database with sample data""" + print("Populating sample data...") + + # Insert sample brands + toyota_id = db_manager.insert_brand("Toyota", "Japan", 1937) + honda_id = db_manager.insert_brand("Honda", "Japan", 1948) + ford_id = db_manager.insert_brand("Ford", "USA", 1903) + bmw_id = db_manager.insert_brand("BMW", "Germany", 1916) + + # Insert sample engines + engine_2jz = db_manager.insert_engine( + "2JZ-GTE", + displacement_cc=3000, + cylinders=6, + fuel_type="gasoline", + power_hp=320, + torque_nm=450, + engine_code="2JZ-GTE" + ) + + engine_k20 = db_manager.insert_engine( + "K20C1", + displacement_cc=2000, + cylinders=4, + fuel_type="gasoline", + power_hp=280, + torque_nm=260, + engine_code="K20C1" + ) + + engine_ecoboost = db_manager.insert_engine( + "EcoBoost V6", + displacement_cc=3500, + cylinders=6, + fuel_type="gasoline", + power_hp=375, + torque_nm=470, + engine_code="EcoBoost V6" + ) + + engine_n52 = db_manager.insert_engine( + "N52B30", + displacement_cc=3000, + cylinders=6, + fuel_type="gasoline", + power_hp=255, + torque_nm=310, + engine_code="N52B30" + ) + + # Insert sample models + camry_id = db_manager.insert_model( + toyota_id, + "Camry", + body_type="sedan", + generation="XV70", + production_start_year=2017, + production_end_year=None + ) + + civic_id = db_manager.insert_model( + honda_id, + "Civic", + body_type="sedan", + generation="XI", + production_start_year=2016, + production_end_year=None + ) + + mustang_id = db_manager.insert_model( + ford_id, + "Mustang", + body_type="coupe", + generation="S550", + production_start_year=2015, + production_end_year=None + ) + + x3_id = db_manager.insert_model( + bmw_id, + "X3", + body_type="suv", + generation="G01", + production_start_year=2017, + production_end_year=None + ) + + # Insert years + year_2020 = db_manager.insert_year(2020) + year_2021 = db_manager.insert_year(2021) + year_2022 = db_manager.insert_year(2022) + + # Link models, years, and engines + db_manager.insert_model_year_engine(civic_id, year_2020, engine_k20, "Sport", "FWD", "automatic") + db_manager.insert_model_year_engine(mustang_id, year_2020, engine_ecoboost, "GT", "RWD", "automatic") + db_manager.insert_model_year_engine(x3_id, year_2021, engine_n52, "xDrive30i", "AWD", "automatic") + db_manager.insert_model_year_engine(camry_id, year_2022, engine_k20, "SE", "FWD", "automatic") + + print("Sample data populated successfully!") + + +def main(): + # Initialize the database manager + db_manager = VehicleDatabaseManager() + + try: + # Connect to database + db_manager.connect() + + # Create tables + db_manager.create_tables() + + # Populate with sample data + populate_sample_data(db_manager) + + # Demonstrate queries + print("\n--- All Brands ---") + brands = db_manager.get_brands() + for brand in brands: + print(f"ID: {brand['id']}, Name: {brand['name']}, Country: {brand['country']}") + + print(f"\n--- Models for Toyota (ID: {brands[0]['id']}) ---") + toyota_models = db_manager.get_models_by_brand(brands[0]['id']) + for model in toyota_models: + print(f"Model: {model['name']}, Body Type: {model['body_type']}") + + print("\n--- All Vehicle Information ---") + all_vehicles = db_manager.get_vehicle_info() + for vehicle in all_vehicles: + print(f"{vehicle['brand']} {vehicle['model']} {vehicle['year']} - " + f"{vehicle['engine']} ({vehicle['power_hp']} HP)") + + print("\n--- Search for Honda Civic ---") + honda_civic = db_manager.get_vehicle_info(brand_name="Honda", model_name="Civic") + for vehicle in honda_civic: + print(f"{vehicle['brand']} {vehicle['model']} {vehicle['year']} - " + f"{vehicle['engine']} ({vehicle['power_hp']} HP)") + + except Exception as e: + print(f"An error occurred: {e}") + + finally: + # Close the connection + db_manager.disconnect() + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/vehicle_database/scripts/query_interface.py b/vehicle_database/scripts/query_interface.py new file mode 100644 index 0000000..289203a --- /dev/null +++ b/vehicle_database/scripts/query_interface.py @@ -0,0 +1,193 @@ +#!/usr/bin/env python3 +""" +Simple Query Interface for Vehicle Database +Provides a command-line interface for searching the vehicle database +""" + +import sqlite3 +from typing import List, Tuple + + +class VehicleQueryInterface: + def __init__(self, db_path: str = "vehicle_database.db"): + self.db_path = db_path + + def search_vehicles(self, brand: str = None, model: str = None, year: int = None, + engine: str = None, limit: int = 50) -> List[Tuple]: + """Search for vehicles based on various criteria""" + conn = sqlite3.connect(self.db_path) + conn.row_factory = sqlite3.Row # Enable column access by name + cursor = conn.cursor() + + query = """ + SELECT + b.name AS brand, + m.name AS model, + y.year, + e.name AS engine, + e.displacement_cc, + e.cylinders, + e.fuel_type, + e.power_hp, + mye.trim_level, + mye.drivetrain, + mye.transmission + FROM model_year_engine mye + JOIN models m ON mye.model_id = m.id + JOIN brands b ON m.brand_id = b.id + JOIN years y ON mye.year_id = y.id + JOIN engines e ON mye.engine_id = e.id + WHERE 1=1 + """ + + params = [] + if brand: + query += " AND b.name LIKE ?" + params.append(f"%{brand}%") + if model: + query += " AND m.name LIKE ?" + params.append(f"%{model}%") + if year: + query += " AND y.year = ?" + params.append(year) + if engine: + query += " AND e.name LIKE ?" + params.append(f"%{engine}%") + + query += f" ORDER BY b.name, m.name, y.year LIMIT {limit}" + + cursor.execute(query, params) + results = cursor.fetchall() + conn.close() + + return results + + def get_all_brands(self) -> List[Tuple]: + """Get a list of all brands in the database""" + conn = sqlite3.connect(self.db_path) + conn.row_factory = sqlite3.Row + cursor = conn.cursor() + + cursor.execute("SELECT DISTINCT name FROM brands ORDER BY name") + results = cursor.fetchall() + conn.close() + + return [row[0] for row in results] + + def get_models_by_brand(self, brand_name: str) -> List[Tuple]: + """Get all models for a specific brand""" + conn = sqlite3.connect(self.db_path) + conn.row_factory = sqlite3.Row + cursor = conn.cursor() + + cursor.execute(""" + SELECT DISTINCT m.name + FROM models m + JOIN brands b ON m.brand_id = b.id + WHERE b.name = ? + ORDER BY m.name + """, (brand_name,)) + results = cursor.fetchall() + conn.close() + + return [row[0] for row in results] + + def get_years_for_model(self, model_name: str) -> List[int]: + """Get all years for a specific model""" + conn = sqlite3.connect(self.db_path) + conn.row_factory = sqlite3.Row + cursor = conn.cursor() + + cursor.execute(""" + SELECT DISTINCT y.year + FROM model_year_engine mye + JOIN models m ON mye.model_id = m.id + JOIN years y ON mye.year_id = y.id + WHERE m.name = ? + ORDER BY y.year + """, (model_name,)) + results = cursor.fetchall() + conn.close() + + return [row[0] for row in results] + + +def main(): + interface = VehicleQueryInterface() + + print("Vehicle Database Query Interface") + print("=================================") + + while True: + print("\nOptions:") + print("1. Search vehicles") + print("2. List all brands") + print("3. List models for a brand") + print("4. List years for a model") + print("5. Exit") + + choice = input("\nEnter your choice (1-5): ").strip() + + if choice == "1": + print("\nSearch for vehicles:") + brand = input("Brand (optional): ").strip() or None + model = input("Model (optional): ").strip() or None + year_input = input("Year (optional): ").strip() + year = int(year_input) if year_input else None + engine = input("Engine (optional): ").strip() or None + + results = interface.search_vehicles(brand=brand, model=model, year=year, engine=engine) + + if results: + print(f"\nFound {len(results)} result(s):") + print("-" * 100) + for i, vehicle in enumerate(results, 1): + print(f"{i:2d}. {vehicle['year']} {vehicle['brand']} {vehicle['model']}") + print(f" Engine: {vehicle['engine']} ({vehicle['power_hp']} HP, {vehicle['displacement_cc']} cc)") + print(f" Trim: {vehicle['trim_level']}, Drivetrain: {vehicle['drivetrain']}, Transmission: {vehicle['transmission']}") + print() + else: + print("No vehicles found matching your criteria.") + + elif choice == "2": + brands = interface.get_all_brands() + print(f"\nAll brands ({len(brands)}):") + for i, brand in enumerate(brands, 1): + print(f"{i:2d}. {brand}") + + elif choice == "3": + brand = input("Enter brand name: ").strip() + if brand: + models = interface.get_models_by_brand(brand) + if models: + print(f"\nModels for {brand} ({len(models)}):") + for i, model in enumerate(models, 1): + print(f"{i:2d}. {model}") + else: + print(f"No models found for brand: {brand}") + else: + print("Please enter a brand name.") + + elif choice == "4": + model = input("Enter model name: ").strip() + if model: + years = interface.get_years_for_model(model) + if years: + print(f"\nYears for {model} ({len(years)}):") + for i, year in enumerate(years, 1): + print(f"{i:2d}. {year}") + else: + print(f"No years found for model: {model}") + else: + print("Please enter a model name.") + + elif choice == "5": + print("Thank you for using the Vehicle Database Query Interface!") + break + + else: + print("Invalid choice. Please enter 1-5.") + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/vehicle_database/scripts/vehicle_database.db b/vehicle_database/scripts/vehicle_database.db new file mode 100644 index 0000000..e69de29 diff --git a/vehicle_database/setup.sh b/vehicle_database/setup.sh new file mode 100755 index 0000000..e425d22 --- /dev/null +++ b/vehicle_database/setup.sh @@ -0,0 +1,29 @@ +#!/bin/bash +# Setup script for Vehicle Database + +echo "Vehicle Database Setup" +echo "======================" + +# Check if Python3 is available +if ! command -v python3 &> /dev/null; then + echo "Python3 is required but not installed. Please install Python3 first." + exit 1 +fi + +echo "Python3 is available." + +# Check if the database already exists +if [ -f "vehicle_database.db" ]; then + echo "Database already exists." +else + echo "Creating new database with sample data..." + python3 scripts/database_manager.py +fi + +echo "" +echo "Setup complete! Available scripts:" +echo "1. Query Interface: python3 scripts/query_interface.py" +echo "2. Database Manager: python3 scripts/database_manager.py" +echo "3. CSV Importer: python3 scripts/csv_importer.py" +echo "" +echo "To start querying the database, run: python3 scripts/query_interface.py" \ No newline at end of file diff --git a/vehicle_database/sql/schema.sql b/vehicle_database/sql/schema.sql new file mode 100644 index 0000000..ea3d866 --- /dev/null +++ b/vehicle_database/sql/schema.sql @@ -0,0 +1,66 @@ +-- Vehicle Database Schema +-- Tables for storing vehicle information: brands, years, models, and engines + +-- Table for vehicle brands/manufacturers +CREATE TABLE IF NOT EXISTS brands ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL UNIQUE, + country TEXT, + founded_year INTEGER, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP +); + +-- Table for engine specifications +CREATE TABLE IF NOT EXISTS engines ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL, + displacement_cc REAL, -- Engine displacement in cubic centimeters + cylinders INTEGER, -- Number of cylinders + fuel_type TEXT CHECK(fuel_type IN ('gasoline', 'diesel', 'electric', 'hybrid', 'other')), + power_hp INTEGER, -- Horsepower + torque_nm INTEGER, -- Torque in Newton meters + engine_code TEXT, -- Manufacturer engine code + created_at DATETIME DEFAULT CURRENT_TIMESTAMP +); + +-- Table for vehicle models +CREATE TABLE IF NOT EXISTS models ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + brand_id INTEGER NOT NULL, + name TEXT NOT NULL, + body_type TEXT CHECK(body_type IN ('sedan', 'hatchback', 'suv', 'truck', 'coupe', 'convertible', 'wagon', 'van', 'other')), + generation TEXT, -- Model generation (e.g., MK1, MK2) + production_start_year INTEGER, + production_end_year INTEGER, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (brand_id) REFERENCES brands(id) +); + +-- Table for years (to link with models) +CREATE TABLE IF NOT EXISTS years ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + year INTEGER NOT NULL UNIQUE, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP +); + +-- Junction table to connect models, years, and engines +CREATE TABLE IF NOT EXISTS model_year_engine ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + model_id INTEGER NOT NULL, + year_id INTEGER NOT NULL, + engine_id INTEGER NOT NULL, + trim_level TEXT, -- Trim level (e.g., base, luxury, sport) + drivetrain TEXT CHECK(drivetrain IN ('FWD', 'RWD', 'AWD', '4WD', 'other')), + transmission TEXT CHECK(transmission IN ('manual', 'automatic', 'CVT', 'other')), + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (model_id) REFERENCES models(id), + FOREIGN KEY (year_id) REFERENCES years(id), + FOREIGN KEY (engine_id) REFERENCES engines(id), + UNIQUE(model_id, year_id, engine_id, trim_level) -- Prevent duplicate combinations +); + +-- Indexes for better performance +CREATE INDEX IF NOT EXISTS idx_models_brand ON models(brand_id); +CREATE INDEX IF NOT EXISTS idx_model_year_engine_model ON model_year_engine(model_id); +CREATE INDEX IF NOT EXISTS idx_model_year_engine_year ON model_year_engine(year_id); +CREATE INDEX IF NOT EXISTS idx_model_year_engine_engine ON model_year_engine(engine_id); \ No newline at end of file diff --git a/vehicle_database/vehicle_database.db b/vehicle_database/vehicle_database.db new file mode 100644 index 0000000..1b7afaa Binary files /dev/null and b/vehicle_database/vehicle_database.db differ diff --git a/vehicle_scraper/manual_input.py b/vehicle_scraper/manual_input.py new file mode 100644 index 0000000..98956a0 --- /dev/null +++ b/vehicle_scraper/manual_input.py @@ -0,0 +1,175 @@ +""" +Manual Data Extraction Guide for RockAuto.com + +Since RockAuto has strong anti-bot measures, here's a manual approach to extract vehicle data: + +1. Visit https://www.rockauto.com/ +2. Click on "Catalog" in the navigation menu +3. You'll see a list of vehicle manufacturers (makes) +4. For each make, manually note down the models, years, and engines + +This script provides a framework to input the manually collected data into your database. +""" + +import sqlite3 +from typing import List, Dict + + +class ManualDataInput: + def __init__(self, db_path: str = "../vehicle_database/vehicle_database.db"): + self.db_path = db_path + + def add_vehicle_data(self, make: str, model: str, year: int, engine: str = "Unknown"): + """Add a single vehicle entry to the database""" + conn = sqlite3.connect(self.db_path) + cursor = conn.cursor() + + try: + # Insert brand + cursor.execute( + "INSERT OR IGNORE INTO brands (name) VALUES (?)", + (make,) + ) + cursor.execute("SELECT id FROM brands WHERE name = ?", (make,)) + brand_id = cursor.fetchone()[0] + + # Insert year + cursor.execute( + "INSERT OR IGNORE INTO years (year) VALUES (?)", + (year,) + ) + cursor.execute("SELECT id FROM years WHERE year = ?", (year,)) + year_id = cursor.fetchone()[0] + + # Insert engine + cursor.execute( + "INSERT OR IGNORE INTO engines (name) VALUES (?)", + (engine,) + ) + cursor.execute("SELECT id FROM engines WHERE name = ?", (engine,)) + engine_id = cursor.fetchone()[0] + + # Insert model + cursor.execute( + "INSERT OR IGNORE INTO models (brand_id, name) VALUES (?, ?)", + (brand_id, model) + ) + cursor.execute("SELECT id FROM models WHERE brand_id = ? AND name = ?", (brand_id, model)) + model_id = cursor.fetchone()[0] + + # Link model, year, and engine + cursor.execute( + """INSERT OR IGNORE INTO model_year_engine + (model_id, year_id, engine_id) VALUES (?, ?, ?)""", + (model_id, year_id, engine_id) + ) + + conn.commit() + print(f"Added: {year} {make} {model} with {engine}") + + except Exception as e: + print(f"Error adding vehicle: {e}") + finally: + conn.close() + + def add_multiple_vehicles(self, vehicles: List[Dict]): + """Add multiple vehicles at once""" + for vehicle in vehicles: + self.add_vehicle_data( + make=vehicle.get('make', ''), + model=vehicle.get('model', ''), + year=vehicle.get('year', 0), + engine=vehicle.get('engine', 'Unknown') + ) + + def show_extraction_guide(self): + """Show the manual extraction guide""" + guide = """ + ================================================ + Manual RockAuto Data Extraction Guide + ================================================ + + 1. OPEN YOUR WEB BROWSER and go to: https://www.rockauto.com + + 2. CLICK on the "Catalog" link in the navigation menu + + 3. YOU WILL SEE a list of vehicle manufacturers (makes) like: + - Acura + - Audi + - BMW + - Chevrolet + - Ford + - Honda + - Toyota + - And many more... + + 4. FOR EACH MANUFACTURER: + a) Click on the manufacturer name + b) You'll see a page with vehicle models organized by year + c) Note down the models and years you see + d) Example format: 2020 Honda Civic, 2019 Ford F-150, etc. + + 5. TO FIND ENGINE INFORMATION: + a) Click on a specific model/year combination + b) You'll see parts categories for that vehicle + c) Look for "Engine" or "Engine Mechanical" category + d) Note down the engine type/specifications + + 6. USE THE FOLLOWING COMMANDS to add data to your database: + + Example Python commands: + >>> from manual_input import ManualDataInput + >>> input_tool = ManualDataInput() + >>> input_tool.add_vehicle_data("Toyota", "Camry", 2020, "2.5L 4-Cylinder") + >>> input_tool.add_vehicle_data("Honda", "Civic", 2019, "1.5L Turbo") + + Or add multiple at once: + >>> vehicles = [ + ... {"make": "Ford", "model": "F-150", "year": 2021, "engine": "3.5L V6"}, + ... {"make": "BMW", "model": "X3", "year": 2020, "engine": "2.0L 4-Cylinder Turbo"} + ... ] + >>> input_tool.add_multiple_vehicles(vehicles) + + 7. TIPS FOR EFFICIENT DATA COLLECTION: + - Focus on popular makes/models first + - Record data in a spreadsheet as you go + - Take screenshots of pages for reference + - Be systematic - go alphabetically or by make popularity + + ================================================ + """ + print(guide) + + +def main(): + print("Manual RockAuto Data Extraction Tool") + print("=====================================") + + input_tool = ManualDataInput() + + # Show the extraction guide + input_tool.show_extraction_guide() + + # Example of how to add data + print("\nExample - Adding sample data:") + sample_vehicles = [ + {"make": "Toyota", "model": "Camry", "year": 2020, "engine": "2.5L 4-Cylinder"}, + {"make": "Honda", "model": "Civic", "year": 2019, "engine": "1.5L Turbo"}, + {"make": "Ford", "model": "F-150", "year": 2021, "engine": "3.5L V6"}, + {"make": "BMW", "model": "X3", "year": 2020, "engine": "2.0L 4-Cylinder Turbo"}, + {"make": "Chevrolet", "model": "Silverado", "year": 2022, "engine": "5.3L V8"} + ] + + print("Would you like to add these sample vehicles to your database? (y/n): ", end="") + response = input().lower() + + if response == 'y': + input_tool.add_multiple_vehicles(sample_vehicles) + print("\nSample vehicles added to database!") + + print("\nYou can now use the ManualDataInput class to add more vehicles manually.") + print("Import it in Python with: from manual_input import ManualDataInput") + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/vehicle_scraper/manual_input_simple.py b/vehicle_scraper/manual_input_simple.py new file mode 100644 index 0000000..b479f4e --- /dev/null +++ b/vehicle_scraper/manual_input_simple.py @@ -0,0 +1,171 @@ +""" +Manual Data Extraction Guide for RockAuto.com + +Since RockAuto has strong anti-bot measures, here's a manual approach to extract vehicle data: + +1. Visit https://www.rockauto.com/ +2. Click on "Catalog" in the navigation menu +3. You'll see a list of vehicle manufacturers (makes) +4. For each make, manually note down the models, years, and engines + +This script provides a framework to input the manually collected data into your database. +""" + +import sqlite3 +from typing import List, Dict + + +class ManualDataInput: + def __init__(self, db_path: str = "../vehicle_database/vehicle_database.db"): + self.db_path = db_path + + def add_vehicle_data(self, make: str, model: str, year: int, engine: str = "Unknown"): + """Add a single vehicle entry to the database""" + conn = sqlite3.connect(self.db_path) + cursor = conn.cursor() + + try: + # Insert brand + cursor.execute( + "INSERT OR IGNORE INTO brands (name) VALUES (?)", + (make,) + ) + cursor.execute("SELECT id FROM brands WHERE name = ?", (make,)) + brand_id = cursor.fetchone()[0] + + # Insert year + cursor.execute( + "INSERT OR IGNORE INTO years (year) VALUES (?)", + (year,) + ) + cursor.execute("SELECT id FROM years WHERE year = ?", (year,)) + year_id = cursor.fetchone()[0] + + # Insert engine + cursor.execute( + "INSERT OR IGNORE INTO engines (name) VALUES (?)", + (engine,) + ) + cursor.execute("SELECT id FROM engines WHERE name = ?", (engine,)) + engine_id = cursor.fetchone()[0] + + # Insert model + cursor.execute( + "INSERT OR IGNORE INTO models (brand_id, name) VALUES (?, ?)", + (brand_id, model) + ) + cursor.execute("SELECT id FROM models WHERE brand_id = ? AND name = ?", (brand_id, model)) + model_id = cursor.fetchone()[0] + + # Link model, year, and engine + cursor.execute( + """INSERT OR IGNORE INTO model_year_engine + (model_id, year_id, engine_id) VALUES (?, ?, ?)""", + (model_id, year_id, engine_id) + ) + + conn.commit() + print(f"Added: {year} {make} {model} with {engine}") + + except Exception as e: + print(f"Error adding vehicle: {e}") + finally: + conn.close() + + def add_multiple_vehicles(self, vehicles: List[Dict]): + """Add multiple vehicles at once""" + for vehicle in vehicles: + self.add_vehicle_data( + make=vehicle.get('make', ''), + model=vehicle.get('model', ''), + year=vehicle.get('year', 0), + engine=vehicle.get('engine', 'Unknown') + ) + + def show_extraction_guide(self): + """Show the manual extraction guide""" + guide = """ + ================================================ + Manual RockAuto Data Extraction Guide + ================================================ + + 1. OPEN YOUR WEB BROWSER and go to: https://www.rockauto.com + + 2. CLICK on the "Catalog" link in the navigation menu + + 3. YOU WILL SEE a list of vehicle manufacturers (makes) like: + - Acura + - Audi + - BMW + - Chevrolet + - Ford + - Honda + - Toyota + - And many more... + + 4. FOR EACH MANUFACTURER: + a) Click on the manufacturer name + b) You'll see a page with vehicle models organized by year + c) Note down the models and years you see + d) Example format: 2020 Honda Civic, 2019 Ford F-150, etc. + + 5. TO FIND ENGINE INFORMATION: + a) Click on a specific model/year combination + b) You'll see parts categories for that vehicle + c) Look for "Engine" or "Engine Mechanical" category + d) Note down the engine type/specifications + + 6. USE THE FOLLOWING COMMANDS to add data to your database: + + Example Python commands: + >>> from manual_input import ManualDataInput + >>> input_tool = ManualDataInput() + >>> input_tool.add_vehicle_data("Toyota", "Camry", 2020, "2.5L 4-Cylinder") + >>> input_tool.add_vehicle_data("Honda", "Civic", 2019, "1.5L Turbo") + + Or add multiple at once: + >>> vehicles = [ + ... {"make": "Ford", "model": "F-150", "year": 2021, "engine": "3.5L V6"}, + ... {"make": "BMW", "model": "X3", "year": 2020, "engine": "2.0L 4-Cylinder Turbo"} + ... ] + >>> input_tool.add_multiple_vehicles(vehicles) + + 7. TIPS FOR EFFICIENT DATA COLLECTION: + - Focus on popular makes/models first + - Record data in a spreadsheet as you go + - Take screenshots of pages for reference + - Be systematic - go alphabetically or by make popularity + + ================================================ + """ + print(guide) + + +def main(): + print("Manual RockAuto Data Extraction Tool") + print("=====================================") + + input_tool = ManualDataInput() + + # Show the extraction guide + input_tool.show_extraction_guide() + + # Add sample vehicles to database + print("\nAdding sample vehicles to database:") + sample_vehicles = [ + {"make": "Toyota", "model": "Camry", "year": 2020, "engine": "2.5L 4-Cylinder"}, + {"make": "Honda", "model": "Civic", "year": 2019, "engine": "1.5L Turbo"}, + {"make": "Ford", "model": "F-150", "year": 2021, "engine": "3.5L V6"}, + {"make": "BMW", "model": "X3", "year": 2020, "engine": "2.0L 4-Cylinder Turbo"}, + {"make": "Chevrolet", "model": "Silverado", "year": 2022, "engine": "5.3L V8"} + ] + + input_tool.add_multiple_vehicles(sample_vehicles) + print("\nSample vehicles added to database!") + + print("\nYou can now use the ManualDataInput class to add more vehicles manually.") + print("Import it in Python with: from manual_input import ManualDataInput") + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/vehicle_scraper/requirements.txt b/vehicle_scraper/requirements.txt new file mode 100644 index 0000000..da1564b --- /dev/null +++ b/vehicle_scraper/requirements.txt @@ -0,0 +1,3 @@ +requests +beautifulsoup4 +lxml \ No newline at end of file diff --git a/vehicle_scraper/rockauto_scraper.py b/vehicle_scraper/rockauto_scraper.py new file mode 100644 index 0000000..a4a0bad --- /dev/null +++ b/vehicle_scraper/rockauto_scraper.py @@ -0,0 +1,292 @@ +#!/usr/bin/env python3 +""" +RockAuto Vehicle Data Scraper +Extracts vehicle information (brands, models, years, engines) from RockAuto.com +""" + +import requests +from bs4 import BeautifulSoup +import time +import random +from urllib.parse import urljoin, urlparse +import json +import sqlite3 +from typing import List, Dict, Optional + + +class RockAutoScraper: + def __init__(self, db_path: str = "../vehicle_database/vehicle_database.db"): + self.base_url = "https://www.rockauto.com" + self.session = requests.Session() + self.session.headers.update({ + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36' + }) + self.db_path = db_path + + # Create a mapping of RockAuto brand names to standardized names + self.brand_mapping = { + 'acura': 'Acura', + 'alfa-romeo': 'Alfa Romeo', + 'audi': 'Audi', + 'bmw': 'BMW', + 'buick': 'Buick', + 'cadillac': 'Cadillac', + 'chevrolet': 'Chevrolet', + 'chrysler': 'Chrysler', + 'dodge': 'Dodge', + 'fiat': 'Fiat', + 'ford': 'Ford', + 'gmc': 'GMC', + 'honda': 'Honda', + 'hyundai': 'Hyundai', + 'infiniti': 'Infiniti', + 'isuzu': 'Isuzu', + 'jaguar': 'Jaguar', + 'jeep': 'Jeep', + 'kia': 'Kia', + 'land-rover': 'Land Rover', + 'lexus': 'Lexus', + 'lincoln': 'Lincoln', + 'mazda': 'Mazda', + 'mercedes-benz': 'Mercedes-Benz', + 'mercury': 'Mercury', + 'mitsubishi': 'Mitsubishi', + 'nissan': 'Nissan', + 'oldsmobile': 'Oldsmobile', + 'plymouth': 'Plymouth', + 'pontiac': 'Pontiac', + 'porsche': 'Porsche', + 'ram': 'Ram', + 'saab': 'Saab', + 'saturn': 'Saturn', + 'scion': 'Scion', + 'subaru': 'Subaru', + 'suzuki': 'Suzuki', + 'tesla': 'Tesla', + 'toyota': 'Toyota', + 'volkswagen': 'Volkswagen', + 'volvo': 'Volvo' + } + + def get_page(self, url: str) -> Optional[BeautifulSoup]: + """Get a page and return BeautifulSoup object""" + try: + # Add random delay to be respectful to the server + time.sleep(random.uniform(1, 3)) + response = self.session.get(url) + response.raise_for_status() + return BeautifulSoup(response.content, 'html.parser') + except requests.RequestException as e: + print(f"Error fetching {url}: {e}") + return None + + def get_makes(self) -> List[str]: + """Get list of makes from RockAuto""" + print("Fetching list of makes...") + soup = self.get_page(f"{self.base_url}/catalog/catalog.php") + + if not soup: + return [] + + makes = [] + # Look for make selection dropdown or similar element + make_elements = soup.find_all('a', href=lambda x: x and '/catalog/' in x and x.count('/') >= 3) + + for elem in make_elements: + href = elem.get('href', '') + # Extract make from URL + parts = href.split('/') + for part in parts: + if part in self.brand_mapping: + make = self.brand_mapping[part] + if make not in makes: + makes.append(make) + + # Alternative approach: look for common selector patterns + if not makes: + # Look for elements that might contain make information + links = soup.find_all('a', href=True) + for link in links: + href = link['href'].lower() + for key, value in self.brand_mapping.items(): + if key in href and value not in makes: + makes.append(value) + + print(f"Found {len(makes)} makes: {makes[:10]}{'...' if len(makes) > 10 else ''}") + return makes + + def get_models_for_make(self, make: str) -> List[Dict]: + """Get models for a specific make""" + print(f"Fetching models for {make}...") + + # Convert make to RockAuto format + make_key = None + for key, value in self.brand_mapping.items(): + if value.lower() == make.lower(): + make_key = key + break + + if not make_key: + print(f"Make {make} not found in mapping") + return [] + + models = [] + soup = self.get_page(f"{self.base_url}/catalog/catalog.php?c={make_key}") + + if not soup: + return models + + # Look for model/year combinations + # RockAuto typically has links with year and model info + links = soup.find_all('a', href=True) + + for link in links: + href = link['href'] + text = link.get_text().strip() + + # Look for patterns that indicate year/model/engine info + if any(char.isdigit() for char in text) and len(text) > 2: + # Try to extract year and model info + parts = text.split() + + # Look for year (usually 4 digits) + year = None + model_parts = [] + + for part in parts: + if part.isdigit() and len(part) == 4 and 1900 < int(part) < 2030: + year = int(part) + else: + model_parts.append(part) + + if model_parts and year: + model = ' '.join(model_parts) + + # Create a record + record = { + 'make': make, + 'model': model, + 'year': year, + 'engine': 'Unknown', # Will need to extract from deeper pages + 'href': href + } + + if record not in models: + models.append(record) + + print(f"Found {len(models)} models for {make}") + return models + + def scrape_vehicle_data(self) -> List[Dict]: + """Main method to scrape vehicle data from RockAuto""" + print("Starting RockAuto scraping...") + + all_vehicles = [] + + # Get all makes + makes = self.get_makes() + + # Limit to first 5 makes for testing + makes = makes[:5] if len(makes) > 5 else makes + + for make in makes: + models = self.get_models_for_make(make) + all_vehicles.extend(models) + + # Limit total records for testing + if len(all_vehicles) > 20: + break + + print(f"Total vehicles found: {len(all_vehicles)}") + return all_vehicles + + def save_to_database(self, vehicles: List[Dict]): + """Save scraped data to the vehicle database""" + print(f"Saving {len(vehicles)} vehicles to database...") + + conn = sqlite3.connect(self.db_path) + cursor = conn.cursor() + + for vehicle in vehicles: + try: + # Insert brand + cursor.execute( + "INSERT OR IGNORE INTO brands (name) VALUES (?)", + (vehicle['make'],) + ) + cursor.execute("SELECT id FROM brands WHERE name = ?", (vehicle['make'],)) + brand_id = cursor.fetchone()[0] + + # Insert year + cursor.execute( + "INSERT OR IGNORE INTO years (year) VALUES (?)", + (vehicle['year'],) + ) + cursor.execute("SELECT id FROM years WHERE year = ?", (vehicle['year'],)) + year_id = cursor.fetchone()[0] + + # Insert engine (with unknown specs for now) + cursor.execute( + "INSERT OR IGNORE INTO engines (name) VALUES (?)", + (vehicle['engine'],) + ) + cursor.execute("SELECT id FROM engines WHERE name = ?", (vehicle['engine'],)) + engine_id = cursor.fetchone()[0] + + # Insert model + cursor.execute( + "INSERT OR IGNORE INTO models (brand_id, name) VALUES (?, ?)", + (brand_id, vehicle['model']) + ) + cursor.execute("SELECT id FROM models WHERE brand_id = ? AND name = ?", (brand_id, vehicle['model'])) + model_id = cursor.fetchone()[0] + + # Link model, year, and engine + cursor.execute( + """INSERT OR IGNORE INTO model_year_engine + (model_id, year_id, engine_id) VALUES (?, ?, ?)""", + (model_id, year_id, engine_id) + ) + + except Exception as e: + print(f"Error saving vehicle {vehicle}: {e}") + + conn.commit() + conn.close() + print("Data saved to database successfully!") + + +def main(): + scraper = RockAutoScraper() + + print("Starting RockAuto data extraction...") + print("Note: This may take several minutes due to rate limiting.") + + try: + # Scrape vehicle data + vehicles = scraper.scrape_vehicle_data() + + if vehicles: + print(f"\nFound {len(vehicles)} vehicles:") + for i, v in enumerate(vehicles[:10]): # Show first 10 + print(f" {i+1}. {v['make']} {v['model']} {v['year']}") + + if len(vehicles) > 10: + print(f" ... and {len(vehicles)-10} more") + + # Save to database + scraper.save_to_database(vehicles) + + print("\nScraping completed successfully!") + else: + print("No vehicles found. This could be due to:") + print("1. RockAuto blocking automated requests") + print("2. Changes in website structure") + print("3. Network connectivity issues") + + except Exception as e: + print(f"An error occurred during scraping: {e}") + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/vehicle_scraper/rockauto_scraper_enhanced.py b/vehicle_scraper/rockauto_scraper_enhanced.py new file mode 100644 index 0000000..0a5d4e3 --- /dev/null +++ b/vehicle_scraper/rockauto_scraper_enhanced.py @@ -0,0 +1,400 @@ +#!/usr/bin/env python3 +""" +RockAuto Vehicle Data Scraper - Enhanced Version +Extracts vehicle information (brands, models, years, engines) from RockAuto.com +""" + +import requests +from bs4 import BeautifulSoup +import time +import random +from urllib.parse import urljoin, urlparse +import json +import sqlite3 +from typing import List, Dict, Optional + + +class RockAutoScraper: + def __init__(self, db_path: str = "../vehicle_database/vehicle_database.db"): + self.base_url = "https://www.rockauto.com" + self.session = requests.Session() + self.session.headers.update({ + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36', + 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8', + 'Accept-Language': 'en-US,en;q=0.5', + 'Accept-Encoding': 'gzip, deflate', + 'Connection': 'keep-alive', + 'Upgrade-Insecure-Requests': '1', + }) + self.db_path = db_path + + # Create a mapping of RockAuto brand names to standardized names + self.brand_mapping = { + 'acura': 'Acura', + 'alfa-romeo': 'Alfa Romeo', + 'audi': 'Audi', + 'bmw': 'BMW', + 'buick': 'Buick', + 'cadillac': 'Cadillac', + 'chevrolet': 'Chevrolet', + 'chrysler': 'Chrysler', + 'dodge': 'Dodge', + 'fiat': 'Fiat', + 'ford': 'Ford', + 'gmc': 'GMC', + 'honda': 'Honda', + 'hyundai': 'Hyundai', + 'infiniti': 'Infiniti', + 'isuzu': 'Isuzu', + 'jaguar': 'Jaguar', + 'jeep': 'Jeep', + 'kia': 'Kia', + 'land-rover': 'Land Rover', + 'lexus': 'Lexus', + 'lincoln': 'Lincoln', + 'mazda': 'Mazda', + 'mercedes-benz': 'Mercedes-Benz', + 'mercury': 'Mercury', + 'mitsubishi': 'Mitsubishi', + 'nissan': 'Nissan', + 'oldsmobile': 'Oldsmobile', + 'plymouth': 'Plymouth', + 'pontiac': 'Pontiac', + 'porsche': 'Porsche', + 'ram': 'Ram', + 'saab': 'Saab', + 'saturn': 'Saturn', + 'scion': 'Scion', + 'subaru': 'Subaru', + 'suzuki': 'Suzuki', + 'tesla': 'Tesla', + 'toyota': 'Toyota', + 'volkswagen': 'Volkswagen', + 'volvo': 'Volvo' + } + + def get_page(self, url: str) -> Optional[BeautifulSoup]: + """Get a page and return BeautifulSoup object""" + try: + # Add random delay to be respectful to the server + time.sleep(random.uniform(2, 4)) + response = self.session.get(url) + response.raise_for_status() + return BeautifulSoup(response.content, 'html.parser') + except requests.RequestException as e: + print(f"Error fetching {url}: {e}") + return None + + def get_makes_enhanced(self) -> List[str]: + """Enhanced method to get makes from RockAuto""" + print("Fetching list of makes (enhanced)...") + + # Try multiple approaches to get makes + makes = [] + + # Approach 1: Visit the main catalog page + soup = self.get_page(f"{self.base_url}/catalog/catalog.php") + + if not soup: + return makes + + # Look for links that contain make information in the URL + links = soup.find_all('a', href=True) + + for link in links: + href = link.get('href', '').lower() + + # Check if the href contains a known make + for key, value in self.brand_mapping.items(): + if f"/{key}/" in href and value not in makes: + makes.append(value) + + # Approach 2: Look for JavaScript variables or data attributes that might contain makes + scripts = soup.find_all('script') + for script in scripts: + if script.string: + # Look for common patterns in JavaScript + import re + # Look for patterns like make names in quotes + matches = re.findall(r'["\']([a-z-]+)["\']', script.string) + for match in matches: + if match in self.brand_mapping and self.brand_mapping[match] not in makes: + makes.append(self.brand_mapping[match]) + + print(f"Found {len(makes)} makes: {makes[:10]}{'...' if len(makes) > 10 else ''}") + return makes + + def get_detailed_models_for_make(self, make: str) -> List[Dict]: + """Get detailed models for a specific make by exploring deeper pages""" + print(f"Fetching detailed models for {make}...") + + # Convert make to RockAuto format + make_key = None + for key, value in self.brand_mapping.items(): + if value.lower() == make.lower(): + make_key = key + break + + if not make_key: + print(f"Make {make} not found in mapping") + return [] + + models = [] + + # Visit the make-specific page + url = f"{self.base_url}/catalog/catalog.php?c={make_key}" + soup = self.get_page(url) + + if not soup: + return models + + # Look for year links first + year_links = soup.find_all('a', href=lambda x: x and f'/catalog/{make_key}/' in x and any(str(y) in x for y in range(1900, 2030))) + + for link in year_links: + href = link.get('href', '') + text = link.get_text().strip() + + # Extract year from URL or text + import re + year_match = re.search(r'\b(19|20)\d{2}\b', text) + if not year_match: + year_match = re.search(r'\b(19|20)\d{2}\b', href) + + if year_match: + year = int(year_match.group()) + + # Extract model from text or URL + # Remove year from text to get model + model_text = re.sub(r'\b(19|20)\d{2}\b', '', text).strip() + + if model_text: + # Create a record + record = { + 'make': make, + 'model': model_text, + 'year': year, + 'engine': 'Unknown', # Will need to extract from deeper pages + 'href': href + } + + if record not in models: + models.append(record) + + # If no year-specific links found, try alternative approach + if not models: + # Look for links that might contain both make and year + all_links = soup.find_all('a', href=True) + for link in all_links: + href = link.get('href', '').lower() + text = link.get_text().strip() + + if f"/{make_key}/" in href: + # Look for year in the text or href + year_match = re.search(r'\b(19|20)\d{2}\b', text) + if not year_match: + year_match = re.search(r'\b(19|20)\d{2}\b', href) + + if year_match: + year = int(year_match.group()) + + # Extract model info + model_parts = [part for part in text.split() if not re.match(r'\b(19|20)\d{2}\b', part)] + model = ' '.join(model_parts) + + if model: + record = { + 'make': make, + 'model': model, + 'year': year, + 'engine': 'Unknown', + 'href': link.get('href') + } + + if record not in models: + models.append(record) + + print(f"Found {len(models)} models for {make}") + return models + + def explore_categories(self, make: str) -> List[Dict]: + """Explore categories for a specific make to find models and years""" + print(f"Exploring categories for {make}...") + + # Convert make to RockAuto format + make_key = None + for key, value in self.brand_mapping.items(): + if value.lower() == make.lower(): + make_key = key + break + + if not make_key: + print(f"Make {make} not found in mapping") + return [] + + models = [] + + # Visit the make-specific page + url = f"{self.base_url}/catalog/catalog.php?c={make_key}" + soup = self.get_page(url) + + if not soup: + return models + + # Look for elements that represent vehicle categories + # RockAuto typically organizes by year/model + category_elements = soup.find_all(['div', 'section', 'ul'], class_=lambda x: x and any(keyword in x.lower() for keyword in ['year', 'model', 'catalog', 'vehicle'])) + + if not category_elements: + # If no categorized elements found, try looking for all links with year info + all_links = soup.find_all('a', href=True) + for link in all_links: + href = link.get('href', '').lower() + text = link.get_text().strip() + + if f"/{make_key}/" in href and any(str(year) in href for year in range(1900, 2030)): + # Extract year and model + import re + year_match = re.search(r'\b(19|20)\d{2}\b', href) + if year_match: + year = int(year_match.group()) + + # Clean up text to extract model + clean_text = re.sub(r'\b(19|20)\d{2}\b', '', text).strip(' -_') + + if clean_text and len(clean_text) > 1: + record = { + 'make': make, + 'model': clean_text, + 'year': year, + 'engine': 'Unknown', + 'href': link.get('href') + } + + if record not in models: + models.append(record) + + print(f"Found {len(models)} entries for {make} through category exploration") + return models + + def scrape_vehicle_data(self) -> List[Dict]: + """Main method to scrape vehicle data from RockAuto""" + print("Starting enhanced RockAuto scraping...") + + all_vehicles = [] + + # Get all makes using enhanced method + makes = self.get_makes_enhanced() + + # Limit to first 3 makes for testing + makes = makes[:3] if len(makes) > 3 else makes + + for make in makes: + # Try multiple approaches to get models + models = self.get_detailed_models_for_make(make) + + # If still no models, try category exploration + if not models: + models = self.explore_categories(make) + + all_vehicles.extend(models) + + # Limit total records for testing + if len(all_vehicles) > 15: + break + + print(f"Total vehicles found: {len(all_vehicles)}") + return all_vehicles + + def save_to_database(self, vehicles: List[Dict]): + """Save scraped data to the vehicle database""" + print(f"Saving {len(vehicles)} vehicles to database...") + + conn = sqlite3.connect(self.db_path) + cursor = conn.cursor() + + for vehicle in vehicles: + try: + # Insert brand + cursor.execute( + "INSERT OR IGNORE INTO brands (name) VALUES (?)", + (vehicle['make'],) + ) + cursor.execute("SELECT id FROM brands WHERE name = ?", (vehicle['make'],)) + brand_id = cursor.fetchone()[0] + + # Insert year + cursor.execute( + "INSERT OR IGNORE INTO years (year) VALUES (?)", + (vehicle['year'],) + ) + cursor.execute("SELECT id FROM years WHERE year = ?", (vehicle['year'],)) + year_id = cursor.fetchone()[0] + + # Insert engine (with unknown specs for now) + engine_name = vehicle['engine'] if vehicle['engine'] != 'Unknown' else f"Engine_{vehicle['year']}_{vehicle['model'][:10]}" + cursor.execute( + "INSERT OR IGNORE INTO engines (name) VALUES (?)", + (engine_name,) + ) + cursor.execute("SELECT id FROM engines WHERE name = ?", (engine_name,)) + engine_id = cursor.fetchone()[0] + + # Insert model + cursor.execute( + "INSERT OR IGNORE INTO models (brand_id, name, body_type) VALUES (?, ?, ?)", + (brand_id, vehicle['model'], 'Unknown') + ) + cursor.execute("SELECT id FROM models WHERE brand_id = ? AND name = ?", (brand_id, vehicle['model'])) + model_id = cursor.fetchone()[0] + + # Link model, year, and engine + cursor.execute( + """INSERT OR IGNORE INTO model_year_engine + (model_id, year_id, engine_id) VALUES (?, ?, ?)""", + (model_id, year_id, engine_id) + ) + + except Exception as e: + print(f"Error saving vehicle {vehicle}: {e}") + + conn.commit() + conn.close() + print("Data saved to database successfully!") + + +def main(): + scraper = RockAutoScraper() + + print("Starting enhanced RockAuto data extraction...") + print("Note: This may take several minutes due to rate limiting.") + + try: + # Scrape vehicle data + vehicles = scraper.scrape_vehicle_data() + + if vehicles: + print(f"\nFound {len(vehicles)} vehicles:") + for i, v in enumerate(vehicles[:10]): # Show first 10 + print(f" {i+1}. {v['make']} {v['model']} {v['year']}") + + if len(vehicles) > 10: + print(f" ... and {len(vehicles)-10} more") + + # Save to database + scraper.save_to_database(vehicles) + + print("\nScraping completed successfully!") + else: + print("No vehicles found. This could be due to:") + print("1. RockAuto blocking automated requests") + print("2. Changes in website structure") + print("3. Network connectivity issues") + print("4. Anti-bot measures implemented by RockAuto") + + except Exception as e: + print(f"An error occurred during scraping: {e}") + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/vehicle_scraper/rockauto_scraper_v2.py b/vehicle_scraper/rockauto_scraper_v2.py new file mode 100644 index 0000000..d7ec6c1 --- /dev/null +++ b/vehicle_scraper/rockauto_scraper_v2.py @@ -0,0 +1,350 @@ +#!/usr/bin/env python3 +""" +RockAuto Vehicle Data Scraper v2 +Extrae información de vehículos (marcas, años, modelos, motores) de RockAuto.com +""" + +import requests +from bs4 import BeautifulSoup +import time +import random +import sqlite3 +import re +import sys +from typing import List, Dict, Set, Optional +from urllib.parse import unquote + + +class RockAutoScraperV2: + def __init__(self, db_path: str = "../vehicle_database/vehicle_database.db"): + self.base_url = "https://www.rockauto.com/en/catalog" + self.session = requests.Session() + self.session.headers.update({ + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', + 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8', + 'Accept-Language': 'en-US,en;q=0.5', + }) + self.db_path = db_path + self.delay_range = (1, 2) # Segundos entre peticiones + + def _delay(self): + """Pausa respetuosa entre peticiones""" + time.sleep(random.uniform(*self.delay_range)) + + def _get_soup(self, url: str) -> Optional[BeautifulSoup]: + """Obtiene y parsea una página""" + try: + self._delay() + response = self.session.get(url, timeout=30) + response.raise_for_status() + return BeautifulSoup(response.content, 'html.parser') + except Exception as e: + print(f" Error al obtener {url}: {e}") + return None + + def _clean_name(self, name: str) -> str: + """Limpia y formatea un nombre""" + name = unquote(name.replace('+', ' ')) + name = re.sub(r'\s+', ' ', name).strip() + return name.upper() + + def get_all_brands(self) -> List[str]: + """Obtiene todas las marcas disponibles""" + print("Obteniendo lista de marcas...") + soup = self._get_soup(f"{self.base_url}/") + + if not soup: + return [] + + brands = set() + links = soup.find_all('a', href=True) + + for link in links: + href = link['href'] + # Buscar enlaces como /en/catalog/MARCA + match = re.match(r'/en/catalog/([^,/]+)$', href) + if match: + brand = self._clean_name(match.group(1)) + if brand and len(brand) > 1 and not brand.isdigit(): + brands.add(brand) + + brands_list = sorted(brands) + print(f" Encontradas {len(brands_list)} marcas") + return brands_list + + def get_years_for_brand(self, brand: str) -> List[int]: + """Obtiene los años disponibles para una marca""" + brand_url = brand.lower().replace(' ', '+') + soup = self._get_soup(f"{self.base_url}/{brand_url}") + + if not soup: + return [] + + years = set() + links = soup.find_all('a', href=True) + + for link in links: + href = link['href'] + # Buscar patrones como /catalog/brand,YEAR + match = re.search(rf'/catalog/{re.escape(brand_url)},(\d{{4}})', href, re.IGNORECASE) + if match: + year = int(match.group(1)) + if 1900 < year <= 2030: + years.add(year) + + return sorted(years, reverse=True) + + def get_models_for_brand_year(self, brand: str, year: int) -> List[str]: + """Obtiene los modelos para una marca y año""" + brand_url = brand.lower().replace(' ', '+') + soup = self._get_soup(f"{self.base_url}/{brand_url},{year}") + + if not soup: + return [] + + models = set() + links = soup.find_all('a', href=True) + + for link in links: + href = link['href'] + # Buscar patrones como /catalog/brand,year,MODEL + pattern = rf'/catalog/{re.escape(brand_url)},{year},([^,/]+)' + match = re.search(pattern, href, re.IGNORECASE) + if match: + model = self._clean_name(match.group(1)) + if model and len(model) > 0 and not model.isdigit(): + models.add(model) + + return sorted(models) + + def get_engines_for_vehicle(self, brand: str, year: int, model: str) -> List[str]: + """Obtiene los motores para un vehículo específico""" + brand_url = brand.lower().replace(' ', '+') + model_url = model.lower().replace(' ', '+') + soup = self._get_soup(f"{self.base_url}/{brand_url},{year},{model_url}") + + if not soup: + return [] + + engines = set() + links = soup.find_all('a', href=True) + + for link in links: + href = link['href'] + text = link.get_text().strip() + + # Buscar patrones de motor en el href + pattern = rf'/catalog/{re.escape(brand_url)},{year},{re.escape(model_url)},([^,/]+)' + match = re.search(pattern, href, re.IGNORECASE) + if match: + engine = self._clean_name(match.group(1)) + # Filtrar solo motores válidos (contienen L, V, cilindros, etc.) + if engine and re.search(r'\d+\.?\d*L|V\d|I\d|HYBRID|ELECTRIC|DIESEL', engine, re.IGNORECASE): + engines.add(engine) + + return sorted(engines) + + def scrape_brand(self, brand: str, max_years: int = None, max_models_per_year: int = None) -> List[Dict]: + """Extrae todos los vehículos de una marca""" + print(f"\n{'='*50}") + print(f"Procesando marca: {brand}") + print('='*50) + + vehicles = [] + + # Obtener años + years = self.get_years_for_brand(brand) + if max_years: + years = years[:max_years] + + print(f" Años encontrados: {len(years)}") + + for year in years: + print(f"\n Año {year}:") + + # Obtener modelos + models = self.get_models_for_brand_year(brand, year) + if max_models_per_year: + models = models[:max_models_per_year] + + print(f" Modelos: {len(models)}") + + for model in models: + # Obtener motores + engines = self.get_engines_for_vehicle(brand, year, model) + + if engines: + for engine in engines: + vehicle = { + 'brand': brand, + 'year': year, + 'model': model, + 'engine': engine + } + vehicles.append(vehicle) + print(f" {model} - {engine}") + else: + # Si no hay motores específicos, agregar con motor genérico + vehicle = { + 'brand': brand, + 'year': year, + 'model': model, + 'engine': 'Standard' + } + vehicles.append(vehicle) + print(f" {model} - (sin motor específico)") + + print(f"\n Total vehículos para {brand}: {len(vehicles)}") + return vehicles + + def save_to_database(self, vehicles: List[Dict]): + """Guarda los vehículos en la base de datos""" + if not vehicles: + print("No hay vehículos para guardar") + return + + print(f"\nGuardando {len(vehicles)} vehículos en la base de datos...") + + conn = sqlite3.connect(self.db_path) + cursor = conn.cursor() + + saved = 0 + skipped = 0 + + for vehicle in vehicles: + try: + # Insertar o obtener marca + cursor.execute( + "INSERT OR IGNORE INTO brands (name) VALUES (?)", + (vehicle['brand'],) + ) + cursor.execute("SELECT id FROM brands WHERE name = ?", (vehicle['brand'],)) + brand_id = cursor.fetchone()[0] + + # Insertar o obtener año + cursor.execute( + "INSERT OR IGNORE INTO years (year) VALUES (?)", + (vehicle['year'],) + ) + cursor.execute("SELECT id FROM years WHERE year = ?", (vehicle['year'],)) + year_id = cursor.fetchone()[0] + + # Insertar o obtener motor + cursor.execute( + "INSERT OR IGNORE INTO engines (name) VALUES (?)", + (vehicle['engine'],) + ) + cursor.execute("SELECT id FROM engines WHERE name = ?", (vehicle['engine'],)) + engine_id = cursor.fetchone()[0] + + # Insertar o obtener modelo + cursor.execute( + "INSERT OR IGNORE INTO models (brand_id, name) VALUES (?, ?)", + (brand_id, vehicle['model']) + ) + cursor.execute( + "SELECT id FROM models WHERE brand_id = ? AND name = ?", + (brand_id, vehicle['model']) + ) + model_id = cursor.fetchone()[0] + + # Insertar relación modelo-año-motor + cursor.execute( + """INSERT OR IGNORE INTO model_year_engine + (model_id, year_id, engine_id) VALUES (?, ?, ?)""", + (model_id, year_id, engine_id) + ) + + if cursor.rowcount > 0: + saved += 1 + else: + skipped += 1 + + except Exception as e: + print(f" Error guardando {vehicle}: {e}") + skipped += 1 + + conn.commit() + conn.close() + + print(f" Guardados: {saved}, Omitidos (duplicados): {skipped}") + + def scrape_multiple_brands(self, brands: List[str], **kwargs) -> List[Dict]: + """Extrae vehículos de múltiples marcas""" + all_vehicles = [] + + for i, brand in enumerate(brands, 1): + print(f"\n[{i}/{len(brands)}] ", end="") + vehicles = self.scrape_brand(brand, **kwargs) + all_vehicles.extend(vehicles) + + return all_vehicles + + +def main(): + import argparse + + parser = argparse.ArgumentParser(description='Scraper de vehículos de RockAuto') + parser.add_argument('--brands', nargs='+', help='Marcas específicas a extraer') + parser.add_argument('--all-brands', action='store_true', help='Extraer todas las marcas') + parser.add_argument('--max-years', type=int, default=5, help='Máximo de años por marca (default: 5)') + parser.add_argument('--max-models', type=int, help='Máximo de modelos por año') + parser.add_argument('--list-brands', action='store_true', help='Solo listar marcas disponibles') + parser.add_argument('--db', default='../vehicle_database/vehicle_database.db', help='Ruta a la base de datos') + + args = parser.parse_args() + + scraper = RockAutoScraperV2(db_path=args.db) + + if args.list_brands: + brands = scraper.get_all_brands() + print("\nMarcas disponibles en RockAuto:") + for i, brand in enumerate(brands, 1): + print(f" {i:3}. {brand}") + print(f"\nTotal: {len(brands)} marcas") + return + + # Determinar qué marcas procesar + if args.brands: + brands_to_scrape = [b.upper() for b in args.brands] + elif args.all_brands: + brands_to_scrape = scraper.get_all_brands() + else: + # Por defecto, algunas marcas populares + brands_to_scrape = ['TOYOTA', 'HONDA', 'FORD', 'CHEVROLET', 'NISSAN'] + + print(f"\nMarcas a procesar: {', '.join(brands_to_scrape)}") + print(f"Máximo años por marca: {args.max_years}") + if args.max_models: + print(f"Máximo modelos por año: {args.max_models}") + + # Extraer datos + vehicles = scraper.scrape_multiple_brands( + brands_to_scrape, + max_years=args.max_years, + max_models_per_year=args.max_models + ) + + # Guardar en base de datos + if vehicles: + scraper.save_to_database(vehicles) + + print(f"\n{'='*50}") + print("RESUMEN") + print('='*50) + print(f"Total de vehículos extraídos: {len(vehicles)}") + + # Estadísticas + brands_count = len(set(v['brand'] for v in vehicles)) + models_count = len(set(f"{v['brand']}-{v['model']}" for v in vehicles)) + years_range = f"{min(v['year'] for v in vehicles)} - {max(v['year'] for v in vehicles)}" + + print(f"Marcas: {brands_count}") + print(f"Modelos únicos: {models_count}") + print(f"Rango de años: {years_range}") + else: + print("\nNo se encontraron vehículos") + + +if __name__ == "__main__": + main() diff --git a/vehicle_scraper/scrape_nissan_ford_chevrolet.py b/vehicle_scraper/scrape_nissan_ford_chevrolet.py new file mode 100644 index 0000000..b850595 --- /dev/null +++ b/vehicle_scraper/scrape_nissan_ford_chevrolet.py @@ -0,0 +1,393 @@ +#!/usr/bin/env python3 +""" +Scraper de Ford y Chevrolet +- Procesa de 5 en 5 años +- Espera 3 minutos (180 segundos) entre lotes para activar VPN +- Presiona ENTER para saltar la espera +- Años: 1975-2026 +""" + +import requests +from bs4 import BeautifulSoup +import sqlite3 +import time +import re +import os +import sys +import threading +from urllib.parse import unquote + +# Detectar ruta base del proyecto +SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) +if os.path.basename(SCRIPT_DIR) == "vehicle_scraper": + BASE_DIR = os.path.dirname(SCRIPT_DIR) +else: + BASE_DIR = SCRIPT_DIR +DB_PATH = os.path.join(BASE_DIR, "vehicle_database", "vehicle_database.db") + +BASE_URL = "https://www.rockauto.com/en/catalog" + +# Marcas a scrapear (Nissan ya fue procesado) +BRANDS = ["FORD", "CHEVROLET"] + +# Años de 1975 a 2026 (orden descendente) +ALL_YEARS = list(range(2026, 1974, -1)) + +# Configuración de lotes +BATCH_SIZE = 5 # años por lote +WAIT_TIME = 180 # 3 minutos entre lotes + +session = requests.Session() +session.headers.update({ + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', + 'Accept': 'text/html,application/xhtml+xml', + 'Accept-Language': 'en-US,en;q=0.9', +}) + +# Variable global para controlar salto de espera +skip_wait = False + +def wait_with_skip(seconds, message=""): + """Espera que se puede saltar presionando ENTER""" + global skip_wait + skip_wait = False + + print(f"\n{'*'*60}") + print(f" {message}") + print(f" ACTIVA/CAMBIA EL VPN AHORA") + print(f" >>> Presiona ENTER para saltar la espera <<<") + print(f"{'*'*60}") + + # Usar threading para detectar input + def check_input(): + global skip_wait + try: + input() + skip_wait = True + except: + pass + + input_thread = threading.Thread(target=check_input, daemon=True) + input_thread.start() + + for sec in range(seconds, 0, -1): + if skip_wait: + print(f"\n >>> ESPERA SALTADA <<<") + return + mins = sec // 60 + secs = sec % 60 + print(f"\r Continuando en {mins}:{secs:02d}... (ENTER para saltar) ", end="", flush=True) + time.sleep(1) + print() + +def clean_name(name): + name = unquote(name.replace('+', ' ')) + return re.sub(r'\s+', ' ', name).strip().upper() + +def get_soup(url, retries=3): + for attempt in range(retries): + try: + time.sleep(0.5) + response = session.get(url, timeout=15) + if response.status_code == 200: + return BeautifulSoup(response.content, 'html.parser') + elif response.status_code == 403: + print(f"\n [!] Bloqueado (403) - Cambia el VPN") + return None + except Exception as e: + if attempt < retries - 1: + time.sleep(3) + else: + print(f"\n Error: {e}") + return None + +def get_models(brand, year): + brand_url = brand.lower().replace(' ', '+') + soup = get_soup(f"{BASE_URL}/{brand_url},{year}") + if not soup: + return [] + + models = set() + for link in soup.find_all('a', href=True): + pattern = rf'/catalog/{re.escape(brand_url)},{year},([^,/]+)' + match = re.search(pattern, link['href'], re.I) + if match: + model = clean_name(match.group(1)) + if model and not model.isdigit() and len(model) > 1: + models.add(model) + return sorted(models) + +def get_engines(brand, year, model): + brand_url = brand.lower().replace(' ', '+') + model_url = model.lower().replace(' ', '+') + soup = get_soup(f"{BASE_URL}/{brand_url},{year},{model_url}") + if not soup: + return ['STANDARD'] + + engines = set() + for link in soup.find_all('a', href=True): + pattern = rf'/catalog/{re.escape(brand_url)},{year},{re.escape(model_url)},([^,/]+)' + match = re.search(pattern, link['href'], re.I) + if match: + engine = clean_name(match.group(1)) + if engine and re.search(r'\d+\.?\d*L|V\d|I\d|H\d|HYBRID|ELECTRIC|DIESEL', engine, re.I): + engines.add(engine) + return sorted(engines) if engines else ['STANDARD'] + +def save_to_db(conn, brand, year, model, engine): + cursor = conn.cursor() + try: + cursor.execute("INSERT OR IGNORE INTO brands (name) VALUES (?)", (brand,)) + cursor.execute("SELECT id FROM brands WHERE name = ?", (brand,)) + brand_id = cursor.fetchone()[0] + + cursor.execute("INSERT OR IGNORE INTO years (year) VALUES (?)", (year,)) + cursor.execute("SELECT id FROM years WHERE year = ?", (year,)) + year_id = cursor.fetchone()[0] + + cursor.execute("INSERT OR IGNORE INTO engines (name) VALUES (?)", (engine,)) + cursor.execute("SELECT id FROM engines WHERE name = ?", (engine,)) + engine_id = cursor.fetchone()[0] + + cursor.execute("INSERT OR IGNORE INTO models (brand_id, name) VALUES (?, ?)", (brand_id, model)) + cursor.execute("SELECT id FROM models WHERE brand_id = ? AND name = ?", (brand_id, model)) + model_id = cursor.fetchone()[0] + + cursor.execute( + "INSERT OR IGNORE INTO model_year_engine (model_id, year_id, engine_id) VALUES (?, ?, ?)", + (model_id, year_id, engine_id) + ) + return cursor.rowcount > 0 + except Exception as e: + print(f" DB Error: {e}") + return False + +def get_existing_years(conn, brand): + """Obtiene los años que ya existen para esta marca""" + cursor = conn.cursor() + cursor.execute(""" + SELECT DISTINCT y.year + FROM years y + JOIN model_year_engine mye ON y.id = mye.year_id + JOIN models m ON mye.model_id = m.id + JOIN brands b ON m.brand_id = b.id + WHERE b.name = ? + """, (brand,)) + return set(row[0] for row in cursor.fetchall()) + +def process_batch(conn, brand, years_batch, batch_num, total_batches): + """Procesa un lote de 5 años""" + print(f"\n{'='*60}") + print(f"[{brand}] LOTE {batch_num}/{total_batches}: Años {years_batch}") + print('='*60) + + batch_saved = 0 + batch_total = 0 + + for year in years_batch: + print(f"\n[{brand} - Año {year}] Obteniendo modelos... ", end="", flush=True) + models = get_models(brand, year) + print(f"{len(models)} modelos encontrados") + + if not models: + print(f" No se encontraron modelos para {year}") + continue + + for model in models: + engines = get_engines(brand, year, model) + for engine in engines: + batch_total += 1 + if save_to_db(conn, brand, year, model, engine): + batch_saved += 1 + print(f" {model} - {engine}") + + # Guardar cambios del lote + conn.commit() + print(f"\n>> Lote {batch_num} completado: {batch_saved} nuevos de {batch_total} encontrados") + return batch_saved, batch_total + +def get_brand_batches(conn, brand): + """Obtiene los lotes disponibles para una marca""" + existing = get_existing_years(conn, brand) + years_to_process = [y for y in ALL_YEARS if y not in existing] + if not years_to_process: + return [], existing + batches = [years_to_process[i:i+BATCH_SIZE] for i in range(0, len(years_to_process), BATCH_SIZE)] + return batches, existing + +def process_brand(conn, brand, start_batch=1): + """Procesa una marca completa desde un lote específico""" + print(f"\n{'#'*60}") + print(f" PROCESANDO MARCA: {brand}") + print(f"{'#'*60}") + + # Verificar qué años ya existen + existing = get_existing_years(conn, brand) + print(f"Años existentes de {brand}: {len(existing)} años") + if existing: + print(f" Rango existente: {min(existing)}-{max(existing)}") + + # Filtrar solo los que faltan + years_to_process = [y for y in ALL_YEARS if y not in existing] + + if not years_to_process: + print(f"\n[OK] {brand}: Todos los años ya están en la base de datos!") + return 0, 0 + + print(f"\nAños por procesar para {brand}: {len(years_to_process)}") + print(f" De {max(years_to_process)} a {min(years_to_process)}") + + # Dividir en lotes de 5 + batches = [years_to_process[i:i+BATCH_SIZE] for i in range(0, len(years_to_process), BATCH_SIZE)] + total_batches = len(batches) + + print(f"Lotes de {BATCH_SIZE} años: {total_batches} lotes") + + if start_batch > 1: + print(f"\n>>> Comenzando desde el lote {start_batch} <<<") + + total_saved = 0 + total_found = 0 + + for i, batch in enumerate(batches, 1): + # Saltar lotes anteriores al inicial + if i < start_batch: + continue + + saved, found = process_batch(conn, brand, batch, i, total_batches) + total_saved += saved + total_found += found + + # Si no es el último lote, esperar para cambiar VPN + if i < total_batches: + wait_with_skip(WAIT_TIME, f"PAUSA DE {WAIT_TIME//60} MINUTOS - [{brand}] Lotes restantes: {total_batches - i}") + + return total_saved, total_found + +def show_batch_menu(conn): + """Muestra menú para seleccionar marca y lote inicial""" + print("\n" + "="*60) + print(" MENÚ DE SELECCIÓN DE LOTES") + print("="*60) + + brand_info = {} + for i, brand in enumerate(BRANDS, 1): + batches, existing = get_brand_batches(conn, brand) + brand_info[brand] = {'batches': batches, 'existing': existing} + + if batches: + print(f"\n {i}. {brand}") + print(f" Años existentes: {len(existing)}") + print(f" Lotes pendientes: {len(batches)}") + for j, batch in enumerate(batches, 1): + print(f" Lote {j}: años {batch[0]}-{batch[-1]}") + else: + print(f"\n {i}. {brand} - [COMPLETO]") + + print(f"\n 0. Procesar todo desde el inicio") + print("="*60) + + # Seleccionar marca + while True: + try: + choice = input("\nSelecciona marca (0 para todo): ").strip() + if choice == '0' or choice == '': + return None, 1 # Procesar todo + + brand_idx = int(choice) - 1 + if 0 <= brand_idx < len(BRANDS): + selected_brand = BRANDS[brand_idx] + break + print("Opción inválida") + except ValueError: + print("Ingresa un número válido") + + batches = brand_info[selected_brand]['batches'] + if not batches: + print(f"\n{selected_brand} ya está completo!") + return selected_brand, 1 + + # Seleccionar lote + print(f"\n--- Lotes de {selected_brand} ---") + for j, batch in enumerate(batches, 1): + print(f" {j}. Lote {j}: años {batch[0]}-{batch[-1]}") + + while True: + try: + batch_choice = input(f"\nComenzar desde lote (1-{len(batches)}): ").strip() + if batch_choice == '': + return selected_brand, 1 + + batch_num = int(batch_choice) + if 1 <= batch_num <= len(batches): + return selected_brand, batch_num + print(f"Ingresa un número entre 1 y {len(batches)}") + except ValueError: + print("Ingresa un número válido") + +def main(): + print("="*60) + print(" SCRAPER FORD, CHEVROLET") + print(f" Años: 1975-2026 | Lotes de {BATCH_SIZE} años") + print(f" Pausa entre lotes: {WAIT_TIME//60} minutos") + print(" >>> Presiona ENTER para saltar esperas <<<") + print("="*60) + + # Verificar base de datos + if not os.path.exists(DB_PATH): + print(f"\n[ERROR] Base de datos no encontrada: {DB_PATH}") + print("Verifica que la ruta sea correcta.") + sys.exit(1) + + print(f"\nBase de datos: {DB_PATH}") + + conn = sqlite3.connect(DB_PATH) + + # Mostrar estado inicial + print(f"\nMarcas a procesar: {', '.join(BRANDS)}") + print(f"Rango de años: {min(ALL_YEARS)}-{max(ALL_YEARS)} ({len(ALL_YEARS)} años)") + + # Menú de selección de lotes + selected_brand, start_batch = show_batch_menu(conn) + + grand_total_saved = 0 + grand_total_found = 0 + brand_stats = {} + + # Determinar qué marcas procesar + if selected_brand: + # Solo procesar la marca seleccionada desde el lote indicado + brands_to_process = [selected_brand] + start_batches = {selected_brand: start_batch} + else: + # Procesar todas las marcas desde el inicio + brands_to_process = BRANDS + start_batches = {brand: 1 for brand in BRANDS} + + for brand in brands_to_process: + saved, found = process_brand(conn, brand, start_batches.get(brand, 1)) + brand_stats[brand] = {'saved': saved, 'found': found} + grand_total_saved += saved + grand_total_found += found + + # Pausa entre marcas (si hay otra marca por procesar) + if brand != brands_to_process[-1]: + wait_with_skip(WAIT_TIME, f"PAUSA ENTRE MARCAS - Siguiente: {brands_to_process[brands_to_process.index(brand)+1]}") + + conn.close() + + print("\n" + "="*60) + print(" RESUMEN FINAL") + print("="*60) + for brand, stats in brand_stats.items(): + print(f" {brand}:") + print(f" Encontrados: {stats['found']}") + print(f" Nuevos guardados: {stats['saved']}") + print("-"*60) + print(f" TOTAL:") + print(f" Vehículos encontrados: {grand_total_found}") + print(f" Nuevos guardados: {grand_total_saved}") + print("="*60) + +if __name__ == "__main__": + main() diff --git a/vehicle_scraper/scrape_toyota.py b/vehicle_scraper/scrape_toyota.py new file mode 100644 index 0000000..3899bb3 --- /dev/null +++ b/vehicle_scraper/scrape_toyota.py @@ -0,0 +1,163 @@ +#!/usr/bin/env python3 +""" +Script optimizado para extraer todos los vehículos Toyota de RockAuto +Guarda datos incrementalmente para no perder progreso +""" + +import requests +from bs4 import BeautifulSoup +import sqlite3 +import time +import re +import sys +from urllib.parse import unquote + +DB_PATH = "/home/Autopartes/vehicle_database/vehicle_database.db" +BASE_URL = "https://www.rockauto.com/en/catalog" + +session = requests.Session() +session.headers.update({ + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', + 'Accept': 'text/html,application/xhtml+xml', + 'Accept-Language': 'en-US,en;q=0.9', +}) + +def clean_name(name): + name = unquote(name.replace('+', ' ')) + return re.sub(r'\s+', ' ', name).strip().upper() + +def get_soup(url, retries=3): + for attempt in range(retries): + try: + time.sleep(0.3) # Delay corto + response = session.get(url, timeout=10) + if response.status_code == 200: + return BeautifulSoup(response.content, 'html.parser') + except Exception as e: + if attempt < retries - 1: + time.sleep(2) + else: + print(f" Error: {e}") + return None + +def get_years(brand): + brand_url = brand.lower().replace(' ', '+') + soup = get_soup(f"{BASE_URL}/{brand_url}") + if not soup: + return [] + + years = set() + for link in soup.find_all('a', href=True): + match = re.search(rf'/catalog/{re.escape(brand_url)},(\d{{4}})', link['href'], re.I) + if match: + year = int(match.group(1)) + if 1950 < year <= 2030: + years.add(year) + return sorted(years, reverse=True) + +def get_models(brand, year): + brand_url = brand.lower().replace(' ', '+') + soup = get_soup(f"{BASE_URL}/{brand_url},{year}") + if not soup: + return [] + + models = set() + for link in soup.find_all('a', href=True): + pattern = rf'/catalog/{re.escape(brand_url)},{year},([^,/]+)' + match = re.search(pattern, link['href'], re.I) + if match: + model = clean_name(match.group(1)) + if model and not model.isdigit() and len(model) > 1: + models.add(model) + return sorted(models) + +def get_engines(brand, year, model): + brand_url = brand.lower().replace(' ', '+') + model_url = model.lower().replace(' ', '+') + soup = get_soup(f"{BASE_URL}/{brand_url},{year},{model_url}") + if not soup: + return [] + + engines = set() + for link in soup.find_all('a', href=True): + pattern = rf'/catalog/{re.escape(brand_url)},{year},{re.escape(model_url)},([^,/]+)' + match = re.search(pattern, link['href'], re.I) + if match: + engine = clean_name(match.group(1)) + if engine and re.search(r'\d+\.?\d*L|V\d|I\d|H\d|HYBRID|ELECTRIC|DIESEL', engine, re.I): + engines.add(engine) + return sorted(engines) if engines else ['Standard'] + +def save_to_db(conn, brand, year, model, engine): + cursor = conn.cursor() + try: + cursor.execute("INSERT OR IGNORE INTO brands (name) VALUES (?)", (brand,)) + cursor.execute("SELECT id FROM brands WHERE name = ?", (brand,)) + brand_id = cursor.fetchone()[0] + + cursor.execute("INSERT OR IGNORE INTO years (year) VALUES (?)", (year,)) + cursor.execute("SELECT id FROM years WHERE year = ?", (year,)) + year_id = cursor.fetchone()[0] + + cursor.execute("INSERT OR IGNORE INTO engines (name) VALUES (?)", (engine,)) + cursor.execute("SELECT id FROM engines WHERE name = ?", (engine,)) + engine_id = cursor.fetchone()[0] + + cursor.execute("INSERT OR IGNORE INTO models (brand_id, name) VALUES (?, ?)", (brand_id, model)) + cursor.execute("SELECT id FROM models WHERE brand_id = ? AND name = ?", (brand_id, model)) + model_id = cursor.fetchone()[0] + + cursor.execute( + "INSERT OR IGNORE INTO model_year_engine (model_id, year_id, engine_id) VALUES (?, ?, ?)", + (model_id, year_id, engine_id) + ) + return cursor.rowcount > 0 + except Exception as e: + print(f" DB Error: {e}") + return False + +def main(): + brand = "TOYOTA" + + print(f"Obteniendo años disponibles para {brand}...") + years = get_years(brand) + print(f"Encontrados {len(years)} años: {years[0]} - {years[-1]}") + + # Filtrar solo 1975-2026 + years = [y for y in years if 1975 <= y <= 2026] + print(f"Procesando años 1975-2026: {len(years)} años") + print("=" * 60) + + conn = sqlite3.connect(DB_PATH) + total_saved = 0 + total_vehicles = 0 + + for i, year in enumerate(years, 1): + print(f"\n[{i}/{len(years)}] Año {year}: ", end="", flush=True) + + models = get_models(brand, year) + print(f"{len(models)} modelos") + + year_count = 0 + for model in models: + engines = get_engines(brand, year, model) + for engine in engines: + total_vehicles += 1 + if save_to_db(conn, brand, year, model, engine): + total_saved += 1 + year_count += 1 + print(f" {model}: {engine}") + + conn.commit() + print(f" -> Guardados: {year_count} nuevos") + + conn.close() + + print("\n" + "=" * 60) + print(f"RESUMEN TOYOTA") + print(f" Años procesados: {len(years)}") + print(f" Total vehículos encontrados: {total_vehicles}") + print(f" Nuevos guardados: {total_saved}") + +if __name__ == "__main__": + main() diff --git a/vehicle_scraper/scrape_toyota_windows.py b/vehicle_scraper/scrape_toyota_windows.py new file mode 100644 index 0000000..16b8038 --- /dev/null +++ b/vehicle_scraper/scrape_toyota_windows.py @@ -0,0 +1,240 @@ +#!/usr/bin/env python3 +""" +Scraper de Toyota para Windows +- Procesa de 3 en 3 años +- Espera 60 segundos entre lotes para activar VPN +- Años faltantes: 1975-2003 +""" + +import requests +from bs4 import BeautifulSoup +import sqlite3 +import time +import re +import os +import sys +from urllib.parse import unquote + +# Detectar ruta base del proyecto +SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) +# Si estamos en vehicle_scraper, subir un nivel +if os.path.basename(SCRIPT_DIR) == "vehicle_scraper": + BASE_DIR = os.path.dirname(SCRIPT_DIR) +else: + BASE_DIR = SCRIPT_DIR +DB_PATH = os.path.join(BASE_DIR, "vehicle_database", "vehicle_database.db") + +BASE_URL = "https://www.rockauto.com/en/catalog" + +# Años que faltan por scrapear +MISSING_YEARS = [ + 2003, 2002, 2001, 2000, 1999, 1998, 1997, 1996, 1995, 1994, + 1993, 1992, 1991, 1990, 1989, 1988, 1987, 1986, 1985, 1984, + 1983, 1982, 1981, 1980, 1979, 1978, 1977, 1976, 1975 +] + +session = requests.Session() +session.headers.update({ + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', + 'Accept': 'text/html,application/xhtml+xml', + 'Accept-Language': 'en-US,en;q=0.9', +}) + +def clean_name(name): + name = unquote(name.replace('+', ' ')) + return re.sub(r'\s+', ' ', name).strip().upper() + +def get_soup(url, retries=3): + for attempt in range(retries): + try: + time.sleep(0.5) + response = session.get(url, timeout=15) + if response.status_code == 200: + return BeautifulSoup(response.content, 'html.parser') + elif response.status_code == 403: + print(f"\n [!] Bloqueado (403) - Cambia el VPN") + return None + except Exception as e: + if attempt < retries - 1: + time.sleep(3) + else: + print(f"\n Error: {e}") + return None + +def get_models(brand, year): + brand_url = brand.lower().replace(' ', '+') + soup = get_soup(f"{BASE_URL}/{brand_url},{year}") + if not soup: + return [] + + models = set() + for link in soup.find_all('a', href=True): + pattern = rf'/catalog/{re.escape(brand_url)},{year},([^,/]+)' + match = re.search(pattern, link['href'], re.I) + if match: + model = clean_name(match.group(1)) + if model and not model.isdigit() and len(model) > 1: + models.add(model) + return sorted(models) + +def get_engines(brand, year, model): + brand_url = brand.lower().replace(' ', '+') + model_url = model.lower().replace(' ', '+') + soup = get_soup(f"{BASE_URL}/{brand_url},{year},{model_url}") + if not soup: + return ['STANDARD'] + + engines = set() + for link in soup.find_all('a', href=True): + pattern = rf'/catalog/{re.escape(brand_url)},{year},{re.escape(model_url)},([^,/]+)' + match = re.search(pattern, link['href'], re.I) + if match: + engine = clean_name(match.group(1)) + if engine and re.search(r'\d+\.?\d*L|V\d|I\d|H\d|HYBRID|ELECTRIC|DIESEL', engine, re.I): + engines.add(engine) + return sorted(engines) if engines else ['STANDARD'] + +def save_to_db(conn, brand, year, model, engine): + cursor = conn.cursor() + try: + cursor.execute("INSERT OR IGNORE INTO brands (name) VALUES (?)", (brand,)) + cursor.execute("SELECT id FROM brands WHERE name = ?", (brand,)) + brand_id = cursor.fetchone()[0] + + cursor.execute("INSERT OR IGNORE INTO years (year) VALUES (?)", (year,)) + cursor.execute("SELECT id FROM years WHERE year = ?", (year,)) + year_id = cursor.fetchone()[0] + + cursor.execute("INSERT OR IGNORE INTO engines (name) VALUES (?)", (engine,)) + cursor.execute("SELECT id FROM engines WHERE name = ?", (engine,)) + engine_id = cursor.fetchone()[0] + + cursor.execute("INSERT OR IGNORE INTO models (brand_id, name) VALUES (?, ?)", (brand_id, model)) + cursor.execute("SELECT id FROM models WHERE brand_id = ? AND name = ?", (brand_id, model)) + model_id = cursor.fetchone()[0] + + cursor.execute( + "INSERT OR IGNORE INTO model_year_engine (model_id, year_id, engine_id) VALUES (?, ?, ?)", + (model_id, year_id, engine_id) + ) + return cursor.rowcount > 0 + except Exception as e: + print(f" DB Error: {e}") + return False + +def get_existing_years(conn, brand): + """Obtiene los años que ya existen para esta marca""" + cursor = conn.cursor() + cursor.execute(""" + SELECT DISTINCT y.year + FROM years y + JOIN model_year_engine mye ON y.id = mye.year_id + JOIN models m ON mye.model_id = m.id + JOIN brands b ON m.brand_id = b.id + WHERE b.name = ? + """, (brand,)) + return set(row[0] for row in cursor.fetchall()) + +def process_batch(conn, brand, years_batch, batch_num, total_batches): + """Procesa un lote de 3 años""" + print(f"\n{'='*60}") + print(f"LOTE {batch_num}/{total_batches}: Años {years_batch}") + print('='*60) + + batch_saved = 0 + batch_total = 0 + + for year in years_batch: + print(f"\n[Año {year}] Obteniendo modelos... ", end="", flush=True) + models = get_models(brand, year) + print(f"{len(models)} modelos encontrados") + + if not models: + print(f" No se encontraron modelos para {year}") + continue + + for model in models: + engines = get_engines(brand, year, model) + for engine in engines: + batch_total += 1 + if save_to_db(conn, brand, year, model, engine): + batch_saved += 1 + print(f" {model} - {engine}") + + # Guardar cambios del lote + conn.commit() + print(f"\n>> Lote {batch_num} completado: {batch_saved} nuevos de {batch_total} encontrados") + return batch_saved, batch_total + +def main(): + brand = "TOYOTA" + + print("="*60) + print(" SCRAPER TOYOTA - WINDOWS") + print(" Procesa 3 años, guarda, espera 60s para VPN") + print("="*60) + + # Verificar base de datos + if not os.path.exists(DB_PATH): + print(f"\n[ERROR] Base de datos no encontrada: {DB_PATH}") + print("Verifica que la ruta sea correcta.") + sys.exit(1) + + print(f"\nBase de datos: {DB_PATH}") + + conn = sqlite3.connect(DB_PATH) + + # Verificar qué años ya existen + existing = get_existing_years(conn, brand) + print(f"Años existentes de {brand}: {sorted(existing)}") + + # Filtrar solo los que faltan + years_to_process = [y for y in MISSING_YEARS if y not in existing] + + if not years_to_process: + print("\n[OK] Todos los años ya están en la base de datos!") + conn.close() + return + + print(f"\nAños por procesar: {years_to_process}") + print(f"Total: {len(years_to_process)} años") + + # Dividir en lotes de 3 + batches = [years_to_process[i:i+3] for i in range(0, len(years_to_process), 3)] + total_batches = len(batches) + + print(f"Lotes de 3 años: {total_batches} lotes") + input("\nPresiona ENTER para comenzar...") + + total_saved = 0 + total_found = 0 + + for i, batch in enumerate(batches, 1): + saved, found = process_batch(conn, brand, batch, i, total_batches) + total_saved += saved + total_found += found + + # Si no es el último lote, esperar para cambiar VPN + if i < total_batches: + print(f"\n{'*'*60}") + print(f" PAUSA DE 60 SEGUNDOS - ACTIVA/CAMBIA EL VPN AHORA") + print(f" Lotes restantes: {total_batches - i}") + print(f"{'*'*60}") + + for sec in range(60, 0, -1): + print(f"\r Continuando en {sec} segundos... ", end="", flush=True) + time.sleep(1) + print() + + conn.close() + + print("\n" + "="*60) + print(" RESUMEN FINAL - TOYOTA") + print("="*60) + print(f" Años procesados: {len(years_to_process)}") + print(f" Vehículos encontrados: {total_found}") + print(f" Nuevos guardados: {total_saved}") + print("="*60) + +if __name__ == "__main__": + main()