Data Ingestion & Scraping Pipeline
This document describes the end-to-end data ingestion pipeline used to scrape, parse, database-populate, and optimize restaurant and menu assets for Flavor Fusion (FoodApp).
graph TD
A[Scraped Wolt JSON files] -->|1. Parse & Correlate| B[Python ingestion utility]
M[master.json with branch details] -->|1. Correlate| B
B -->|2. Generate| C[Populating SQL Script]
B -->|3. Generate| D[to_upload.api File]
C -->|4. Execute| E[(PostgreSQL Database)]
D -->|5. Read via Spring Boot| F[UrlBulkImageImporter]
F -->|6. Download| G[Original Image URLs]
F -->|7. Image Resize 600x600 PNG| H[Thumbnailator]
H -->|8. Upload| I[AWS S3 Bucket]
I -->|9. Serve| J[CloudFront CDN]1. Phase 1: Python Ingestion & SQL Generator
The Python ingestion script (process_restaurant.py) processes scraped JSON assortments of a restaurant, correlates them with venue information, assigns contiguous database sequences, and outputs both a SQL populate script and an image manifest.
Input Folder Structure
The Python script expects a restaurant-specific folder containing:
master.json(optional): Contains Wolt section items representing the branches/venues. This file maps Wolt branch slugs to names, physical addresses, and geographical coordinates.assortment-{branch-slug}.jsonorassortment.json: Contains Wolt categories, items, prices, descriptions, options/modifiers, and image URLs.{restaurant_name}.json: The primary JSON file containing general restaurant metadata.
Command Parameters
Run the Python script with the following arguments to specify the start of ID ranges (sequences) in the PostgreSQL database:
python process_restaurant.py <folder_path> \
--users <user_seq_start> \
--rest <restaurant_id> \
--loc <location_seq_start> \
--cat <category_seq_start> \
--og <option_group_seq_start> \
--ov <option_variant_seq_start> \
--menu <menu_seq_start> \
--bmi <branch_menu_item_seq_start> \
--desc "Optional description of the restaurant"Generation Logic
Database Sequences
The script reads current sequence positions and increments them to generate unique database keys for:
- Restaurant Manager: Created with Role
3(restaurant_manager). - Branch Managers: One per branch with Role
4(branch_manager) and structured emails likeastir@burgerking.al(using the last part of the branch slug). - Restaurant Locations & Opening Hours: Location coordinates are loaded from
master.jsonas WKTPOINT(longitude latitude)usingST_GeomFromText. Default hours are set from09:00:00to23:00:00daily. - Payment Methods: Automatically connects cash (ID
1) and credit card (ID3) payment methods to the branch. - Categories, Menu Options, & Option Variants: Normalizes shared categories and modifiers across Wolt's options structure.
- Branch Menu Items: Automatically maps items available in each branch, and randomly highlights up to 4 items per branch (
is_highlighted = true).
Output Files
{restaurant_name}_populating_db.sql: A complete SQL script containing bulkINSERT ... ON CONFLICT DO NOTHINGstatements ready to run on PostgreSQL.to_upload.api: A whitespace-separated image manifest mapping destination CloudFront folders, optimized filenames, and original scraped image URLs. Format:text<restaurant_folder_name> <target_filename.png> <original_web_image_url>
2. Phase 2: Bulk Image Downloader & CDN Uploader
To avoid hosting high-resolution or heavy original URLs directly from scraping sources, the Spring Boot backend contains a utility (UrlBulkImageImporter) to process the to_upload.api manifest, optimize images, and upload them to AWS S3.
Implementation Class
The importer is run as a console command runner in Spring Boot:
@Slf4j
@Component
@Profile("import_url")
@RequiredArgsConstructor
public class UrlBulkImageImporter implements CommandLineRunner {
...
}Operational Steps
- Activate the Profile: Run the Spring Boot backend with the active profile
-Dspring.profiles.active=import_url. - Read Manifest: The importer reads
/json/restaurants/to_upload.apifrom classpath resources. - Parse and Standardize:
- Skips empty lines and splits entries.
- Cleans filenames by stripping query parameters (e.g.
?w=1080or resizing tags) to preventAccessDeniederrors in S3. - Renames file extensions to
.pngto match optimized format.
- Download: Fetches the original image bytes via
RestTemplate. - Optimize: Uses
Thumbnailatorto resize the downloaded image down to a fast-loading size of600x600and outputs it strictly as a compressedpng. - AWS S3 Upload: Sends the optimized image bytes to AWS S3 under the key pattern
folder/filename.pngwith a MIME-type ofimage/png. - CloudFront Distribution: Images are instantly available via CloudFront CDN:
https://d3u269mlo8clta.cloudfront.net/{restaurantName}/filename.png
3. Walkthrough: Adding a New Restaurant
Follow this standard manual procedure to ingest a new restaurant into Flavor Fusion:
Step 1: Prepare the Scraped JSON Files
Create a new folder under your workspace (e.g., ./scraped/burger-king) and place your Wolt scraped JSON files inside. Ensure you have the branch assortments (assortment-*.json) and the optional master.json.
Step 2: Determine Current Database Sequence Offsets
Query your database to find the maximum IDs currently used so you don't create overlapping records:
SELECT MAX(id) FROM users;
SELECT MAX(id) FROM restaurant;
SELECT MAX(id) FROM restaurant_locations;
SELECT MAX(id) FROM categories;
SELECT MAX(id) FROM option_group;
SELECT MAX(id) FROM option_variant;
SELECT MAX(id) FROM menus;
SELECT MAX(id) FROM branch_menu_items;Step 3: Run the Ingestion Script
Run the Python script using the offsets fetched in Step 2:
python process_restaurant.py ./scraped/burger-king \
--users 150 \
--rest 12 \
--loc 40 \
--cat 200 \
--og 500 \
--ov 1200 \
--menu 800 \
--bmi 2500 \
--desc "Burger King - Taste is King!"Step 4: Run the Generated SQL Script
Import the generated SQL file (burger-king_populating_db.sql) into your PostgreSQL database:
psql -U username -d foodapp_db -f ./scraped/burger-king/burger-king_populating_db.sqlStep 5: Upload Cover & Profile Images (Manual)
Since cover and profile images cannot be easily scraped in a structured format:
- Place custom cover and profile photos under the restaurant image directory.
- Save them using the naming convention:
- Cover:
{restaurant_name}_cover.png - Profile:
{restaurant_name}_profile.png
- Cover:
- Upload them to the S3 bucket under the restaurant's folder.
Step 6: Import & Optimize Menu Item Images
- Copy the generated
to_upload.apifile into the Spring Boot resource directory:backend/src/main/resources/json/restaurants/to_upload.api - Run your Spring Boot application with the
import_urlprofile:bashmvn spring-boot:run -Dspring-boot.run.profiles=import_url - Verify that all menu item images are processed, optimized, uploaded to S3, and can be resolved through CloudFront CDN.