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
This commit is contained in:
53
.gitignore
vendored
Normal file
53
.gitignore
vendored
Normal file
@@ -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
|
||||||
103
DASHBOARD_GUIDE.md
Normal file
103
DASHBOARD_GUIDE.md
Normal file
@@ -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!
|
||||||
126
FINAL_SUMMARY.md
Normal file
126
FINAL_SUMMARY.md
Normal file
@@ -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.
|
||||||
53
QUICK_START.sh
Executable file
53
QUICK_START.sh
Executable file
@@ -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 "=============================================="
|
||||||
306
README.md
Normal file
306
README.md
Normal file
@@ -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
|
||||||
177
add_abarth_data.py
Normal file
177
add_abarth_data.py
Normal file
@@ -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()
|
||||||
178
add_ac_data.py
Normal file
178
add_ac_data.py
Normal file
@@ -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()
|
||||||
200
add_acura_additional_data.py
Normal file
200
add_acura_additional_data.py
Normal file
@@ -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()
|
||||||
317
add_acura_data.py
Normal file
317
add_acura_data.py
Normal file
@@ -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()
|
||||||
188
add_acura_historical_data.py
Normal file
188
add_acura_historical_data.py
Normal file
@@ -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()
|
||||||
308
add_alfa_romeo_data.py
Normal file
308
add_alfa_romeo_data.py
Normal file
@@ -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()
|
||||||
279
add_american_motors_data.py
Normal file
279
add_american_motors_data.py
Normal file
@@ -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()
|
||||||
445
add_aston_martin_data.py
Normal file
445
add_aston_martin_data.py
Normal file
@@ -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()
|
||||||
167
add_asuna_data.py
Normal file
167
add_asuna_data.py
Normal file
@@ -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()
|
||||||
372
add_audi_data.py
Normal file
372
add_audi_data.py
Normal file
@@ -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()
|
||||||
379
add_audi_data_extended.py
Normal file
379
add_audi_data_extended.py
Normal file
@@ -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()
|
||||||
367
add_audi_data_extended2.py
Normal file
367
add_audi_data_extended2.py
Normal file
@@ -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()
|
||||||
177
add_crosley_data.py
Normal file
177
add_crosley_data.py
Normal file
@@ -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()
|
||||||
255
add_historical_data.py
Normal file
255
add_historical_data.py
Normal file
@@ -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()
|
||||||
127
check_and_remove_brands.py
Normal file
127
check_and_remove_brands.py
Normal file
@@ -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()
|
||||||
79
dashboard/README.md
Normal file
79
dashboard/README.md
Normal file
@@ -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
|
||||||
440
dashboard/dashboard.js
Normal file
440
dashboard/dashboard.js
Normal file
@@ -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 = `<li class="breadcrumb-item active"><i class="fas fa-home"></i> Marcas</li>`;
|
||||||
|
} else if (this.currentView === 'models') {
|
||||||
|
html = `
|
||||||
|
<li class="breadcrumb-item">
|
||||||
|
<a href="#" onclick="dashboard.goToBrands(); return false;">
|
||||||
|
<i class="fas fa-home"></i> Marcas
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="breadcrumb-item active">${this.selectedBrand}</li>
|
||||||
|
`;
|
||||||
|
} else if (this.currentView === 'vehicles') {
|
||||||
|
html = `
|
||||||
|
<li class="breadcrumb-item">
|
||||||
|
<a href="#" onclick="dashboard.goToBrands(); return false;">
|
||||||
|
<i class="fas fa-home"></i> Marcas
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="breadcrumb-item">
|
||||||
|
<a href="#" onclick="dashboard.goToModels('${this.selectedBrand}'); return false;">
|
||||||
|
${this.selectedBrand}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="breadcrumb-item active">${this.selectedModel}</li>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = `
|
||||||
|
<div class="loading-state">
|
||||||
|
<i class="fas fa-spinner fa-spin"></i>
|
||||||
|
<h4>Cargando marcas...</h4>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
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 = `
|
||||||
|
<div class="empty-state">
|
||||||
|
<i class="fas fa-car"></i>
|
||||||
|
<h4>No hay marcas disponibles</h4>
|
||||||
|
<p>Agrega algunas marcas a la base de datos</p>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
container.innerHTML = `<div class="content-grid brands-grid">
|
||||||
|
${brands.map(brand => `
|
||||||
|
<div class="brand-card" onclick="dashboard.goToModels('${brand}')">
|
||||||
|
<div class="brand-icon">
|
||||||
|
<i class="fas fa-car"></i>
|
||||||
|
</div>
|
||||||
|
<div class="brand-name">${brand}</div>
|
||||||
|
<div class="brand-count">
|
||||||
|
${brandStats[brand].models.size} modelos
|
||||||
|
</div>
|
||||||
|
<div class="brand-count">
|
||||||
|
${brandStats[brand].vehicles} vehículos
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`).join('')}
|
||||||
|
</div>`;
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error:', error);
|
||||||
|
container.innerHTML = `
|
||||||
|
<div class="empty-state">
|
||||||
|
<i class="fas fa-exclamation-triangle"></i>
|
||||||
|
<h4>Error al cargar marcas</h4>
|
||||||
|
<p>${error.message}</p>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async goToModels(brand) {
|
||||||
|
this.currentView = 'models';
|
||||||
|
this.selectedBrand = brand;
|
||||||
|
this.selectedModel = null;
|
||||||
|
this.updateBreadcrumb();
|
||||||
|
this.hideFilters();
|
||||||
|
|
||||||
|
const container = document.getElementById('mainContent');
|
||||||
|
container.innerHTML = `
|
||||||
|
<div class="loading-state">
|
||||||
|
<i class="fas fa-spinner fa-spin"></i>
|
||||||
|
<h4>Cargando modelos de ${brand}...</h4>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
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 = `
|
||||||
|
<div class="empty-state">
|
||||||
|
<i class="fas fa-car-side"></i>
|
||||||
|
<h4>No hay modelos para ${brand}</h4>
|
||||||
|
<p>Esta marca no tiene modelos registrados</p>
|
||||||
|
<button class="btn btn-back mt-3" onclick="dashboard.goToBrands()">
|
||||||
|
<i class="fas fa-arrow-left"></i> Volver a marcas
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
container.innerHTML = `<div class="content-grid models-grid">
|
||||||
|
${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 `
|
||||||
|
<div class="model-card" onclick="dashboard.goToVehicles('${brand}', '${model}')">
|
||||||
|
<div class="model-name">${model}</div>
|
||||||
|
<div class="model-info">
|
||||||
|
<i class="fas fa-calendar-alt"></i> ${yearRange}
|
||||||
|
</div>
|
||||||
|
<div class="model-info">
|
||||||
|
<i class="fas fa-cogs"></i> ${stats.engines.size} motores
|
||||||
|
</div>
|
||||||
|
<div class="model-info">
|
||||||
|
<i class="fas fa-list"></i> ${stats.vehicles} variantes
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}).join('')}
|
||||||
|
</div>`;
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error:', error);
|
||||||
|
container.innerHTML = `
|
||||||
|
<div class="empty-state">
|
||||||
|
<i class="fas fa-exclamation-triangle"></i>
|
||||||
|
<h4>Error al cargar modelos</h4>
|
||||||
|
<p>${error.message}</p>
|
||||||
|
<button class="btn btn-back mt-3" onclick="dashboard.goToBrands()">
|
||||||
|
<i class="fas fa-arrow-left"></i> Volver a marcas
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async goToVehicles(brand, model) {
|
||||||
|
this.currentView = 'vehicles';
|
||||||
|
this.selectedBrand = brand;
|
||||||
|
this.selectedModel = model;
|
||||||
|
this.updateBreadcrumb();
|
||||||
|
this.showFilters();
|
||||||
|
|
||||||
|
const container = document.getElementById('mainContent');
|
||||||
|
container.innerHTML = `
|
||||||
|
<div class="loading-state">
|
||||||
|
<i class="fas fa-spinner fa-spin"></i>
|
||||||
|
<h4>Cargando vehículos...</h4>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
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 = `
|
||||||
|
<div class="empty-state">
|
||||||
|
<i class="fas fa-exclamation-triangle"></i>
|
||||||
|
<h4>Error al cargar vehículos</h4>
|
||||||
|
<p>${error.message}</p>
|
||||||
|
<button class="btn btn-back mt-3" onclick="dashboard.goToModels('${brand}')">
|
||||||
|
<i class="fas fa-arrow-left"></i> Volver a modelos
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = '<option value="">Todos los años</option>';
|
||||||
|
years.forEach(year => {
|
||||||
|
yearFilter.innerHTML += `<option value="${year}">${year}</option>`;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (enginesRes.ok) {
|
||||||
|
const engines = await enginesRes.json();
|
||||||
|
const engineFilter = document.getElementById('engineFilter');
|
||||||
|
engineFilter.innerHTML = '<option value="">Todos los motores</option>';
|
||||||
|
engines.forEach(engine => {
|
||||||
|
engineFilter.innerHTML += `<option value="${engine}">${engine}</option>`;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
} 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 = `
|
||||||
|
<div class="empty-state">
|
||||||
|
<i class="fas fa-car"></i>
|
||||||
|
<h4>No se encontraron vehículos</h4>
|
||||||
|
<p>Intenta ajustar los filtros</p>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
container.innerHTML = `<div class="content-grid vehicles-grid">
|
||||||
|
${this.filteredVehicles.map(v => `
|
||||||
|
<div class="vehicle-card">
|
||||||
|
<div class="vehicle-header">
|
||||||
|
<div class="vehicle-title">${v.year} ${v.brand} ${v.model}</div>
|
||||||
|
<div class="vehicle-engine">${v.engine}</div>
|
||||||
|
</div>
|
||||||
|
<div class="vehicle-body">
|
||||||
|
<div class="vehicle-specs">
|
||||||
|
<div class="spec-item">
|
||||||
|
<i class="fas fa-gas-pump"></i>
|
||||||
|
<div class="spec-value">${v.fuel_type || 'N/A'}</div>
|
||||||
|
</div>
|
||||||
|
<div class="spec-item">
|
||||||
|
<i class="fas fa-tachometer-alt"></i>
|
||||||
|
<div class="spec-value">${v.power_hp || 0} HP</div>
|
||||||
|
</div>
|
||||||
|
<div class="spec-item">
|
||||||
|
<i class="fas fa-cogs"></i>
|
||||||
|
<div class="spec-value">${v.transmission || 'N/A'}</div>
|
||||||
|
</div>
|
||||||
|
<div class="spec-item">
|
||||||
|
<i class="fas fa-road"></i>
|
||||||
|
<div class="spec-value">${v.drivetrain || 'N/A'}</div>
|
||||||
|
</div>
|
||||||
|
<div class="spec-item">
|
||||||
|
<i class="fas fa-cube"></i>
|
||||||
|
<div class="spec-value">${v.cylinders || 0} cil.</div>
|
||||||
|
</div>
|
||||||
|
<div class="spec-item">
|
||||||
|
<i class="fas fa-oil-can"></i>
|
||||||
|
<div class="spec-value">${v.displacement_cc || 0} cc</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
${v.trim_level && v.trim_level !== 'unknown' ? `
|
||||||
|
<div class="mt-2 text-center">
|
||||||
|
<span class="badge bg-primary">${v.trim_level}</span>
|
||||||
|
</div>
|
||||||
|
` : ''}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`).join('')}
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
});
|
||||||
367
dashboard/index.html
Normal file
367
dashboard/index.html
Normal file
@@ -0,0 +1,367 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="es">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Base de Datos de Vehículos</title>
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
--primary-color: #2c3e50;
|
||||||
|
--secondary-color: #3498db;
|
||||||
|
--accent-color: #e74c3c;
|
||||||
|
--bg-color: #ecf0f1;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background-color: var(--bg-color);
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard-header {
|
||||||
|
background: linear-gradient(135deg, var(--primary-color), #1a252f);
|
||||||
|
color: white;
|
||||||
|
padding: 2rem 0;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.breadcrumb-nav {
|
||||||
|
background: white;
|
||||||
|
padding: 1rem 1.5rem;
|
||||||
|
border-radius: 10px;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.breadcrumb-nav .breadcrumb {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.breadcrumb-nav .breadcrumb-item a {
|
||||||
|
color: var(--secondary-color);
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.breadcrumb-nav .breadcrumb-item a:hover {
|
||||||
|
color: var(--primary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.breadcrumb-nav .breadcrumb-item.active {
|
||||||
|
color: var(--primary-color);
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tarjetas de marcas */
|
||||||
|
.brand-card {
|
||||||
|
background: white;
|
||||||
|
border-radius: 15px;
|
||||||
|
padding: 2rem;
|
||||||
|
text-align: center;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
cursor: pointer;
|
||||||
|
border: 2px solid transparent;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.brand-card:hover {
|
||||||
|
transform: translateY(-10px);
|
||||||
|
box-shadow: 0 15px 30px rgba(0,0,0,0.15);
|
||||||
|
border-color: var(--secondary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.brand-card .brand-icon {
|
||||||
|
width: 80px;
|
||||||
|
height: 80px;
|
||||||
|
background: linear-gradient(135deg, var(--secondary-color), #2980b9);
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin: 0 auto 1rem;
|
||||||
|
font-size: 2rem;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.brand-card .brand-name {
|
||||||
|
font-size: 1.3rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--primary-color);
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.brand-card .brand-count {
|
||||||
|
color: #7f8c8d;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.brand-card .brand-country {
|
||||||
|
font-size: 0.85rem;
|
||||||
|
color: #95a5a6;
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tarjetas de modelos */
|
||||||
|
.model-card {
|
||||||
|
background: white;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 1.5rem;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
cursor: pointer;
|
||||||
|
border: 2px solid transparent;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.model-card:hover {
|
||||||
|
transform: translateY(-5px);
|
||||||
|
box-shadow: 0 10px 25px rgba(0,0,0,0.12);
|
||||||
|
border-color: var(--secondary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.model-card .model-name {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--primary-color);
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.model-card .model-info {
|
||||||
|
color: #7f8c8d;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tarjetas de vehículos */
|
||||||
|
.vehicle-card {
|
||||||
|
background: white;
|
||||||
|
border-radius: 12px;
|
||||||
|
overflow: hidden;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vehicle-card:hover {
|
||||||
|
transform: translateY(-5px);
|
||||||
|
box-shadow: 0 10px 25px rgba(0,0,0,0.12);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vehicle-header {
|
||||||
|
background: linear-gradient(135deg, var(--primary-color), #1a252f);
|
||||||
|
color: white;
|
||||||
|
padding: 1.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vehicle-header .vehicle-title {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 0.3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vehicle-header .vehicle-engine {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vehicle-body {
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vehicle-specs {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
gap: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spec-item {
|
||||||
|
background: var(--bg-color);
|
||||||
|
padding: 0.6rem;
|
||||||
|
border-radius: 8px;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spec-item i {
|
||||||
|
color: var(--secondary-color);
|
||||||
|
margin-bottom: 0.3rem;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spec-item .spec-value {
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--primary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Filtros en vista de vehículos */
|
||||||
|
.filters-bar {
|
||||||
|
background: white;
|
||||||
|
padding: 1rem 1.5rem;
|
||||||
|
border-radius: 10px;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.filters-bar .form-select {
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Grid de contenido */
|
||||||
|
.content-grid {
|
||||||
|
display: grid;
|
||||||
|
gap: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-grid.brands-grid {
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-grid.models-grid {
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-grid.vehicles-grid {
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Estados */
|
||||||
|
.loading-state, .empty-state {
|
||||||
|
text-align: center;
|
||||||
|
padding: 4rem 2rem;
|
||||||
|
color: #7f8c8d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-state i, .empty-state i {
|
||||||
|
font-size: 4rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
color: #bdc3c7;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Botón volver */
|
||||||
|
.btn-back {
|
||||||
|
background: var(--secondary-color);
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
padding: 0.5rem 1.5rem;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-weight: 500;
|
||||||
|
transition: all 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-back:hover {
|
||||||
|
background: #2980b9;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Estadísticas */
|
||||||
|
.stats-bar {
|
||||||
|
display: flex;
|
||||||
|
gap: 2rem;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-item {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-item .stat-number {
|
||||||
|
font-size: 2rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-item .stat-label {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.content-grid.brands-grid {
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-bar {
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
.content-grid.brands-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="dashboard-header">
|
||||||
|
<div class="container">
|
||||||
|
<div class="row align-items-center">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<h1><i class="fas fa-car-side"></i> Base de Datos de Vehículos</h1>
|
||||||
|
<p class="lead mb-0">Explora vehículos por marca, modelo y especificaciones</p>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="stats-bar justify-content-md-end">
|
||||||
|
<div class="stat-item">
|
||||||
|
<div class="stat-number" id="totalBrands">0</div>
|
||||||
|
<div class="stat-label">Marcas</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-item">
|
||||||
|
<div class="stat-number" id="totalModels">0</div>
|
||||||
|
<div class="stat-label">Modelos</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-item">
|
||||||
|
<div class="stat-number" id="totalVehicles">0</div>
|
||||||
|
<div class="stat-label">Vehículos</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<!-- Breadcrumb navegación -->
|
||||||
|
<div class="breadcrumb-nav" id="breadcrumbNav">
|
||||||
|
<nav aria-label="breadcrumb">
|
||||||
|
<ol class="breadcrumb" id="breadcrumb">
|
||||||
|
<li class="breadcrumb-item active">
|
||||||
|
<i class="fas fa-home"></i> Marcas
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Barra de filtros (solo visible en vista de vehículos) -->
|
||||||
|
<div class="filters-bar" id="filtersBar" style="display: none;">
|
||||||
|
<div class="row g-3 align-items-center">
|
||||||
|
<div class="col-auto">
|
||||||
|
<label class="col-form-label fw-bold">Filtrar por:</label>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3">
|
||||||
|
<select id="yearFilter" class="form-select">
|
||||||
|
<option value="">Todos los años</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3">
|
||||||
|
<select id="engineFilter" class="form-select">
|
||||||
|
<option value="">Todos los motores</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
|
<span id="resultCount" class="badge bg-secondary fs-6">0 resultados</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Contenedor principal -->
|
||||||
|
<div id="mainContent">
|
||||||
|
<div class="loading-state">
|
||||||
|
<i class="fas fa-spinner fa-spin"></i>
|
||||||
|
<h4>Cargando...</h4>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
|
<script src="dashboard.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
1
dashboard/requirements.txt
Normal file
1
dashboard/requirements.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Flask==2.3.3
|
||||||
248
dashboard/server.py
Normal file
248
dashboard/server.py
Normal file
@@ -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('/<path:path>')
|
||||||
|
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)
|
||||||
30
dashboard/start_dashboard.sh
Executable file
30
dashboard/start_dashboard.sh
Executable file
@@ -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
|
||||||
270
docs/API.md
Normal file
270
docs/API.md
Normal file
@@ -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.
|
||||||
425
docs/DATABASE.md
Normal file
425
docs/DATABASE.md
Normal file
@@ -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 <<EOF
|
||||||
|
.mode csv
|
||||||
|
.import brands.csv brands
|
||||||
|
.import models.csv models
|
||||||
|
.import engines.csv engines
|
||||||
|
EOF
|
||||||
|
```
|
||||||
326
docs/INSTALLATION.md
Normal file
326
docs/INSTALLATION.md
Normal file
@@ -0,0 +1,326 @@
|
|||||||
|
# Guía de Instalación - Autoparts DB
|
||||||
|
|
||||||
|
## Requisitos del Sistema
|
||||||
|
|
||||||
|
### Software Requerido
|
||||||
|
|
||||||
|
| Software | Versión Mínima | Propósito |
|
||||||
|
|----------|----------------|-----------|
|
||||||
|
| Python | 3.8+ | Lenguaje principal |
|
||||||
|
| pip | 20.0+ | Gestor de paquetes |
|
||||||
|
| Git | 2.0+ | Control de versiones |
|
||||||
|
|
||||||
|
### Sistema Operativo
|
||||||
|
|
||||||
|
El proyecto es compatible con:
|
||||||
|
- Linux (Ubuntu, Debian, CentOS, etc.)
|
||||||
|
- macOS
|
||||||
|
- Windows 10/11
|
||||||
|
|
||||||
|
### Espacio en Disco
|
||||||
|
|
||||||
|
- Mínimo: 50 MB
|
||||||
|
- Recomendado: 100 MB (para crecimiento de la base de datos)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Instalación Rápida
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Clonar el repositorio
|
||||||
|
git clone https://git.consultoria-as.com/[usuario]/Autoparts-DB.git
|
||||||
|
|
||||||
|
# 2. Entrar al directorio
|
||||||
|
cd Autoparts-DB
|
||||||
|
|
||||||
|
# 3. Instalar dependencias
|
||||||
|
pip install -r requirements.txt
|
||||||
|
|
||||||
|
# 4. Iniciar el dashboard
|
||||||
|
cd dashboard
|
||||||
|
python3 server.py
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Instalación Detallada
|
||||||
|
|
||||||
|
### Paso 1: Clonar el Repositorio
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://git.consultoria-as.com/[usuario]/Autoparts-DB.git
|
||||||
|
cd Autoparts-DB
|
||||||
|
```
|
||||||
|
|
||||||
|
### Paso 2: Crear Entorno Virtual (Recomendado)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Crear entorno virtual
|
||||||
|
python3 -m venv venv
|
||||||
|
|
||||||
|
# Activar entorno virtual
|
||||||
|
# Linux/macOS:
|
||||||
|
source venv/bin/activate
|
||||||
|
|
||||||
|
# Windows:
|
||||||
|
venv\Scripts\activate
|
||||||
|
```
|
||||||
|
|
||||||
|
### Paso 3: Instalar Dependencias
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Instalar todas las dependencias
|
||||||
|
pip install flask requests beautifulsoup4 lxml
|
||||||
|
|
||||||
|
# O usando requirements.txt
|
||||||
|
pip install -r requirements.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
### Paso 4: Verificar la Base de Datos
|
||||||
|
|
||||||
|
La base de datos viene pre-poblada con datos. Para verificar:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd vehicle_database
|
||||||
|
sqlite3 vehicle_database.db "SELECT COUNT(*) FROM brands;"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Paso 5: Inicializar (Opcional)
|
||||||
|
|
||||||
|
Si necesitas reiniciar la base de datos:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd vehicle_database
|
||||||
|
./setup.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Dependencias de Python
|
||||||
|
|
||||||
|
### Archivo requirements.txt
|
||||||
|
|
||||||
|
```
|
||||||
|
flask==2.3.3
|
||||||
|
requests>=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
|
||||||
|
```
|
||||||
180
fix_arra_data.py
Normal file
180
fix_arra_data.py
Normal file
@@ -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()
|
||||||
95
integration_demo.py
Normal file
95
integration_demo.py
Normal file
@@ -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()
|
||||||
141
remove_brands_and_cleanup.py
Normal file
141
remove_brands_and_cleanup.py
Normal file
@@ -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()
|
||||||
105
remove_brands_and_cleanup_final.py
Normal file
105
remove_brands_and_cleanup_final.py
Normal file
@@ -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()
|
||||||
102
remove_old_vehicles.py
Normal file
102
remove_old_vehicles.py
Normal file
@@ -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()
|
||||||
98
remove_old_vehicles_auto.py
Normal file
98
remove_old_vehicles_auto.py
Normal file
@@ -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()
|
||||||
4
requirements.txt
Normal file
4
requirements.txt
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
flask==2.3.3
|
||||||
|
requests>=2.28.0
|
||||||
|
beautifulsoup4>=4.11.0
|
||||||
|
lxml>=4.9.0
|
||||||
185
scrape_toyota_windows.py
Normal file
185
scrape_toyota_windows.py
Normal file
@@ -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()
|
||||||
54
test_dashboard.sh
Executable file
54
test_dashboard.sh
Executable file
@@ -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"
|
||||||
82
vehicle_database/GETTING_STARTED.md
Normal file
82
vehicle_database/GETTING_STARTED.md
Normal file
@@ -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
|
||||||
106
vehicle_database/README.md
Normal file
106
vehicle_database/README.md
Normal file
@@ -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
|
||||||
|
```
|
||||||
6
vehicle_database/data/brands.csv
Normal file
6
vehicle_database/data/brands.csv
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
name,country,founded_year
|
||||||
|
Mercedes-Benz,Germany,1926
|
||||||
|
Audi,Germany,1909
|
||||||
|
Nissan,Japan,1933
|
||||||
|
Chevrolet,USA,1911
|
||||||
|
Volkswagen,Germany,1937
|
||||||
|
5
vehicle_database/data/engines.csv
Normal file
5
vehicle_database/data/engines.csv
Normal file
@@ -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
|
||||||
|
5
vehicle_database/data/models.csv
Normal file
5
vehicle_database/data/models.csv
Normal file
@@ -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,
|
||||||
|
152
vehicle_database/scripts/csv_importer.py
Normal file
152
vehicle_database/scripts/csv_importer.py
Normal file
@@ -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()
|
||||||
323
vehicle_database/scripts/database_manager.py
Normal file
323
vehicle_database/scripts/database_manager.py
Normal file
@@ -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()
|
||||||
193
vehicle_database/scripts/query_interface.py
Normal file
193
vehicle_database/scripts/query_interface.py
Normal file
@@ -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()
|
||||||
0
vehicle_database/scripts/vehicle_database.db
Normal file
0
vehicle_database/scripts/vehicle_database.db
Normal file
29
vehicle_database/setup.sh
Executable file
29
vehicle_database/setup.sh
Executable file
@@ -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"
|
||||||
66
vehicle_database/sql/schema.sql
Normal file
66
vehicle_database/sql/schema.sql
Normal file
@@ -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);
|
||||||
BIN
vehicle_database/vehicle_database.db
Normal file
BIN
vehicle_database/vehicle_database.db
Normal file
Binary file not shown.
175
vehicle_scraper/manual_input.py
Normal file
175
vehicle_scraper/manual_input.py
Normal file
@@ -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()
|
||||||
171
vehicle_scraper/manual_input_simple.py
Normal file
171
vehicle_scraper/manual_input_simple.py
Normal file
@@ -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()
|
||||||
3
vehicle_scraper/requirements.txt
Normal file
3
vehicle_scraper/requirements.txt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
requests
|
||||||
|
beautifulsoup4
|
||||||
|
lxml
|
||||||
292
vehicle_scraper/rockauto_scraper.py
Normal file
292
vehicle_scraper/rockauto_scraper.py
Normal file
@@ -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()
|
||||||
400
vehicle_scraper/rockauto_scraper_enhanced.py
Normal file
400
vehicle_scraper/rockauto_scraper_enhanced.py
Normal file
@@ -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()
|
||||||
350
vehicle_scraper/rockauto_scraper_v2.py
Normal file
350
vehicle_scraper/rockauto_scraper_v2.py
Normal file
@@ -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()
|
||||||
393
vehicle_scraper/scrape_nissan_ford_chevrolet.py
Normal file
393
vehicle_scraper/scrape_nissan_ford_chevrolet.py
Normal file
@@ -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()
|
||||||
163
vehicle_scraper/scrape_toyota.py
Normal file
163
vehicle_scraper/scrape_toyota.py
Normal file
@@ -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()
|
||||||
240
vehicle_scraper/scrape_toyota_windows.py
Normal file
240
vehicle_scraper/scrape_toyota_windows.py
Normal file
@@ -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()
|
||||||
Reference in New Issue
Block a user