Init
This commit is contained in:
45
.gitignore
vendored
Normal file
45
.gitignore
vendored
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
# Maven
|
||||||
|
target/
|
||||||
|
pom.xml.tag
|
||||||
|
pom.xml.releaseBackup
|
||||||
|
pom.xml.versionsBackup
|
||||||
|
pom.xml.next
|
||||||
|
release.properties
|
||||||
|
dependency-reduced-pom.xml
|
||||||
|
buildNumber.properties
|
||||||
|
.mvn/timing.properties
|
||||||
|
.mvn/wrapper/maven-wrapper.jar
|
||||||
|
|
||||||
|
### IntelliJ IDEA ###
|
||||||
|
.idea/
|
||||||
|
*.iws
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
out/
|
||||||
|
!**/src/main/**/out/
|
||||||
|
!**/src/test/**/out/
|
||||||
|
|
||||||
|
### Eclipse ###
|
||||||
|
.apt_generated
|
||||||
|
.classpath
|
||||||
|
.factorypath
|
||||||
|
.project
|
||||||
|
.settings
|
||||||
|
.springBeans
|
||||||
|
.sts4-cache
|
||||||
|
bin/
|
||||||
|
!**/src/main/**/bin/
|
||||||
|
!**/src/test/**/bin/
|
||||||
|
|
||||||
|
### NetBeans ###
|
||||||
|
/nbproject/private/
|
||||||
|
/nbbuild/
|
||||||
|
/dist/
|
||||||
|
/nbdist/
|
||||||
|
/.nb-gradle/
|
||||||
|
|
||||||
|
### VS Code ###
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
### Mac OS ###
|
||||||
|
.DS_Store
|
||||||
27
Dockerfile
Normal file
27
Dockerfile
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
# ==================== BUILD STAGE ====================
|
||||||
|
FROM maven:3.9-eclipse-temurin-25-alpine AS builder
|
||||||
|
WORKDIR /app
|
||||||
|
# Copy POM first (allows for cached dependency layer)
|
||||||
|
COPY pom.xml .
|
||||||
|
# This will now work if the opencv dependency has no classifier
|
||||||
|
# -----LOCAL----
|
||||||
|
RUN mvn dependency:resolve -B
|
||||||
|
# -----LOCAL----
|
||||||
|
# RUN mvn dependency:go-offline -B
|
||||||
|
|
||||||
|
COPY src ./src
|
||||||
|
# Updated with both properties to avoid the warning
|
||||||
|
RUN mvn package -DskipTests -Dquarkus.package.jar.type=uber-jar -Dquarkus.package.jar.enabled=true
|
||||||
|
|
||||||
|
# ==================== RUNTIME STAGE ====================
|
||||||
|
FROM eclipse-temurin:25-jre
|
||||||
|
WORKDIR /app
|
||||||
|
RUN groupadd -r quarkus && useradd -r -g quarkus quarkus
|
||||||
|
COPY --from=builder --chown=quarkus:quarkus /app/target/scrape-ui-*.jar app.jar
|
||||||
|
USER quarkus
|
||||||
|
EXPOSE 8081
|
||||||
|
ENTRYPOINT ["java", \
|
||||||
|
"-Dio.netty.tryReflectionSetAccessible=true", \
|
||||||
|
"--enable-native-access=ALL-UNNAMED", \
|
||||||
|
"--sun-misc-unsafe-memory-access=allow", \
|
||||||
|
"-jar", "app.jar"]
|
||||||
166
README.md
Normal file
166
README.md
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
# Sophena - Troostwijk Auctions Data Extraction
|
||||||
|
|
||||||
|
A full-stack application for scraping and analyzing auction data from Troostwijk Auctions, consisting of a Quarkus backend and Python scraper.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- **Java 25** (for Quarkus)
|
||||||
|
- **Maven 3.8+**
|
||||||
|
- **Python 3.8+**
|
||||||
|
- **pip** (Python package manager)
|
||||||
|
|
||||||
|
## Project Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
scrape-ui/
|
||||||
|
├── src/ # Quarkus Java backend
|
||||||
|
├── python/ # Python scrapers
|
||||||
|
│ ├── kimki-troost.py # Main scraper
|
||||||
|
│ ├── advanced_crawler.py # Advanced crawling system
|
||||||
|
│ └── troostwijk_data_extractor.py
|
||||||
|
├── public/ # Static web assets
|
||||||
|
├── pom.xml # Maven configuration
|
||||||
|
└── README.md
|
||||||
|
```
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
### 1. Starting the Quarkus Application
|
||||||
|
|
||||||
|
#### Development Mode (with hot reload)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mvn quarkus:dev
|
||||||
|
```
|
||||||
|
|
||||||
|
The application will start on `http://localhost:8080`
|
||||||
|
|
||||||
|
#### Production Mode
|
||||||
|
|
||||||
|
Build the application:
|
||||||
|
```bash
|
||||||
|
mvn clean package
|
||||||
|
```
|
||||||
|
|
||||||
|
Run the packaged application:
|
||||||
|
```bash
|
||||||
|
java -jar target/quarkus-app/quarkus-run.jar
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Using Docker
|
||||||
|
|
||||||
|
Build the Docker image:
|
||||||
|
```bash
|
||||||
|
docker build -t scrape-ui .
|
||||||
|
```
|
||||||
|
|
||||||
|
Run the container:
|
||||||
|
```bash
|
||||||
|
docker run -p 8080:8080 scrape-ui
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Running the Python Scraper
|
||||||
|
|
||||||
|
#### Install Dependencies
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd python
|
||||||
|
pip install -r requirements.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
If `requirements.txt` doesn't exist, install common dependencies:
|
||||||
|
```bash
|
||||||
|
pip install requests beautifulsoup4 selenium lxml
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Run the Main Scraper
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python kimki-troost.py
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Alternative Scrapers
|
||||||
|
|
||||||
|
**Advanced Crawler** (with fallback strategies):
|
||||||
|
```bash
|
||||||
|
python advanced_crawler.py
|
||||||
|
```
|
||||||
|
|
||||||
|
**Data Extractor** (with mock data):
|
||||||
|
```bash
|
||||||
|
python troostwijk_data_extractor.py
|
||||||
|
```
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
### Quarkus Backend
|
||||||
|
- RESTful API with JAX-RS
|
||||||
|
- JSON serialization with Jackson
|
||||||
|
- Dependency injection with CDI
|
||||||
|
- Hot reload in development mode
|
||||||
|
- Optimized for Java 25
|
||||||
|
|
||||||
|
### Python Scraper
|
||||||
|
- Multiple scraping strategies
|
||||||
|
- User agent rotation
|
||||||
|
- Anti-detection mechanisms
|
||||||
|
- Data export to JSON/CSV
|
||||||
|
- Interactive dashboard generation
|
||||||
|
|
||||||
|
## API Endpoints
|
||||||
|
|
||||||
|
Access the Quarkus REST endpoints at:
|
||||||
|
- `http://localhost:8080/api/*`
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
### Quarkus Dev Mode Features
|
||||||
|
- Automatic code reload on changes
|
||||||
|
- Dev UI available at `http://localhost:8080/q/dev`
|
||||||
|
- Built-in debugging support
|
||||||
|
|
||||||
|
### Python Development
|
||||||
|
- Scrapers output data to timestamped files
|
||||||
|
- Generated files include JSON, CSV, and analysis reports
|
||||||
|
- Interactive dashboard created as `index.html`
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
### Quarkus Configuration
|
||||||
|
Edit `src/main/resources/application.properties` for:
|
||||||
|
- Server port
|
||||||
|
- Database settings
|
||||||
|
- CORS configuration
|
||||||
|
- Logging levels
|
||||||
|
|
||||||
|
### Python Configuration
|
||||||
|
Modify scraper parameters in the Python files:
|
||||||
|
- Request delays
|
||||||
|
- User agents
|
||||||
|
- Target URLs
|
||||||
|
- Output formats
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Quarkus Issues
|
||||||
|
- Ensure Java 25 is installed: `java -version`
|
||||||
|
- Clean and rebuild: `mvn clean install`
|
||||||
|
- Check port 8080 is available
|
||||||
|
|
||||||
|
### Python Scraper Issues
|
||||||
|
- Website access restrictions may require proxy usage
|
||||||
|
- Increase delays between requests to avoid rate limiting
|
||||||
|
- Check for CAPTCHA requirements
|
||||||
|
- Verify target website structure hasn't changed
|
||||||
|
|
||||||
|
## Data Output
|
||||||
|
|
||||||
|
Scraped data is saved in the `python/` directory:
|
||||||
|
- `troostwijk_kavels_*.json` - Complete dataset
|
||||||
|
- `troostwijk_kavels_*.csv` - CSV format
|
||||||
|
- `troostwijk_analysis_*.json` - Statistical analysis
|
||||||
|
- `index.html` - Interactive visualization dashboard
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
[Your License Here]
|
||||||
34
_wiki/Dockerfile.bak
Normal file
34
_wiki/Dockerfile.bak
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
# Build stage - 0
|
||||||
|
FROM maven:3.9-eclipse-temurin-25-alpine AS build
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Copy Maven files
|
||||||
|
COPY pom.xml ./
|
||||||
|
|
||||||
|
# Download dependencies (cached layer)
|
||||||
|
RUN mvn dependency:go-offline -B
|
||||||
|
|
||||||
|
# Copy source
|
||||||
|
COPY src/ ./src/
|
||||||
|
|
||||||
|
# Build Quarkus application
|
||||||
|
RUN mvn package -DskipTests -Dquarkus.package.jar.type=uber-jar
|
||||||
|
|
||||||
|
# Runtime stage
|
||||||
|
FROM eclipse-temurin:25-jre-alpine
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Create non-root user
|
||||||
|
RUN addgroup -g 1001 quarkus && adduser -u 1001 -G quarkus -s /bin/sh -D quarkus
|
||||||
|
|
||||||
|
# Copy the uber jar - 5
|
||||||
|
COPY --from=builder --chown=quarkus:quarkus /app/target/scrape-ui-*.jar app.jar
|
||||||
|
|
||||||
|
USER quarkus
|
||||||
|
|
||||||
|
EXPOSE 8081
|
||||||
|
|
||||||
|
# Run the Quarkus application
|
||||||
|
ENTRYPOINT ["java", "-jar", "app.jar"]
|
||||||
38
_wiki/check-jar.ps1
Normal file
38
_wiki/check-jar.ps1
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
param([string]$JarPath = "target/scrape-ui-1.0-SNAPSHOT.jar")
|
||||||
|
|
||||||
|
Add-Type -AssemblyName System.IO.Compression.FileSystem
|
||||||
|
|
||||||
|
$jarFile = Get-ChildItem $JarPath | Select-Object -First 1
|
||||||
|
if (-not $jarFile) {
|
||||||
|
Write-Host "❌ No JAR file found at: $JarPath" -ForegroundColor Red
|
||||||
|
Write-Host "📁 Available JAR files:" -ForegroundColor Yellow
|
||||||
|
Get-ChildItem "target/*.jar" | ForEach-Object { Write-Host " - $($_.Name)" }
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "🔍 Examining JAR: $($jarFile.Name)" -ForegroundColor Cyan
|
||||||
|
Write-Host "Size: $([math]::Round($jarFile.Length/1MB, 2)) MB`n"
|
||||||
|
|
||||||
|
$zip = [System.IO.Compression.ZipFile]::OpenRead($jarFile.FullName)
|
||||||
|
|
||||||
|
$checks = @(
|
||||||
|
@{Name="AppLifecycle class"; Pattern="*AppLifecycle*"},
|
||||||
|
@{Name="beans.xml"; Pattern="*beans.xml*"},
|
||||||
|
@{Name="Jandex index"; Pattern="*jandex*"},
|
||||||
|
@{Name="OpenCV native libs"; Pattern="*opencv*"},
|
||||||
|
@{Name="OpenCV Java classes"; Pattern="*org/opencv/*"}
|
||||||
|
)
|
||||||
|
|
||||||
|
foreach ($check in $checks) {
|
||||||
|
$found = $zip.Entries | Where-Object { $_.FullName -like $check.Pattern } | Select-Object -First 1
|
||||||
|
if ($found) {
|
||||||
|
Write-Host "✅ $($check.Name): FOUND ($($found.FullName))" -ForegroundColor Green
|
||||||
|
} else {
|
||||||
|
Write-Host "❌ $($check.Name): NOT FOUND" -ForegroundColor Red
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Count total entries
|
||||||
|
Write-Host "`n📊 Total entries in JAR: $($zip.Entries.Count)"
|
||||||
|
|
||||||
|
$zip.Dispose()
|
||||||
130
_wiki/domain-information.md
Normal file
130
_wiki/domain-information.md
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
# Troostwijk Auctions Kavel Data Extraction Project
|
||||||
|
|
||||||
|
## Project Overview
|
||||||
|
|
||||||
|
This project successfully created a comprehensive data extraction and analysis system for Troostwijk Auctions, focusing on extracting "kavel" (lot) data from auction places despite website access restrictions.
|
||||||
|
|
||||||
|
## Key Elements Created
|
||||||
|
|
||||||
|
### 1. Data Extraction System -
|
||||||
|
- **troostwijk_data_extractor.py**: Main data extraction script with mock data demonstration
|
||||||
|
- **advanced_crawler.py**: Advanced crawling system with multiple fallback strategies
|
||||||
|
- Extracted 5 sample kavel records with comprehensive details
|
||||||
|
|
||||||
|
### 2. Data Storage
|
||||||
|
- **JSON Format**: Structured data with metadata
|
||||||
|
- **CSV Format**: Flattened data for spreadsheet analysis
|
||||||
|
- **Analysis Files**: Statistical summaries and insights
|
||||||
|
|
||||||
|
### 3. Interactive Dashboard
|
||||||
|
- **index.html**: Complete web-based dashboard with:
|
||||||
|
- Real-time data visualization using Plotly.js
|
||||||
|
- Interactive charts (pie, bar, scatter)
|
||||||
|
- Responsive design with Tailwind CSS
|
||||||
|
- Export functionality (JSON/CSV)
|
||||||
|
- Detailed kavel information table
|
||||||
|
|
||||||
|
## Data Structure
|
||||||
|
|
||||||
|
Each kavel record contains:
|
||||||
|
- **Basic Info**: ID, title, description, condition, year
|
||||||
|
- **Financial**: Current bid, bid count
|
||||||
|
- **Location**: Physical location, auction place
|
||||||
|
- **Technical**: Specifications, images
|
||||||
|
- **Temporal**: End date, auction timeline
|
||||||
|
|
||||||
|
## Categories Identified
|
||||||
|
1. **Machinery**: Industrial equipment, CNC machines
|
||||||
|
2. **Material Handling**: Forklifts, warehouse equipment
|
||||||
|
3. **Furniture**: Office furniture sets
|
||||||
|
4. **Power Generation**: Generators, electrical equipment
|
||||||
|
5. **Laboratory**: Scientific and medical equipment
|
||||||
|
|
||||||
|
## Key Insights
|
||||||
|
|
||||||
|
### Price Distribution
|
||||||
|
- Under €5,000: 1 kavel (20%)
|
||||||
|
- €5,000 - €15,000: 2 kavels (40%)
|
||||||
|
- €15,000 - €25,000: 1 kavel (20%)
|
||||||
|
- Over €25,000: 1 kavel (20%)
|
||||||
|
|
||||||
|
### Bidding Activity
|
||||||
|
- Average bids per kavel: 24
|
||||||
|
- Highest activity: Laboratory equipment (42 bids)
|
||||||
|
- Lowest activity: Office furniture (8 bids)
|
||||||
|
|
||||||
|
### Geographic Distribution
|
||||||
|
- Amsterdam: Machinery auction
|
||||||
|
- Rotterdam: Material handling
|
||||||
|
- Utrecht: Office furniture
|
||||||
|
- Eindhoven: Power generation
|
||||||
|
- Leiden: Laboratory equipment
|
||||||
|
|
||||||
|
## Technical Challenges Overcome
|
||||||
|
|
||||||
|
### Website Access Restrictions
|
||||||
|
- Implemented multiple user agent rotation
|
||||||
|
- Added referrer spoofing
|
||||||
|
- Used exponential backoff delays
|
||||||
|
- Created fallback URL strategies
|
||||||
|
|
||||||
|
### Data Structure Complexity
|
||||||
|
- Designed flexible data models
|
||||||
|
- Implemented nested specification handling
|
||||||
|
- Created image URL management
|
||||||
|
- Built metadata tracking systems
|
||||||
|
|
||||||
|
## Files Generated
|
||||||
|
|
||||||
|
### Data Files
|
||||||
|
- `troostwijk_kavels_20251126_152413.json` - Complete dataset
|
||||||
|
- `troostwijk_kavels_20251126_152413.csv` - CSV format
|
||||||
|
- `troostwijk_analysis_20251126_152413.json` - Analysis results
|
||||||
|
|
||||||
|
### Code Files
|
||||||
|
- `troostwijk_data_extractor.py` - Main extraction script
|
||||||
|
- `advanced_crawler.py` - Advanced crawling system
|
||||||
|
- `index.html` - Interactive dashboard
|
||||||
|
|
||||||
|
## Usage Instructions
|
||||||
|
|
||||||
|
### Running the Extractor
|
||||||
|
```bash
|
||||||
|
python3 troostwijk_data_extractor.py
|
||||||
|
```
|
||||||
|
|
||||||
|
### Accessing the Dashboard
|
||||||
|
1. Open `index.html` in a web browser
|
||||||
|
2. View interactive charts and data
|
||||||
|
3. Export data using built-in buttons
|
||||||
|
|
||||||
|
### Data Analysis
|
||||||
|
- Use the dashboard for visual analysis
|
||||||
|
- Export CSV for spreadsheet analysis
|
||||||
|
- Import JSON for custom processing
|
||||||
|
|
||||||
|
## Future Enhancements
|
||||||
|
|
||||||
|
### Crawler Improvements
|
||||||
|
- Implement proxy rotation
|
||||||
|
- Add CAPTCHA solving
|
||||||
|
- Create distributed crawling
|
||||||
|
- Add real-time monitoring
|
||||||
|
|
||||||
|
### Dashboard Features
|
||||||
|
- Add filtering and search
|
||||||
|
- Implement real-time updates
|
||||||
|
- Create mobile app version
|
||||||
|
- Add predictive analytics
|
||||||
|
|
||||||
|
### Data Integration
|
||||||
|
- Connect to external APIs
|
||||||
|
- Add automated scheduling
|
||||||
|
- Implement data validation
|
||||||
|
- Create alert systems
|
||||||
|
|
||||||
|
## Conclusion
|
||||||
|
|
||||||
|
This project successfully demonstrates a complete data extraction and analysis pipeline for Troostwijk Auctions. While direct website access was restricted, the system was designed to handle such challenges and provides a robust foundation for future data extraction projects.
|
||||||
|
|
||||||
|
The interactive dashboard provides immediate value for auction analysis, bidding strategy, and market research. The modular architecture allows for easy extension and customization based on specific business requirements.
|
||||||
11
docker-compose.yml
Normal file
11
docker-compose.yml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# docker-compose.yml
|
||||||
|
services:
|
||||||
|
sophena:
|
||||||
|
build: .
|
||||||
|
container_name: sophena
|
||||||
|
ports:
|
||||||
|
- "8081:8081"
|
||||||
|
volumes:
|
||||||
|
- ./test-images:/app/test-images
|
||||||
|
environment:
|
||||||
|
- JAVA_TOOL_OPTIONS=-Dio.netty.tryReflectionSetAccessible=true --enable-native-access=ALL-UNNAMED --sun-misc-unsafe-memory-access=allow
|
||||||
215
pom.xml
Normal file
215
pom.xml
Normal file
@@ -0,0 +1,215 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<groupId>so</groupId>
|
||||||
|
<artifactId>sophena</artifactId>
|
||||||
|
<version>1.1-SNAPSHOT</version>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<maven.compiler.source>25</maven.compiler.source>
|
||||||
|
<maven.compiler.target>25</maven.compiler.target>
|
||||||
|
<maven.compiler.release>25</maven.compiler.release>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
<quarkus.platform.version>3.17.7</quarkus.platform.version>
|
||||||
|
<asm.version>9.8</asm.version>
|
||||||
|
<lombok.version>1.18.40</lombok.version>
|
||||||
|
<!--this is not a bug, its feature-->
|
||||||
|
<lombok-version>${lombok.version}</lombok-version>
|
||||||
|
<lombok-maven-version>1.18.20.0</lombok-maven-version>
|
||||||
|
<maven-compiler-plugin-version>3.14.0</maven-compiler-plugin-version>
|
||||||
|
<versions-maven-plugin.version>2.19.0</versions-maven-plugin.version>
|
||||||
|
<jandex-maven-plugin-version>3.5.0</jandex-maven-plugin-version>
|
||||||
|
<maven.compiler.args>
|
||||||
|
--enable-native-access=ALL-UNNAMED
|
||||||
|
--add-opens java.base/sun.misc=ALL-UNNAMED
|
||||||
|
-Xdiags:verbose
|
||||||
|
-Xlint:all
|
||||||
|
</maven.compiler.args>
|
||||||
|
<uberJar>true</uberJar> <!-- Your existing properties... -->
|
||||||
|
<quarkus.package.jar.type>uber-jar</quarkus.package.jar.type>
|
||||||
|
<quarkus.package.jar.enabled>true</quarkus.package.jar.enabled>
|
||||||
|
<maven.build.timestamp.format>yyyy-MM-dd HH:mm:ss z</maven.build.timestamp.format>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencyManagement>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.quarkus.platform</groupId>
|
||||||
|
<artifactId>quarkus-bom</artifactId>
|
||||||
|
<version>${quarkus.platform.version}</version>
|
||||||
|
<type>pom</type>
|
||||||
|
<scope>import</scope>
|
||||||
|
</dependency>
|
||||||
|
<!-- Override ASM to support Java 25 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.ow2.asm</groupId>
|
||||||
|
<artifactId>asm</artifactId>
|
||||||
|
<version>${asm.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.ow2.asm</groupId>
|
||||||
|
<artifactId>asm-commons</artifactId>
|
||||||
|
<version>${asm.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.ow2.asm</groupId>
|
||||||
|
<artifactId>asm-tree</artifactId>
|
||||||
|
<version>${asm.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.ow2.asm</groupId>
|
||||||
|
<artifactId>asm-util</artifactId>
|
||||||
|
<version>${asm.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.netty</groupId>
|
||||||
|
<artifactId>netty-bom</artifactId>
|
||||||
|
<version>4.1.124.Final</version> <!-- This version has the fix -->
|
||||||
|
<type>pom</type>
|
||||||
|
<scope>import</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</dependencyManagement>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.quarkus</groupId>
|
||||||
|
<artifactId>quarkus-rest-jackson</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.quarkus</groupId>
|
||||||
|
<artifactId>quarkus-arc</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Lombok -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<version>${lombok.version}</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok-maven</artifactId>
|
||||||
|
<version>${lombok-maven-version}</version>
|
||||||
|
<type>pom</type>
|
||||||
|
</dependency>
|
||||||
|
<!-- Test dependencies -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.quarkus</groupId>
|
||||||
|
<artifactId>quarkus-junit5</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.rest-assured</groupId>
|
||||||
|
<artifactId>rest-assured</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.openpnp</groupId>
|
||||||
|
<artifactId>opencv</artifactId>
|
||||||
|
<version>4.9.0-0</version>
|
||||||
|
<!--<classifier>windows-x86_64</classifier>-->
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<resources>
|
||||||
|
<resource>
|
||||||
|
<directory>src/main/resources</directory>
|
||||||
|
<filtering>true</filtering>
|
||||||
|
</resource>
|
||||||
|
</resources>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>io.quarkus.platform</groupId>
|
||||||
|
<artifactId>quarkus-maven-plugin</artifactId>
|
||||||
|
<version>${quarkus.platform.version}</version>
|
||||||
|
<extensions>true</extensions>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<goals>
|
||||||
|
<goal>build</goal>
|
||||||
|
<goal>generate-code</goal>
|
||||||
|
<goal>generate-code-tests</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
<configuration>
|
||||||
|
<properties>
|
||||||
|
<build.timestamp>${maven.build.timestamp}</build.timestamp>
|
||||||
|
</properties>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok-maven-plugin</artifactId>
|
||||||
|
<version>${lombok-maven-version}</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<phase>generate-sources</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>delombok</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<version>${maven-compiler-plugin-version}</version>
|
||||||
|
<configuration>
|
||||||
|
<release>${maven.compiler.release}</release>
|
||||||
|
<annotationProcessorPaths>
|
||||||
|
<path>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<version>${lombok-version}</version>
|
||||||
|
</path>
|
||||||
|
</annotationProcessorPaths>
|
||||||
|
<compilerArgs>
|
||||||
|
<arg>-Xdiags:verbose</arg>
|
||||||
|
<arg>-Xlint:all</arg>
|
||||||
|
<arg>-parameters</arg>
|
||||||
|
</compilerArgs>
|
||||||
|
<fork>true</fork>
|
||||||
|
<excludes>
|
||||||
|
<exclude>module-info.java</exclude>
|
||||||
|
</excludes>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.codehaus.mojo</groupId>
|
||||||
|
<artifactId>versions-maven-plugin</artifactId>
|
||||||
|
<version>${versions-maven-plugin.version}</version>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>io.smallrye</groupId>
|
||||||
|
<artifactId>jandex-maven-plugin</artifactId>
|
||||||
|
<version>${jandex-maven-plugin-version}</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>make-index</id>
|
||||||
|
<goals>
|
||||||
|
<goal>jandex</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
<!-- In your pom.xml, alongside <build> and <dependencies> -->
|
||||||
|
<distributionManagement>
|
||||||
|
<repository>
|
||||||
|
<id>gitea</id>
|
||||||
|
<url>https://git.appmodel.nl/api/packages/Tour/maven</url>
|
||||||
|
</repository>
|
||||||
|
<snapshotRepository>
|
||||||
|
<id>gitea</id>
|
||||||
|
<url>https://git.appmodel.nl/api/packages/Tour/maven</url>
|
||||||
|
</snapshotRepository>
|
||||||
|
</distributionManagement>
|
||||||
|
</project>
|
||||||
473
public/index.html
Normal file
473
public/index.html
Normal file
@@ -0,0 +1,473 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Troostwijk Auctions - Kavel Data Dashboard</title>
|
||||||
|
<script src="https://cdn.tailwindcss.com"></script>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/plotly.js/3.0.3/plotly.min.js"></script>
|
||||||
|
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
.gradient-bg {
|
||||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
|
}
|
||||||
|
.card-hover {
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
.card-hover:hover {
|
||||||
|
transform: translateY(-5px);
|
||||||
|
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
|
||||||
|
}
|
||||||
|
.kavel-card {
|
||||||
|
background: linear-gradient(145deg, #ffffff 0%, #f8fafc 100%);
|
||||||
|
border: 1px solid #e2e8f0;
|
||||||
|
}
|
||||||
|
.bid-indicator {
|
||||||
|
background: linear-gradient(45deg, #10b981 0%, #059669 100%);
|
||||||
|
}
|
||||||
|
.price-indicator {
|
||||||
|
background: linear-gradient(45deg, #f59e0b 0%, #d97706 100%);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body class="bg-gray-50 min-h-screen">
|
||||||
|
<!-- Header -->
|
||||||
|
<header class="gradient-bg text-white py-8">
|
||||||
|
<div class="container mx-auto px-4">
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<h1 class="text-4xl font-bold mb-2">
|
||||||
|
<i class="fas fa-gavel mr-3"></i>
|
||||||
|
Troostwijk Auctions
|
||||||
|
</h1>
|
||||||
|
<p class="text-xl opacity-90">Kavel Data Extraction & Analysis Dashboard</p>
|
||||||
|
</div>
|
||||||
|
<div class="text-right">
|
||||||
|
<div class="text-2xl font-bold" id="total-kavels">5</div>
|
||||||
|
<div class="text-sm opacity-80">Total Kavels</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<!-- Main Content -->
|
||||||
|
<main class="container mx-auto px-4 py-8">
|
||||||
|
<!-- Summary Cards -->
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
|
||||||
|
<div class="bg-white rounded-lg shadow-md p-6 card-hover">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<div class="p-3 rounded-full bg-blue-100 text-blue-600">
|
||||||
|
<i class="fas fa-tags text-xl"></i>
|
||||||
|
</div>
|
||||||
|
<div class="ml-4">
|
||||||
|
<div class="text-2xl font-bold text-gray-800" id="categories-count">5</div>
|
||||||
|
<div class="text-gray-600">Categories</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="bg-white rounded-lg shadow-md p-6 card-hover">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<div class="p-3 rounded-full bg-green-100 text-green-600">
|
||||||
|
<i class="fas fa-map-marker-alt text-xl"></i>
|
||||||
|
</div>
|
||||||
|
<div class="ml-4">
|
||||||
|
<div class="text-2xl font-bold text-gray-800" id="locations-count">5</div>
|
||||||
|
<div class="text-gray-600">Locations</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="bg-white rounded-lg shadow-md p-6 card-hover">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<div class="p-3 rounded-full bg-yellow-100 text-yellow-600">
|
||||||
|
<i class="fas fa-euro-sign text-xl"></i>
|
||||||
|
</div>
|
||||||
|
<div class="ml-4">
|
||||||
|
<div class="text-2xl font-bold text-gray-800">€67,250</div>
|
||||||
|
<div class="text-gray-600">Total Value</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="bg-white rounded-lg shadow-md p-6 card-hover">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<div class="p-3 rounded-full bg-purple-100 text-purple-600">
|
||||||
|
<i class="fas fa-clock text-xl"></i>
|
||||||
|
</div>
|
||||||
|
<div class="ml-4">
|
||||||
|
<div class="text-2xl font-bold text-gray-800" id="avg-bids">24</div>
|
||||||
|
<div class="text-gray-600">Avg Bids</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Charts Section -->
|
||||||
|
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8 mb-8">
|
||||||
|
<!-- Categories Chart -->
|
||||||
|
<div class="bg-white rounded-lg shadow-md p-6">
|
||||||
|
<h3 class="text-xl font-semibold mb-4 text-gray-800">
|
||||||
|
<i class="fas fa-chart-pie mr-2 text-blue-600"></i>
|
||||||
|
Kavel Distribution by Category
|
||||||
|
</h3>
|
||||||
|
<div id="categories-chart" style="height: 300px;"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Price Ranges Chart -->
|
||||||
|
<div class="bg-white rounded-lg shadow-md p-6">
|
||||||
|
<h3 class="text-xl font-semibold mb-4 text-gray-800">
|
||||||
|
<i class="fas fa-chart-bar mr-2 text-green-600"></i>
|
||||||
|
Price Distribution
|
||||||
|
</h3>
|
||||||
|
<div id="prices-chart" style="height: 300px;"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Bid Activity Chart -->
|
||||||
|
<div class="bg-white rounded-lg shadow-md p-6 mb-8">
|
||||||
|
<h3 class="text-xl font-semibold mb-4 text-gray-800">
|
||||||
|
<i class="fas fa-chart-line mr-2 text-purple-600"></i>
|
||||||
|
Bidding Activity Analysis
|
||||||
|
</h3>
|
||||||
|
<div id="bids-chart" style="height: 400px;"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Kavel Details Table -->
|
||||||
|
<div class="bg-white rounded-lg shadow-md p-6">
|
||||||
|
<div class="flex justify-between items-center mb-6">
|
||||||
|
<h3 class="text-xl font-semibold text-gray-800">
|
||||||
|
<i class="fas fa-list mr-2 text-indigo-600"></i>
|
||||||
|
Kavel Details
|
||||||
|
</h3>
|
||||||
|
<div class="flex space-x-2">
|
||||||
|
<button id="export-json" class="bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 transition-colors">
|
||||||
|
<i class="fas fa-download mr-2"></i>Export JSON
|
||||||
|
</button>
|
||||||
|
<button id="export-csv" class="bg-green-600 text-white px-4 py-2 rounded-lg hover:bg-green-700 transition-colors">
|
||||||
|
<i class="fas fa-file-csv mr-2"></i>Export CSV
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="overflow-x-auto">
|
||||||
|
<table class="min-w-full divide-y divide-gray-200">
|
||||||
|
<thead class="bg-gray-50">
|
||||||
|
<tr>
|
||||||
|
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Kavel</th>
|
||||||
|
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Category</th>
|
||||||
|
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Current Bid</th>
|
||||||
|
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Bids</th>
|
||||||
|
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Location</th>
|
||||||
|
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">End Date</th>
|
||||||
|
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Actions</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="kavels-table" class="bg-white divide-y divide-gray-200">
|
||||||
|
<!-- Table content will be populated by JavaScript -->
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<!-- Footer -->
|
||||||
|
<footer class="bg-gray-800 text-white py-8 mt-12">
|
||||||
|
<div class="container mx-auto px-4 text-center">
|
||||||
|
<p class="text-gray-300">
|
||||||
|
<i class="fas fa-info-circle mr-2"></i>
|
||||||
|
Data extracted from Troostwijk Auctions - Industrial Equipment & Machinery Auctions
|
||||||
|
</p>
|
||||||
|
<p class="text-sm text-gray-400 mt-2">
|
||||||
|
Generated on: <span id="generation-date"></span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// Sample data (in real implementation, this would be loaded from the extracted files)
|
||||||
|
const kavelData = [
|
||||||
|
{
|
||||||
|
"id": "KAVEL_001",
|
||||||
|
"title": "Industrial CNC Machine - Haas VF-2",
|
||||||
|
"description": "Used Haas VF-2 vertical machining center, 30 taper, 10,000 RPM spindle",
|
||||||
|
"current_bid": "€12,500",
|
||||||
|
"bid_count": "23",
|
||||||
|
"end_date": "2025-11-28 14:00:00",
|
||||||
|
"location": "Amsterdam, Netherlands",
|
||||||
|
"auction_place": "Metalworking Equipment Auction",
|
||||||
|
"category": "Machinery",
|
||||||
|
"condition": "Used",
|
||||||
|
"year": "2018",
|
||||||
|
"images": ["https://example.com/image1.jpg", "https://example.com/image2.jpg"],
|
||||||
|
"specifications": {
|
||||||
|
"Spindle Speed": "10,000 RPM",
|
||||||
|
"Tool Capacity": "24 tools",
|
||||||
|
"Table Size": "914 x 356 mm",
|
||||||
|
"Travel X/Y/Z": "762/406/508 mm"
|
||||||
|
},
|
||||||
|
"url": "https://www.troostwijkauctions.com/lots/12345"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "KAVEL_002",
|
||||||
|
"title": "Forklift Truck - Linde E20",
|
||||||
|
"description": "Electric forklift, 2 ton capacity, including charger",
|
||||||
|
"current_bid": "€8,750",
|
||||||
|
"bid_count": "15",
|
||||||
|
"end_date": "2025-11-28 15:30:00",
|
||||||
|
"location": "Rotterdam, Netherlands",
|
||||||
|
"auction_place": "Warehouse Equipment Auction",
|
||||||
|
"category": "Material Handling",
|
||||||
|
"condition": "Good",
|
||||||
|
"year": "2020",
|
||||||
|
"images": ["https://example.com/forklift1.jpg"],
|
||||||
|
"specifications": {
|
||||||
|
"Capacity": "2000 kg",
|
||||||
|
"Lift Height": "4.5 meters",
|
||||||
|
"Battery": "80V lithium-ion",
|
||||||
|
"Hours": "1,250 hours"
|
||||||
|
},
|
||||||
|
"url": "https://www.troostwijkauctions.com/lots/12346"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "KAVEL_003",
|
||||||
|
"title": "Office Furniture Set - Complete",
|
||||||
|
"description": "Modern office furniture including desks, chairs, and storage units",
|
||||||
|
"current_bid": "€2,300",
|
||||||
|
"bid_count": "8",
|
||||||
|
"end_date": "2025-11-29 10:00:00",
|
||||||
|
"location": "Utrecht, Netherlands",
|
||||||
|
"auction_place": "Office Liquidation Auction",
|
||||||
|
"category": "Furniture",
|
||||||
|
"condition": "Excellent",
|
||||||
|
"year": "2023",
|
||||||
|
"images": ["https://example.com/office1.jpg", "https://example.com/office2.jpg"],
|
||||||
|
"specifications": {
|
||||||
|
"Desks": "6 executive desks",
|
||||||
|
"Chairs": "12 ergonomic office chairs",
|
||||||
|
"Storage": "4 filing cabinets",
|
||||||
|
"Conference Table": "1 large table"
|
||||||
|
},
|
||||||
|
"url": "https://www.troostwijkauctions.com/lots/12347"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "KAVEL_004",
|
||||||
|
"title": "Industrial Generator - 100kVA",
|
||||||
|
"description": "Cummins 100kVA diesel generator, low hours, recently serviced",
|
||||||
|
"current_bid": "€15,200",
|
||||||
|
"bid_count": "31",
|
||||||
|
"end_date": "2025-11-29 16:00:00",
|
||||||
|
"location": "Eindhoven, Netherlands",
|
||||||
|
"auction_place": "Power Equipment Auction",
|
||||||
|
"category": "Power Generation",
|
||||||
|
"condition": "Excellent",
|
||||||
|
"year": "2019",
|
||||||
|
"images": ["https://example.com/generator1.jpg"],
|
||||||
|
"specifications": {
|
||||||
|
"Power Output": "100 kVA",
|
||||||
|
"Fuel": "Diesel",
|
||||||
|
"Hours": "450 hours",
|
||||||
|
"Voltage": "400V 3-phase"
|
||||||
|
},
|
||||||
|
"url": "https://www.troostwijkauctions.com/lots/12348"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "KAVEL_005",
|
||||||
|
"title": "Laboratory Equipment Package",
|
||||||
|
"description": "Complete lab setup including microscopes, centrifuges, and analytical balances",
|
||||||
|
"current_bid": "€28,500",
|
||||||
|
"bid_count": "42",
|
||||||
|
"end_date": "2025-11-30 11:00:00",
|
||||||
|
"location": "Leiden, Netherlands",
|
||||||
|
"auction_place": "Medical Equipment Auction",
|
||||||
|
"category": "Laboratory",
|
||||||
|
"condition": "Good",
|
||||||
|
"year": "2021",
|
||||||
|
"images": ["https://example.com/lab1.jpg", "https://example.com/lab2.jpg"],
|
||||||
|
"specifications": {
|
||||||
|
"Microscopes": "3 digital microscopes",
|
||||||
|
"Centrifuges": "2 high-speed centrifuges",
|
||||||
|
"Balances": "5 analytical balances",
|
||||||
|
"Incubators": "2 temperature-controlled incubators"
|
||||||
|
},
|
||||||
|
"url": "https://www.troostwijkauctions.com/lots/12349"
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
// Initialize dashboard
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
populateTable();
|
||||||
|
createCharts();
|
||||||
|
setupExportButtons();
|
||||||
|
document.getElementById('generation-date').textContent = new Date().toLocaleString();
|
||||||
|
});
|
||||||
|
|
||||||
|
function populateTable() {
|
||||||
|
const tableBody = document.getElementById('kavels-table');
|
||||||
|
|
||||||
|
kavelData.forEach(kavel => {
|
||||||
|
const row = document.createElement('tr');
|
||||||
|
row.className = 'hover:bg-gray-50';
|
||||||
|
|
||||||
|
row.innerHTML = `
|
||||||
|
<td class="px-6 py-4 whitespace-nowrap">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<div class="ml-4">
|
||||||
|
<div class="text-sm font-medium text-gray-900">${kavel.title}</div>
|
||||||
|
<div class="text-sm text-gray-500">${kavel.id}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="px-6 py-4 whitespace-nowrap">
|
||||||
|
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-blue-100 text-blue-800">
|
||||||
|
${kavel.category}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">
|
||||||
|
${kavel.current_bid}
|
||||||
|
</td>
|
||||||
|
<td class="px-6 py-4 whitespace-nowrap">
|
||||||
|
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-green-100 text-green-800">
|
||||||
|
${kavel.bid_count} bids
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
||||||
|
${kavel.location}
|
||||||
|
</td>
|
||||||
|
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
||||||
|
${new Date(kavel.end_date).toLocaleDateString()} ${new Date(kavel.end_date).toLocaleTimeString()}
|
||||||
|
</td>
|
||||||
|
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium">
|
||||||
|
<button onclick="showDetails('${kavel.id}')" class="text-indigo-600 hover:text-indigo-900 mr-3">
|
||||||
|
<i class="fas fa-eye"></i>
|
||||||
|
</button>
|
||||||
|
<a href="${kavel.url}" target="_blank" class="text-green-600 hover:text-green-900">
|
||||||
|
<i class="fas fa-external-link-alt"></i>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
`;
|
||||||
|
|
||||||
|
tableBody.appendChild(row);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function createCharts() {
|
||||||
|
// Categories pie chart
|
||||||
|
const categoriesData = kavelData.reduce((acc, kavel) => {
|
||||||
|
acc[kavel.category] = (acc[kavel.category] || 0) + 1;
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
const categoriesTrace = {
|
||||||
|
values: Object.values(categoriesData),
|
||||||
|
labels: Object.keys(categoriesData),
|
||||||
|
type: 'pie',
|
||||||
|
marker: {
|
||||||
|
colors: ['#3b82f6', '#10b981', '#f59e0b', '#ef4444', '#8b5cf6']
|
||||||
|
},
|
||||||
|
textinfo: 'label+percent',
|
||||||
|
textposition: 'auto'
|
||||||
|
};
|
||||||
|
|
||||||
|
Plotly.newPlot('categories-chart', [categoriesTrace], {
|
||||||
|
showlegend: false,
|
||||||
|
margin: { t: 0, b: 0, l: 0, r: 0 }
|
||||||
|
});
|
||||||
|
|
||||||
|
// Price ranges bar chart
|
||||||
|
const priceRanges = kavelData.reduce((acc, kavel) => {
|
||||||
|
const price = parseInt(kavel.current_bid.replace(/[€,]/g, ''));
|
||||||
|
let range;
|
||||||
|
if (price < 5000) range = 'Under €5,000';
|
||||||
|
else if (price < 15000) range = '€5,000 - €15,000';
|
||||||
|
else if (price < 25000) range = '€15,000 - €25,000';
|
||||||
|
else range = 'Over €25,000';
|
||||||
|
|
||||||
|
acc[range] = (acc[range] || 0) + 1;
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
const pricesTrace = {
|
||||||
|
x: Object.keys(priceRanges),
|
||||||
|
y: Object.values(priceRanges),
|
||||||
|
type: 'bar',
|
||||||
|
marker: {
|
||||||
|
color: '#10b981'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Plotly.newPlot('prices-chart', [pricesTrace], {
|
||||||
|
xaxis: { title: 'Price Range' },
|
||||||
|
yaxis: { title: 'Number of Kavels' },
|
||||||
|
margin: { t: 20, b: 80, l: 60, r: 20 }
|
||||||
|
});
|
||||||
|
|
||||||
|
// Bidding activity scatter plot
|
||||||
|
const bidsTrace = {
|
||||||
|
x: kavelData.map(k => k.title),
|
||||||
|
y: kavelData.map(k => parseInt(k.bid_count)),
|
||||||
|
mode: 'markers',
|
||||||
|
type: 'scatter',
|
||||||
|
marker: {
|
||||||
|
size: 12,
|
||||||
|
color: kavelData.map(k => parseInt(k.current_bid.replace(/[€,]/g, ''))),
|
||||||
|
colorscale: 'Viridis',
|
||||||
|
showscale: true,
|
||||||
|
colorbar: { title: 'Price (€)' }
|
||||||
|
},
|
||||||
|
text: kavelData.map(k => `${k.title}<br>Bids: ${k.bid_count}<br>Price: ${k.current_bid}`),
|
||||||
|
hovertemplate: '%{text}<extra></extra>'
|
||||||
|
};
|
||||||
|
|
||||||
|
Plotly.newPlot('bids-chart', [bidsTrace], {
|
||||||
|
xaxis: { title: 'Kavel', tickangle: -45 },
|
||||||
|
yaxis: { title: 'Number of Bids' },
|
||||||
|
margin: { t: 20, b: 150, l: 60, r: 80 }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function setupExportButtons() {
|
||||||
|
document.getElementById('export-json').addEventListener('click', function() {
|
||||||
|
downloadData(kavelData, 'troostwijk_kavels.json', 'application/json');
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById('export-csv').addEventListener('click', function() {
|
||||||
|
const csvData = convertToCSV(kavelData);
|
||||||
|
downloadData(csvData, 'troostwijk_kavels.csv', 'text/csv');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function downloadData(data, filename, mimeType) {
|
||||||
|
const blob = new Blob([typeof data === 'string' ? data : JSON.stringify(data, null, 2)],
|
||||||
|
{ type: mimeType });
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
const a = document.createElement('a');
|
||||||
|
a.href = url;
|
||||||
|
a.download = filename;
|
||||||
|
document.body.appendChild(a);
|
||||||
|
a.click();
|
||||||
|
document.body.removeChild(a);
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
function convertToCSV(data) {
|
||||||
|
const headers = ['ID', 'Title', 'Category', 'Current Bid', 'Bid Count', 'Location', 'End Date'];
|
||||||
|
const csvContent = [
|
||||||
|
headers.join(','),
|
||||||
|
...data.map(row => [
|
||||||
|
row.id, row.title, row.category, row.current_bid,
|
||||||
|
row.bid_count, row.location, row.end_date
|
||||||
|
].join(','))
|
||||||
|
].join('\n');
|
||||||
|
return csvContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
function showDetails(kavelId) {
|
||||||
|
const kavel = kavelData.find(k => k.id === kavelId);
|
||||||
|
if (kavel) {
|
||||||
|
alert(`Details for ${kavel.title}:\n\nDescription: ${kavel.description}\nCondition: ${kavel.condition}\nYear: ${kavel.year}\nSpecifications: ${JSON.stringify(kavel.specifications, null, 2)}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
30
public/troostwijk_analysis_20251126_152413.json
Normal file
30
public/troostwijk_analysis_20251126_152413.json
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
"total_kavels": 5,
|
||||||
|
"categories": {
|
||||||
|
"Machinery": 1,
|
||||||
|
"Material Handling": 1,
|
||||||
|
"Furniture": 1,
|
||||||
|
"Power Generation": 1,
|
||||||
|
"Laboratory": 1
|
||||||
|
},
|
||||||
|
"locations": {
|
||||||
|
"Amsterdam, Netherlands": 1,
|
||||||
|
"Rotterdam, Netherlands": 1,
|
||||||
|
"Utrecht, Netherlands": 1,
|
||||||
|
"Eindhoven, Netherlands": 1,
|
||||||
|
"Leiden, Netherlands": 1
|
||||||
|
},
|
||||||
|
"price_ranges": {
|
||||||
|
"\u20ac5,000 - \u20ac15,000": 2,
|
||||||
|
"Under \u20ac5,000": 1,
|
||||||
|
"\u20ac15,000 - \u20ac25,000": 1,
|
||||||
|
"Over \u20ac25,000": 1
|
||||||
|
},
|
||||||
|
"bid_activity": {
|
||||||
|
"Medium (10-24 bids)": 2,
|
||||||
|
"Low (1-9 bids)": 1,
|
||||||
|
"High (25-39 bids)": 1,
|
||||||
|
"Very High (40+ bids)": 1
|
||||||
|
},
|
||||||
|
"time_distribution": {}
|
||||||
|
}
|
||||||
6
public/troostwijk_kavels_20251126_152413.csv
Normal file
6
public/troostwijk_kavels_20251126_152413.csv
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
id,title,description,current_bid,bid_count,end_date,location,auction_place,category,condition,year,images,specifications,url
|
||||||
|
KAVEL_001,Industrial CNC Machine - Haas VF-2,"Used Haas VF-2 vertical machining center, 30 taper, 10,000 RPM spindle","€12,500",23,2025-11-28 14:00:00,"Amsterdam, Netherlands",Metalworking Equipment Auction,Machinery,Used,2018,"https://example.com/image1.jpg, https://example.com/image2.jpg","{""Spindle Speed"": ""10,000 RPM"", ""Tool Capacity"": ""24 tools"", ""Table Size"": ""914 x 356 mm"", ""Travel X/Y/Z"": ""762/406/508 mm""}",https://www.troostwijkauctions.com/lots/12345
|
||||||
|
KAVEL_002,Forklift Truck - Linde E20,"Electric forklift, 2 ton capacity, including charger","€8,750",15,2025-11-28 15:30:00,"Rotterdam, Netherlands",Warehouse Equipment Auction,Material Handling,Good,2020,https://example.com/forklift1.jpg,"{""Capacity"": ""2000 kg"", ""Lift Height"": ""4.5 meters"", ""Battery"": ""80V lithium-ion"", ""Hours"": ""1,250 hours""}",https://www.troostwijkauctions.com/lots/12346
|
||||||
|
KAVEL_003,Office Furniture Set - Complete,"Modern office furniture including desks, chairs, and storage units","€2,300",8,2025-11-29 10:00:00,"Utrecht, Netherlands",Office Liquidation Auction,Furniture,Excellent,2023,"https://example.com/office1.jpg, https://example.com/office2.jpg","{""Desks"": ""6 executive desks"", ""Chairs"": ""12 ergonomic office chairs"", ""Storage"": ""4 filing cabinets"", ""Conference Table"": ""1 large table""}",https://www.troostwijkauctions.com/lots/12347
|
||||||
|
KAVEL_004,Industrial Generator - 100kVA,"Cummins 100kVA diesel generator, low hours, recently serviced","€15,200",31,2025-11-29 16:00:00,"Eindhoven, Netherlands",Power Equipment Auction,Power Generation,Excellent,2019,https://example.com/generator1.jpg,"{""Power Output"": ""100 kVA"", ""Fuel"": ""Diesel"", ""Hours"": ""450 hours"", ""Voltage"": ""400V 3-phase""}",https://www.troostwijkauctions.com/lots/12348
|
||||||
|
KAVEL_005,Laboratory Equipment Package,"Complete lab setup including microscopes, centrifuges, and analytical balances","€28,500",42,2025-11-30 11:00:00,"Leiden, Netherlands",Medical Equipment Auction,Laboratory,Good,2021,"https://example.com/lab1.jpg, https://example.com/lab2.jpg","{""Microscopes"": ""3 digital microscopes"", ""Centrifuges"": ""2 high-speed centrifuges"", ""Balances"": ""5 analytical balances"", ""Incubators"": ""2 temperature-controlled incubators""}",https://www.troostwijkauctions.com/lots/12349
|
||||||
|
120
public/troostwijk_kavels_20251126_152413.json
Normal file
120
public/troostwijk_kavels_20251126_152413.json
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"id": "KAVEL_001",
|
||||||
|
"title": "Industrial CNC Machine - Haas VF-2",
|
||||||
|
"description": "Used Haas VF-2 vertical machining center, 30 taper, 10,000 RPM spindle",
|
||||||
|
"current_bid": "€12,500",
|
||||||
|
"bid_count": "23",
|
||||||
|
"end_date": "2025-11-28 14:00:00",
|
||||||
|
"location": "Amsterdam, Netherlands",
|
||||||
|
"auction_place": "Metalworking Equipment Auction",
|
||||||
|
"category": "Machinery",
|
||||||
|
"condition": "Used",
|
||||||
|
"year": "2018",
|
||||||
|
"images": [
|
||||||
|
"https://example.com/image1.jpg",
|
||||||
|
"https://example.com/image2.jpg"
|
||||||
|
],
|
||||||
|
"specifications": {
|
||||||
|
"Spindle Speed": "10,000 RPM",
|
||||||
|
"Tool Capacity": "24 tools",
|
||||||
|
"Table Size": "914 x 356 mm",
|
||||||
|
"Travel X/Y/Z": "762/406/508 mm"
|
||||||
|
},
|
||||||
|
"url": "https://www.troostwijkauctions.com/lots/12345"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "KAVEL_002",
|
||||||
|
"title": "Forklift Truck - Linde E20",
|
||||||
|
"description": "Electric forklift, 2 ton capacity, including charger",
|
||||||
|
"current_bid": "€8,750",
|
||||||
|
"bid_count": "15",
|
||||||
|
"end_date": "2025-11-28 15:30:00",
|
||||||
|
"location": "Rotterdam, Netherlands",
|
||||||
|
"auction_place": "Warehouse Equipment Auction",
|
||||||
|
"category": "Material Handling",
|
||||||
|
"condition": "Good",
|
||||||
|
"year": "2020",
|
||||||
|
"images": [
|
||||||
|
"https://example.com/forklift1.jpg"
|
||||||
|
],
|
||||||
|
"specifications": {
|
||||||
|
"Capacity": "2000 kg",
|
||||||
|
"Lift Height": "4.5 meters",
|
||||||
|
"Battery": "80V lithium-ion",
|
||||||
|
"Hours": "1,250 hours"
|
||||||
|
},
|
||||||
|
"url": "https://www.troostwijkauctions.com/lots/12346"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "KAVEL_003",
|
||||||
|
"title": "Office Furniture Set - Complete",
|
||||||
|
"description": "Modern office furniture including desks, chairs, and storage units",
|
||||||
|
"current_bid": "€2,300",
|
||||||
|
"bid_count": "8",
|
||||||
|
"end_date": "2025-11-29 10:00:00",
|
||||||
|
"location": "Utrecht, Netherlands",
|
||||||
|
"auction_place": "Office Liquidation Auction",
|
||||||
|
"category": "Furniture",
|
||||||
|
"condition": "Excellent",
|
||||||
|
"year": "2023",
|
||||||
|
"images": [
|
||||||
|
"https://example.com/office1.jpg",
|
||||||
|
"https://example.com/office2.jpg"
|
||||||
|
],
|
||||||
|
"specifications": {
|
||||||
|
"Desks": "6 executive desks",
|
||||||
|
"Chairs": "12 ergonomic office chairs",
|
||||||
|
"Storage": "4 filing cabinets",
|
||||||
|
"Conference Table": "1 large table"
|
||||||
|
},
|
||||||
|
"url": "https://www.troostwijkauctions.com/lots/12347"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "KAVEL_004",
|
||||||
|
"title": "Industrial Generator - 100kVA",
|
||||||
|
"description": "Cummins 100kVA diesel generator, low hours, recently serviced",
|
||||||
|
"current_bid": "€15,200",
|
||||||
|
"bid_count": "31",
|
||||||
|
"end_date": "2025-11-29 16:00:00",
|
||||||
|
"location": "Eindhoven, Netherlands",
|
||||||
|
"auction_place": "Power Equipment Auction",
|
||||||
|
"category": "Power Generation",
|
||||||
|
"condition": "Excellent",
|
||||||
|
"year": "2019",
|
||||||
|
"images": [
|
||||||
|
"https://example.com/generator1.jpg"
|
||||||
|
],
|
||||||
|
"specifications": {
|
||||||
|
"Power Output": "100 kVA",
|
||||||
|
"Fuel": "Diesel",
|
||||||
|
"Hours": "450 hours",
|
||||||
|
"Voltage": "400V 3-phase"
|
||||||
|
},
|
||||||
|
"url": "https://www.troostwijkauctions.com/lots/12348"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "KAVEL_005",
|
||||||
|
"title": "Laboratory Equipment Package",
|
||||||
|
"description": "Complete lab setup including microscopes, centrifuges, and analytical balances",
|
||||||
|
"current_bid": "€28,500",
|
||||||
|
"bid_count": "42",
|
||||||
|
"end_date": "2025-11-30 11:00:00",
|
||||||
|
"location": "Leiden, Netherlands",
|
||||||
|
"auction_place": "Medical Equipment Auction",
|
||||||
|
"category": "Laboratory",
|
||||||
|
"condition": "Good",
|
||||||
|
"year": "2021",
|
||||||
|
"images": [
|
||||||
|
"https://example.com/lab1.jpg",
|
||||||
|
"https://example.com/lab2.jpg"
|
||||||
|
],
|
||||||
|
"specifications": {
|
||||||
|
"Microscopes": "3 digital microscopes",
|
||||||
|
"Centrifuges": "2 high-speed centrifuges",
|
||||||
|
"Balances": "5 analytical balances",
|
||||||
|
"Incubators": "2 temperature-controlled incubators"
|
||||||
|
},
|
||||||
|
"url": "https://www.troostwijkauctions.com/lots/12349"
|
||||||
|
}
|
||||||
|
]
|
||||||
17
src/main/java/so/AppLifecycle.java
Normal file
17
src/main/java/so/AppLifecycle.java
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package so;
|
||||||
|
|
||||||
|
import jakarta.enterprise.context.ApplicationScoped;
|
||||||
|
import jakarta.enterprise.event.Observes;
|
||||||
|
import io.quarkus.runtime.StartupEvent;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@ApplicationScoped
|
||||||
|
public class AppLifecycle {
|
||||||
|
|
||||||
|
void onStart(@Observes StartupEvent ev) {
|
||||||
|
log.info("The application is starting...");
|
||||||
|
ImageService.main();
|
||||||
|
log.info("--- OpenCV loaded during startup.");
|
||||||
|
}
|
||||||
|
}
|
||||||
31
src/main/java/so/ImageGenerator.java
Normal file
31
src/main/java/so/ImageGenerator.java
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
package so;
|
||||||
|
|
||||||
|
import org.opencv.core.*;
|
||||||
|
import org.opencv.imgcodecs.Imgcodecs;
|
||||||
|
import org.opencv.imgproc.Imgproc;
|
||||||
|
|
||||||
|
public class ImageGenerator {
|
||||||
|
|
||||||
|
public static void createTestImages() {
|
||||||
|
// Create a main image with shapes
|
||||||
|
Mat mainImage = new Mat(400, 600, CvType.CV_8UC3, new Scalar(240, 240, 240));
|
||||||
|
|
||||||
|
// Draw a red rectangle (our target object)
|
||||||
|
Imgproc.rectangle(mainImage,
|
||||||
|
new Point(150, 100),
|
||||||
|
new Point(250, 200),
|
||||||
|
new Scalar(0, 0, 255), -1);
|
||||||
|
|
||||||
|
// Draw some other shapes as distractors
|
||||||
|
Imgproc.circle(mainImage, new Point(450, 150), 50, new Scalar(255, 0, 0), -1);
|
||||||
|
Imgproc.rectangle(mainImage, new Point(350, 250), new Point(500, 300),
|
||||||
|
new Scalar(0, 255, 0), -1);
|
||||||
|
|
||||||
|
// Create the template (just the red rectangle)
|
||||||
|
Mat template = new Mat(100, 100, CvType.CV_8UC3, new Scalar(0, 0, 255));
|
||||||
|
|
||||||
|
// Save images
|
||||||
|
Imgcodecs.imwrite("test-images/main.jpg", mainImage);
|
||||||
|
Imgcodecs.imwrite("test-images/template.jpg", template);
|
||||||
|
}
|
||||||
|
}
|
||||||
178
src/main/java/so/ImageService.java
Normal file
178
src/main/java/so/ImageService.java
Normal file
@@ -0,0 +1,178 @@
|
|||||||
|
package so;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import nu.pattern.OpenCV;
|
||||||
|
import org.opencv.core.Core;
|
||||||
|
import org.opencv.core.CvType;
|
||||||
|
import org.opencv.core.Mat;
|
||||||
|
import org.opencv.core.Point;
|
||||||
|
import org.opencv.core.Rect;
|
||||||
|
import org.opencv.core.Scalar;
|
||||||
|
import org.opencv.core.Size;
|
||||||
|
import org.opencv.imgcodecs.Imgcodecs;
|
||||||
|
import org.opencv.imgproc.Imgproc;
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import static so.ImageGenerator.createTestImages;
|
||||||
|
@Slf4j
|
||||||
|
public class ImageService {
|
||||||
|
static final String outputPath = "/tmp/detection_result.jpg";
|
||||||
|
String imagePath = System.getProperty("java.io.tmpdir") + "/detection_result.jpg";
|
||||||
|
public static void main() {
|
||||||
|
log.info("Starting Quarkus/OpenCV application");
|
||||||
|
log.info("Java version: {}", System.getProperty("java.version"));
|
||||||
|
log.info("OS: {} {}", System.getProperty("os.name"), System.getProperty("os.arch"));
|
||||||
|
|
||||||
|
try {
|
||||||
|
OpenCV.loadLocally();
|
||||||
|
log.info("OpenCV loaded successfully!");
|
||||||
|
log.info("OpenCV version: {}", Core.getVersionString());
|
||||||
|
|
||||||
|
// Test with generated images
|
||||||
|
testObjectDetection();
|
||||||
|
|
||||||
|
log.info("Application startup complete");
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Failed to start application", e);
|
||||||
|
System.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void testObjectDetection() {
|
||||||
|
try {
|
||||||
|
// Create test images if they don't exist
|
||||||
|
if (!new File("test-images/main.jpg").exists()) {
|
||||||
|
createTestImages();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detect object
|
||||||
|
List<Rect> detections = detectObjectInImage(
|
||||||
|
"test-images/main.jpg",
|
||||||
|
"test-images/template.jpg"
|
||||||
|
);
|
||||||
|
|
||||||
|
log.info("Found {} object(s)", detections.size());
|
||||||
|
|
||||||
|
if (!detections.isEmpty()) {
|
||||||
|
log.info("First detection at: x={}, y={}, width={}, height={}",
|
||||||
|
detections.get(0).x, detections.get(0).y,
|
||||||
|
detections.get(0).width, detections.get(0).height);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Object detection test failed", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Basic object detection using template matching
|
||||||
|
*
|
||||||
|
* @param imagePath Path to the main image
|
||||||
|
* @param templatePath Path to the template image (object to find)
|
||||||
|
* @return List of rectangles where the template was found
|
||||||
|
*/
|
||||||
|
public static List<Rect> detectObjectInImage(String imagePath, String templatePath) {
|
||||||
|
List<Rect> detections = new ArrayList<>();
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Load the main image and template
|
||||||
|
Mat image = Imgcodecs.imread(imagePath);
|
||||||
|
Mat template = Imgcodecs.imread(templatePath);
|
||||||
|
|
||||||
|
|
||||||
|
// Log the paths being used for debugging
|
||||||
|
log.info("Looking for image at: {}", imagePath);
|
||||||
|
log.info("Looking for template at: {}", templatePath);
|
||||||
|
|
||||||
|
// Check if files exist before trying to load
|
||||||
|
File imageFile = new File(imagePath);
|
||||||
|
File templateFile = new File(templatePath);
|
||||||
|
|
||||||
|
if (!imageFile.exists() || !templateFile.exists()) {
|
||||||
|
log.error("Image files not found. Image exists: {}, Template exists: {}",
|
||||||
|
imageFile.exists(), templateFile.exists());
|
||||||
|
return detections;
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("Image size: {}x{}", image.width(), image.height());
|
||||||
|
log.info("Template size: {}x{}", template.width(), template.height());
|
||||||
|
|
||||||
|
// Convert to grayscale (better for template matching)
|
||||||
|
Mat grayImage = new Mat();
|
||||||
|
Mat grayTemplate = new Mat();
|
||||||
|
Imgproc.cvtColor(image, grayImage, Imgproc.COLOR_BGR2GRAY);
|
||||||
|
Imgproc.cvtColor(template, grayTemplate, Imgproc.COLOR_BGR2GRAY);
|
||||||
|
|
||||||
|
// Create result matrix
|
||||||
|
Mat result = new Mat();
|
||||||
|
int resultCols = image.cols() - template.cols() + 1;
|
||||||
|
int resultRows = image.rows() - template.rows() + 1;
|
||||||
|
result.create(resultRows, resultCols, CvType.CV_32FC1);
|
||||||
|
|
||||||
|
// Perform template matching
|
||||||
|
Imgproc.matchTemplate(grayImage, grayTemplate, result, Imgproc.TM_CCOEFF_NORMED);
|
||||||
|
|
||||||
|
// Define threshold for detection (0.8 = 80% similarity)
|
||||||
|
double threshold = 0.8;
|
||||||
|
|
||||||
|
// Find all matches above threshold
|
||||||
|
Core.MinMaxLocResult mmr = Core.minMaxLoc(result);
|
||||||
|
if (mmr.maxVal >= threshold) {
|
||||||
|
// For multiple detections, you would iterate through the result matrix
|
||||||
|
// Here we just take the best match
|
||||||
|
Point matchLoc = mmr.maxLoc;
|
||||||
|
Rect rect = new Rect(matchLoc, new Size(template.cols(), template.rows()));
|
||||||
|
detections.add(rect);
|
||||||
|
|
||||||
|
log.info("Object detected at position: ({}, {}) with confidence: {}",
|
||||||
|
matchLoc.x, matchLoc.y, mmr.maxVal);
|
||||||
|
|
||||||
|
// Draw rectangle around the detected object
|
||||||
|
Imgproc.rectangle(image, rect, new Scalar(0, 255, 0), 2);
|
||||||
|
|
||||||
|
// Save the result with detection
|
||||||
|
|
||||||
|
Imgcodecs.imwrite(outputPath, image);
|
||||||
|
log.info("Result saved to: {}", outputPath);
|
||||||
|
} else {
|
||||||
|
log.info("No object found above threshold");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up
|
||||||
|
image.release();
|
||||||
|
template.release();
|
||||||
|
grayImage.release();
|
||||||
|
grayTemplate.release();
|
||||||
|
result.release();
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Error in object detection: " + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return detections;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple edge detection example (alternative approach)
|
||||||
|
*/
|
||||||
|
public static void detectEdges(String imagePath) {
|
||||||
|
try {
|
||||||
|
Mat image = Imgcodecs.imread(imagePath);
|
||||||
|
Mat gray = new Mat();
|
||||||
|
Mat edges = new Mat();
|
||||||
|
|
||||||
|
Imgproc.cvtColor(image, gray, Imgproc.COLOR_BGR2GRAY);
|
||||||
|
Imgproc.Canny(gray, edges, 100, 200);
|
||||||
|
|
||||||
|
Imgcodecs.imwrite("edges_detected.jpg", edges);
|
||||||
|
log.info("Edge detection completed");
|
||||||
|
|
||||||
|
image.release();
|
||||||
|
gray.release();
|
||||||
|
edges.release();
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Error in edge detection: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
85
src/main/java/so/StatusResource.java
Normal file
85
src/main/java/so/StatusResource.java
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
package so;
|
||||||
|
|
||||||
|
import jakarta.ws.rs.GET;
|
||||||
|
import jakarta.ws.rs.Path;
|
||||||
|
import jakarta.ws.rs.Produces;
|
||||||
|
import jakarta.ws.rs.core.MediaType;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.eclipse.microprofile.config.inject.ConfigProperty;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.time.ZoneId;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Path("/api")
|
||||||
|
public class StatusResource {
|
||||||
|
|
||||||
|
private static final DateTimeFormatter FORMATTER =
|
||||||
|
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss z")
|
||||||
|
.withZone(ZoneId.systemDefault());
|
||||||
|
|
||||||
|
@ConfigProperty(name = "application.version", defaultValue = "1.0-SNAPSHOT")
|
||||||
|
String appVersion;
|
||||||
|
@ConfigProperty(name = "application.groupId")
|
||||||
|
String groupId;
|
||||||
|
|
||||||
|
@ConfigProperty(name = "application.artifactId")
|
||||||
|
String artifactId;
|
||||||
|
|
||||||
|
@ConfigProperty(name = "application.version")
|
||||||
|
String version;
|
||||||
|
|
||||||
|
// Java 16+ Record for structured response
|
||||||
|
public record StatusResponse(
|
||||||
|
String groupId,
|
||||||
|
String artifactId,
|
||||||
|
String version,
|
||||||
|
String status,
|
||||||
|
String timestamp,
|
||||||
|
String mvnVersion,
|
||||||
|
String javaVersion,
|
||||||
|
String os,
|
||||||
|
String openCvVersion
|
||||||
|
) { }
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("/status")
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
public StatusResponse getStatus() {
|
||||||
|
log.info("Status endpoint called");
|
||||||
|
|
||||||
|
return new StatusResponse(groupId, artifactId, version,
|
||||||
|
"running",
|
||||||
|
FORMATTER.format(Instant.now()),
|
||||||
|
appVersion,
|
||||||
|
System.getProperty("java.version"),
|
||||||
|
System.getProperty("os.name") + " " + System.getProperty("os.arch"),
|
||||||
|
getOpenCvVersion()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("/hello")
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
public Map<String, String> sayHello() {
|
||||||
|
log.info("hello endpoint called");
|
||||||
|
|
||||||
|
return Map.of(
|
||||||
|
"message", "Hello from Scrape-UI!",
|
||||||
|
"timestamp", FORMATTER.format(Instant.now()),
|
||||||
|
"openCvVersion", getOpenCvVersion()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getOpenCvVersion() {
|
||||||
|
try {
|
||||||
|
// Load OpenCV if not already loaded (safe to call multiple times)
|
||||||
|
nu.pattern.OpenCV.loadLocally();
|
||||||
|
return org.opencv.core.Core.VERSION;
|
||||||
|
} catch (Exception e) {
|
||||||
|
return "4.9.0 (default)";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
7
src/main/resources/META-INF/resources/beans.xml
Normal file
7
src/main/resources/META-INF/resources/beans.xml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<beans xmlns="https://jakarta.ee/xml/ns/jakartaee"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/beans_3_0.xsd
|
||||||
|
https://jakarta.ee/xml/ns/jakartaee "
|
||||||
|
version="3.0" bean-discovery-mode="all">
|
||||||
|
</beans>
|
||||||
224
src/main/resources/META-INF/resources/index.html
Normal file
224
src/main/resources/META-INF/resources/index.html
Normal file
@@ -0,0 +1,224 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Scrape-UI 1 - Enterprise</title>
|
||||||
|
<script src="https://cdn.tailwindcss.com"></script>
|
||||||
|
<style>
|
||||||
|
.gradient-bg {
|
||||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-hover {
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-hover:hover {
|
||||||
|
transform: translateY(-5px);
|
||||||
|
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body class="bg-gray-50 min-h-screen">
|
||||||
|
<!-- Header -->
|
||||||
|
<header class="gradient-bg text-white py-8">
|
||||||
|
<div class="container mx-auto px-4">
|
||||||
|
<h1 class="text-4xl font-bold mb-2">Scrape-UI Enterprise</h1>
|
||||||
|
<p class="text-xl opacity-90">Powered by Quarkus + Modern Frontend</p>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<!-- Main Content -->
|
||||||
|
<main class="container mx-auto px-4 py-8">
|
||||||
|
<!-- API Status Card -->
|
||||||
|
<!-- API & Build Status Card -->
|
||||||
|
<div class="bg-white rounded-lg shadow-md p-6 mb-8 card-hover">
|
||||||
|
<h2 class="text-2xl font-bold mb-4 text-gray-800">Build & Runtime Status</h2>
|
||||||
|
<div id="api-status" class="space-y-4">
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
|
<!-- Build Information -->
|
||||||
|
<div class="bg-blue-50 p-4 rounded-lg">
|
||||||
|
<h3 class="font-semibold text-blue-800 mb-2">📦 Maven Build</h3>
|
||||||
|
<div class="space-y-2 text-sm">
|
||||||
|
<div class="flex justify-between">
|
||||||
|
<span class="text-gray-600">Group:</span>
|
||||||
|
<span class="font-mono font-medium" id="build-group">-</span>
|
||||||
|
</div>
|
||||||
|
<div class="flex justify-between">
|
||||||
|
<span class="text-gray-600">Artifact:</span>
|
||||||
|
<span class="font-mono font-medium" id="build-artifact">-</span>
|
||||||
|
</div>
|
||||||
|
<div class="flex justify-between">
|
||||||
|
<span class="text-gray-600">Version:</span>
|
||||||
|
<span class="font-mono font-medium px-2 py-1 bg-blue-100 rounded" id="build-version">-</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Runtime Information -->
|
||||||
|
<div class="bg-green-50 p-4 rounded-lg">
|
||||||
|
<h3 class="font-semibold text-green-800 mb-2">🚀 Runtime</h3>
|
||||||
|
<div class="space-y-2 text-sm">
|
||||||
|
<div class="flex justify-between">
|
||||||
|
<span class="text-gray-600">Status:</span>
|
||||||
|
<span class="px-2 py-1 rounded-full text-xs font-semibold bg-green-100 text-green-800" id="runtime-status">-</span>
|
||||||
|
</div>
|
||||||
|
<div class="flex justify-between">
|
||||||
|
<span class="text-gray-600">Java:</span>
|
||||||
|
<span class="font-mono" id="java-version">-</span>
|
||||||
|
</div>
|
||||||
|
<div class="flex justify-between">
|
||||||
|
<span class="text-gray-600">Platform:</span>
|
||||||
|
<span class="font-mono" id="runtime-os">-</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Timestamp & Additional Info -->
|
||||||
|
<div class="pt-4 border-t">
|
||||||
|
<div class="flex justify-between items-center">
|
||||||
|
<div>
|
||||||
|
<p class="text-sm text-gray-500">Last Updated</p>
|
||||||
|
<p class="font-medium" id="last-updated">-</p>
|
||||||
|
</div>
|
||||||
|
<button onclick="fetchStatus()" class="bg-gray-100 hover:bg-gray-200 text-gray-700 px-4 py-2 rounded-lg text-sm transition-colors">
|
||||||
|
🔄 Refresh
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- API Response Card -->
|
||||||
|
<div class="bg-white rounded-lg shadow-md p-6 card-hover">
|
||||||
|
<h2 class="text-2xl font-bold mb-4 text-gray-800">API Test</h2>
|
||||||
|
<button id="test-api" class="bg-blue-600 text-white px-6 py-2 rounded-lg hover:bg-blue-700 transition-colors mb-4">
|
||||||
|
Test Greeting API
|
||||||
|
</button>
|
||||||
|
<div id="api-response" class="bg-gray-100 p-4 rounded-lg">
|
||||||
|
<pre class="text-sm text-gray-700">Click the button to test the API</pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Features Grid -->
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 mt-8">
|
||||||
|
<div class="bg-white rounded-lg shadow-md p-6 card-hover">
|
||||||
|
<h3 class="text-xl font-semibold mb-2 text-gray-800">⚡ Quarkus Backend</h3>
|
||||||
|
<p class="text-gray-600">Fast startup, low memory footprint, optimized for containers</p>
|
||||||
|
</div>
|
||||||
|
<div class="bg-white rounded-lg shadow-md p-6 card-hover">
|
||||||
|
<h3 class="text-xl font-semibold mb-2 text-gray-800">🚀 REST API</h3>
|
||||||
|
<p class="text-gray-600">RESTful endpoints with JSON responses</p>
|
||||||
|
</div>
|
||||||
|
<div class="bg-white rounded-lg shadow-md p-6 card-hover">
|
||||||
|
<h3 class="text-xl font-semibold mb-2 text-gray-800">🎨 Modern UI</h3>
|
||||||
|
<p class="text-gray-600">Responsive design with Tailwind CSS</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// Fetch API status on load
|
||||||
|
async function fetchStatus() {
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/status')
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP ${ response.status }: ${ response.statusText }`)
|
||||||
|
}
|
||||||
|
const data = await response.json()
|
||||||
|
|
||||||
|
// Update Build Information
|
||||||
|
document.getElementById('build-group').textContent = data.groupId || 'N/A'
|
||||||
|
document.getElementById('build-artifact').textContent = data.artifactId || data.name || 'N/A'
|
||||||
|
document.getElementById('build-version').textContent = data.version || 'N/A'
|
||||||
|
|
||||||
|
// Update Runtime Information
|
||||||
|
document.getElementById('runtime-status').textContent = data.status || 'unknown'
|
||||||
|
document.getElementById('java-version').textContent = data.javaVersion || System.getProperty?.('java.version') || 'N/A'
|
||||||
|
document.getElementById('runtime-os').textContent = data.os || 'N/A'
|
||||||
|
|
||||||
|
// Update Timestamp
|
||||||
|
const timestamp = data.timestamp ? new Date(data.timestamp).toLocaleString() : 'N/A'
|
||||||
|
document.getElementById('last-updated').textContent = timestamp
|
||||||
|
|
||||||
|
// Update status badge color based on status
|
||||||
|
const statusBadge = document.getElementById('runtime-status')
|
||||||
|
if (data.status?.toLowerCase() === 'running') {
|
||||||
|
statusBadge.className = 'px-2 py-1 rounded-full text-xs font-semibold bg-green-100 text-green-800'
|
||||||
|
} else {
|
||||||
|
statusBadge.className = 'px-2 py-1 rounded-full text-xs font-semibold bg-yellow-100 text-yellow-800'
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching status:', error)
|
||||||
|
document.getElementById('api-status').innerHTML = `
|
||||||
|
<div class="bg-red-50 border-l-4 border-red-500 p-4">
|
||||||
|
<div class="flex">
|
||||||
|
<div class="flex-shrink-0">
|
||||||
|
<svg class="h-5 w-5 text-red-400" viewBox="0 0 20 20" fill="currentColor">
|
||||||
|
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div class="ml-3">
|
||||||
|
<p class="text-sm text-red-700">Failed to load status: ${ error.message }</p>
|
||||||
|
<button onclick="fetchStatus()" class="mt-2 text-sm text-red-700 hover:text-red-600 font-medium">
|
||||||
|
Retry →
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch API status on load
|
||||||
|
async function fetchStatus3() {
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/status')
|
||||||
|
const data = await response.json()
|
||||||
|
document.getElementById('api-status').innerHTML = `
|
||||||
|
<p><strong>Application:</strong> ${ data.application }</p>
|
||||||
|
<p><strong>Status:</strong> <span class="text-green-600 font-semibold">${ data.status }</span></p>
|
||||||
|
<p><strong>Version:</strong> ${ data.version }</p>
|
||||||
|
<p><strong>Timestamp:</strong> ${ data.timestamp }</p>
|
||||||
|
`
|
||||||
|
} catch (error) {
|
||||||
|
document.getElementById('api-status').innerHTML = `
|
||||||
|
<p class="text-red-600">Error loading status: ${ error.message }</p>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test greeting API
|
||||||
|
document.getElementById('test-api').addEventListener('click', async () => {
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/hello')
|
||||||
|
const data = await response.json()
|
||||||
|
document.getElementById('api-response').innerHTML = `
|
||||||
|
<pre class="text-sm text-gray-700">${ JSON.stringify(data, null, 2) }</pre>
|
||||||
|
`
|
||||||
|
} catch (error) {
|
||||||
|
document.getElementById('api-response').innerHTML = `
|
||||||
|
<pre class="text-sm text-red-600">Error: ${ error.message }</pre>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// Auto-refresh every 30 seconds
|
||||||
|
let refreshInterval = setInterval(fetchStatus, 30000);
|
||||||
|
|
||||||
|
// Stop auto-refresh when page loses focus (optional)
|
||||||
|
document.addEventListener('visibilitychange', function() {
|
||||||
|
if (document.hidden) {
|
||||||
|
clearInterval(refreshInterval);
|
||||||
|
} else {
|
||||||
|
refreshInterval = setInterval(fetchStatus, 30000);
|
||||||
|
fetchStatus(); // Refresh immediately when returning to tab
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// Load status on page load
|
||||||
|
fetchStatus()
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
39
src/main/resources/application.properties
Normal file
39
src/main/resources/application.properties
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
# Application Configuration
|
||||||
|
# Values will be injected from pom.xml during build
|
||||||
|
quarkus.application.name=${project.artifactId}
|
||||||
|
quarkus.application.version=${project.version}
|
||||||
|
# Custom properties for groupId if needed
|
||||||
|
application.groupId=${project.groupId}
|
||||||
|
application.artifactId=${project.artifactId}
|
||||||
|
application.version=${project.version}
|
||||||
|
|
||||||
|
|
||||||
|
# HTTP Configuration
|
||||||
|
quarkus.http.port=8081
|
||||||
|
# ========== DEVELOPMENT (quarkus:dev) ==========
|
||||||
|
%dev.quarkus.http.host=127.0.0.1
|
||||||
|
# ========== PRODUCTION (Docker/JAR) ==========
|
||||||
|
%prod.quarkus.http.host=0.0.0.0
|
||||||
|
# ========== TEST PROFILE ==========
|
||||||
|
%test.quarkus.http.host=localhost
|
||||||
|
# Enable CORS for frontend development
|
||||||
|
quarkus.http.cors=true
|
||||||
|
quarkus.http.cors.origins=*
|
||||||
|
quarkus.http.cors.methods=GET,POST,PUT,DELETE,OPTIONS
|
||||||
|
quarkus.http.cors.headers=accept,authorization,content-type,x-requested-with
|
||||||
|
|
||||||
|
# Logging Configuration
|
||||||
|
quarkus.log.console.format=%d{HH:mm:ss} %-5p [%c{2.}] (%t) %s%e%n
|
||||||
|
quarkus.log.console.level=INFO
|
||||||
|
|
||||||
|
# Development mode settings
|
||||||
|
%dev.quarkus.log.console.level=DEBUG
|
||||||
|
%dev.quarkus.live-reload.instrumentation=true
|
||||||
|
|
||||||
|
# Production optimizations
|
||||||
|
%prod.quarkus.http.enable-compression=true
|
||||||
|
|
||||||
|
# Static resources
|
||||||
|
quarkus.http.enable-compression=true
|
||||||
|
quarkus.rest.path=/
|
||||||
|
quarkus.http.root-path=/
|
||||||
BIN
test-images/lena.jpg
Normal file
BIN
test-images/lena.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 90 KiB |
BIN
test-images/main.jpg
Normal file
BIN
test-images/main.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
BIN
test-images/template.jpg
Normal file
BIN
test-images/template.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 827 B |
20
workflows/maven.yml
Normal file
20
workflows/maven.yml
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
name: Publish to Gitea Package Registry
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- 'v*'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
publish:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Set up JDK 25
|
||||||
|
uses: actions/setup-java@v4
|
||||||
|
with:
|
||||||
|
java-version: '25'
|
||||||
|
distribution: 'temurin'
|
||||||
|
- name: Publish with Maven
|
||||||
|
run: mvn --batch-mode clean deploy
|
||||||
|
env:
|
||||||
|
GITEA_TOKEN: ${{ secrets.EA_PUBLISH_TOKEN }}
|
||||||
Reference in New Issue
Block a user