Changes between Version 3 and Version 4 of Advanced Application Development
- Timestamp:
- 09/18/25 16:11:07 (6 weeks ago)
Legend:
- Unmodified
- Added
- Removed
- Modified
-
Advanced Application Development
v3 v4 1 1 = Report Tables 2 2 3 == Main Restaurant Report Summary Table 4 {{{#!sql 3 5 CREATE TABLE restaurant_weekly_report ( 4 6 report_id SERIAL PRIMARY KEY, 5 7 restaurant_id INT NOT NULL, 6 report_week DATE NOT NULL, -- Monday of the week7 week_number INT NOT NULL, -- Week 1, 2, or 3 (most recent)8 report_week DATE NOT NULL, -- Monday of the week 9 week_number INT NOT NULL, -- Week 1, 2, or 3 (most recent) 8 10 9 11 -- Order Statistics … … 20 22 -- Operational Metrics 21 23 avg_preparation_time_minutes DECIMAL(8,2) DEFAULT 0.00, 22 peak_hour INTEGER, -- Hour with most orders23 busiest_day INTEGER, -- Day of week (0=Sunday, 6=Saturday)24 peak_hour INTEGER, -- Hour with most orders 25 busiest_day INTEGER, -- Day of week (0=Sunday, 6=Saturday) 24 26 25 27 -- Performance Indicators 26 completion_rate DECIMAL(5,2) DEFAULT 0.00, -- % of orders completed28 completion_rate DECIMAL(5,2) DEFAULT 0.00, -- % of orders completed 27 29 customer_satisfaction DECIMAL(3,2) DEFAULT 0.00, -- Placeholder for ratings 28 30 orders_per_hour DECIMAL(8,2) DEFAULT 0.00, … … 32 34 last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP, 33 35 34 CONSTRAINT fk_weekly_report_restaurant FOREIGN KEY (restaurant_id) REFERENCES restaurant(restaurant_id), 35 CONSTRAINT uq_restaurant_week UNIQUE (restaurant_id, report_week) 36 CONSTRAINT fk_weekly_report_restaurant 37 FOREIGN KEY (restaurant_id) REFERENCES restaurant(restaurant_id), 38 CONSTRAINT uq_restaurant_week 39 UNIQUE (restaurant_id, report_week) 36 40 ); 37 \\ 38 == Popular Items Report Table 41 }}} 42 \\ 43 == Popular items report 44 {{{#!sql 39 45 CREATE TABLE restaurant_popular_items ( 40 46 report_id SERIAL PRIMARY KEY, … … 43 49 item_id INT NOT NULL, 44 50 item_name VARCHAR(100), 45 46 51 times_ordered INTEGER DEFAULT 0, 47 52 total_quantity INTEGER DEFAULT 0, … … 49 54 avg_price DECIMAL(8,2) DEFAULT 0.00, 50 55 popularity_rank INTEGER, 51 52 56 generated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, 53 57 54 CONSTRAINT fk_popular_items_restaurant FOREIGN KEY (restaurant_id) REFERENCES restaurant(restaurant_id), 55 CONSTRAINT fk_popular_items_item FOREIGN KEY (item_id) REFERENCES item(item_id) 58 CONSTRAINT fk_popular_items_restaurant 59 FOREIGN KEY (restaurant_id) REFERENCES restaurant(restaurant_id), 60 CONSTRAINT fk_popular_items_item 61 FOREIGN KEY (item_id) REFERENCES item(item_id) 56 62 ); 57 \\ 58 == Daily Breakdown Table 63 }}} 64 \\ 65 == Daily breakdown 66 {{{#!sql 59 67 CREATE TABLE restaurant_daily_breakdown ( 60 68 breakdown_id SERIAL PRIMARY KEY, 61 69 restaurant_id INT NOT NULL, 62 70 report_date DATE NOT NULL, 63 day_of_week INTEGER, -- 0=Sunday, 6=Saturday 64 71 day_of_week INTEGER, -- 0=Sunday, 6=Saturday 65 72 daily_orders INTEGER DEFAULT 0, 66 73 daily_revenue DECIMAL(10,2) DEFAULT 0.00, … … 70 77 -- Hourly breakdown (JSON format for flexibility) 71 78 hourly_orders JSONB, 72 73 79 generated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, 74 80 75 CONSTRAINT fk_daily_breakdown_restaurant FOREIGN KEY (restaurant_id) REFERENCES restaurant(restaurant_id), 76 CONSTRAINT uq_restaurant_daily UNIQUE (restaurant_id, report_date) 81 CONSTRAINT fk_daily_breakdown_restaurant 82 FOREIGN KEY (restaurant_id) REFERENCES restaurant(restaurant_id), 83 CONSTRAINT uq_restaurant_daily 84 UNIQUE (restaurant_id, report_date) 77 85 ); 78 \\ 79 == Comparative Analysis Table (Week Over Week) 86 }}} 87 \\ 88 == Comparative analysis table (week over week) 89 {{{#!sql 80 90 CREATE TABLE restaurant_trend_analysis ( 81 91 trend_id SERIAL PRIMARY KEY, … … 84 94 85 95 -- 3-week trends 86 week1_revenue DECIMAL(10,2), -- Most recent week96 week1_revenue DECIMAL(10,2), -- Most recent week 87 97 week2_revenue DECIMAL(10,2), 88 98 week3_revenue DECIMAL(10,2), 89 90 99 week1_orders INTEGER, 91 100 week2_orders INTEGER, … … 93 102 94 103 -- Trend indicators 95 revenue_trend VARCHAR(20), -- 'INCREASING', 'DECREASING', 'STABLE'104 revenue_trend VARCHAR(20), -- 'INCREASING', 'DECREASING', 'STABLE' 96 105 order_trend VARCHAR(20), 97 106 98 107 -- Growth rates 99 revenue_growth_rate DECIMAL(8,2), -- Week over week %108 revenue_growth_rate DECIMAL(8,2), -- Week over week % 100 109 order_growth_rate DECIMAL(8,2), 101 110 … … 103 112 performance_alerts JSONB, 104 113 recommendations JSONB, 105 106 114 generated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, 107 115 108 CONSTRAINT fk_trend_analysis_restaurant FOREIGN KEY (restaurant_id) REFERENCES restaurant(restaurant_id) 116 CONSTRAINT fk_trend_analysis_restaurant 117 FOREIGN KEY (restaurant_id) REFERENCES restaurant(restaurant_id) 109 118 ); 110 \\ 111 == Performance Metrics Table 119 }}} 120 \\ 121 == Performance metrics table 122 {{{#!sql 112 123 CREATE TABLE performance_metrics ( 113 124 metric_id SERIAL PRIMARY KEY, … … 116 127 metric_value DECIMAL(8,2), 117 128 recorded_at TIMESTAMP, 118 CONSTRAINT fk_performance_metrics_restaurant FOREIGN KEY (restaurant_id) REFERENCES RESTAURANT(restaurant_id) 129 130 CONSTRAINT fk_performance_metrics_restaurant 131 FOREIGN KEY (restaurant_id) REFERENCES restaurant(restaurant_id) 119 132 ); 120 \\ 121 == Audit Table for Orders 133 }}} 134 \\ 135 == Audit table for orders 136 {{{#!sql 122 137 CREATE TABLE audit_orders ( 123 138 audit_id SERIAL PRIMARY KEY, … … 128 143 change_timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP 129 144 ); 130 \\ 131 \\ 145 }}} 146 \\ 147 148 132 149 = Utility Functions 133 \\ 134 == Get Week Start Function 150 == Function to get the Monday of a given week 151 {{{#!sql 135 152 CREATE OR REPLACE FUNCTION get_week_start(input_date DATE) 136 RETURNS DATE AS $$153 RETURNS DATE AS $$ 137 154 BEGIN 138 155 RETURN input_date - INTERVAL '1 day' * EXTRACT(DOW FROM input_date); 139 156 END; 140 157 $$ LANGUAGE plpgsql; 141 142 == Get Week Number Function 158 }}} 159 \\ 160 == Function to get week number (1, 2, or 3 for last 3 weeks) 161 {{{#!sql 143 162 CREATE OR REPLACE FUNCTION get_week_number(input_date DATE) 144 RETURNS INTEGER AS $$163 RETURNS INTEGER AS $$ 145 164 DECLARE 146 165 week_start DATE; … … 158 177 END; 159 178 $$ LANGUAGE plpgsql; 160 161 == Main Report Generation Function 162 CREATE OR REPLACE FUNCTION generate_restaurant_weekly_report(p_restaurant_id INT, p_target_date DATE DEFAULT CURRENT_DATE) 163 RETURNS void AS $$ 179 }}} 180 \\ 181 == Main report generation function 182 {{{#!sql 183 CREATE OR REPLACE FUNCTION generate_restaurant_weekly_report( 184 p_restaurant_id INT, 185 p_target_date DATE DEFAULT CURRENT_DATE 186 ) 187 RETURNS void AS $$ 164 188 DECLARE 165 189 week_start DATE; … … 218 242 INTO v_total_items_sold 219 243 FROM order_items oi 220 JOIN orders o ON oi.order_id = o.order_id244 JOIN orders o ON oi.order_id = o.order_id 221 245 WHERE o.restaurant_id = p_restaurant_id 222 246 AND o.order_date::date BETWEEN week_start AND week_end 223 247 AND o.order_status = 'delivered'; 224 248 225 -- Calculate average preparation time ( using performance metricsif available)249 -- Calculate average preparation time (if available) 226 250 SELECT COALESCE(AVG(metric_value), 0) 227 251 INTO v_avg_prep_time … … 241 265 LIMIT 1; 242 266 243 -- Find busiest day of week267 -- Find busiest day 244 268 SELECT EXTRACT(dow FROM order_date) 245 269 INTO v_busiest_day … … 251 275 LIMIT 1; 252 276 253 -- C alculate completion rate277 -- Completion rate 254 278 v_completion_rate := CASE 255 279 WHEN v_total_orders > 0 THEN … … 258 282 END; 259 283 260 -- Calculate orders per hour (during operating hours)284 -- Orders per hour (during operating hours) 261 285 WITH operating_hours AS ( 262 SELECT 263 EXTRACT(EPOCH FROM (closing_time - opening_time))/3600 * 7 as total_hours_week 286 SELECT EXTRACT(EPOCH FROM (closing_time - opening_time)) / 3600 * 7 AS total_hours_week 264 287 FROM restaurant 265 288 WHERE restaurant_id = p_restaurant_id 266 289 ) 267 290 SELECT CASE 268 WHEN oh.total_hours_week > 0 THEN v_total_orders::DECIMAL / oh.total_hours_week 269 ELSE 0 270 END 291 WHEN oh.total_hours_week > 0 292 THEN v_total_orders::DECIMAL / oh.total_hours_week 293 ELSE 0 294 END 271 295 INTO v_orders_per_hour 272 296 FROM operating_hours oh; 273 297 274 -- Insert or update theweekly report298 -- Insert or update weekly report 275 299 INSERT INTO restaurant_weekly_report ( 276 300 restaurant_id, report_week, week_number, … … 302 326 last_updated = CURRENT_TIMESTAMP; 303 327 304 -- Generate popular items report328 -- Generate supporting reports 305 329 PERFORM generate_popular_items_report(p_restaurant_id, week_start); 306 307 -- Generate daily breakdown308 330 PERFORM generate_daily_breakdown_report(p_restaurant_id, week_start, week_end); 309 310 331 END; 311 332 $$ LANGUAGE plpgsql; 312 313 == Popular Items Report Function 314 CREATE OR REPLACE FUNCTION generate_popular_items_report(p_restaurant_id INT, p_week_start DATE) 315 RETURNS void AS $$ 333 }}} 334 \\ 335 == Popular items report function 336 {{{#!sql 337 CREATE OR REPLACE FUNCTION generate_popular_items_report( 338 p_restaurant_id INT, 339 p_week_start DATE 340 ) 341 RETURNS void AS $$ 316 342 BEGIN 317 343 -- Clear existing data for this week 318 344 DELETE FROM restaurant_popular_items 319 WHERE restaurant_id = p_restaurant_id AND report_week = p_week_start; 320 321 -- Insert top 10 popular items for the week 345 WHERE restaurant_id = p_restaurant_id 346 AND report_week = p_week_start; 347 348 -- Insert top 10 popular items 322 349 INSERT INTO restaurant_popular_items ( 323 350 restaurant_id, report_week, item_id, item_name, … … 327 354 SELECT 328 355 i.item_id, 329 i.name asitem_name,330 COUNT(DISTINCT o.order_id) astimes_ordered,331 SUM(oi.quantity) astotal_quantity,332 SUM(oi.total_price) astotal_revenue,333 AVG(oi.total_price / oi.quantity) asavg_price,334 ROW_NUMBER() OVER (ORDER BY COUNT(DISTINCT o.order_id) DESC) aspopularity_rank356 i.name AS item_name, 357 COUNT(DISTINCT o.order_id) AS times_ordered, 358 SUM(oi.quantity) AS total_quantity, 359 SUM(oi.total_price) AS total_revenue, 360 AVG(oi.total_price / oi.quantity) AS avg_price, 361 ROW_NUMBER() OVER (ORDER BY COUNT(DISTINCT o.order_id) DESC) AS popularity_rank 335 362 FROM orders o 336 JOIN order_items oi ON o.order_id = oi.order_id337 JOIN item i ON oi.item_id = i.item_id363 JOIN order_items oi ON o.order_id = oi.order_id 364 JOIN item i ON oi.item_id = i.item_id 338 365 WHERE o.restaurant_id = p_restaurant_id 339 366 AND o.order_date::date BETWEEN p_week_start AND p_week_start + INTERVAL '6 days' … … 349 376 END; 350 377 $$ LANGUAGE plpgsql; 351 352 == Trend Analysis Function 378 }}} 379 \\ 380 == Trend analysis function 381 {{{#!sql 353 382 CREATE OR REPLACE FUNCTION generate_trend_analysis(p_restaurant_id INT) 354 RETURNS void AS $$383 RETURNS void AS $$ 355 384 DECLARE 356 385 week1_start DATE := get_week_start(CURRENT_DATE); … … 374 403 BEGIN 375 404 -- Get weekly data 376 SELECT gross_revenue, total_orders INTO w1_revenue, w1_orders 405 SELECT gross_revenue, total_orders 406 INTO w1_revenue, w1_orders 377 407 FROM restaurant_weekly_report 378 408 WHERE restaurant_id = p_restaurant_id AND report_week = week1_start; 379 409 380 SELECT gross_revenue, total_orders INTO w2_revenue, w2_orders 410 SELECT gross_revenue, total_orders 411 INTO w2_revenue, w2_orders 381 412 FROM restaurant_weekly_report 382 413 WHERE restaurant_id = p_restaurant_id AND report_week = week2_start; 383 414 384 SELECT gross_revenue, total_orders INTO w3_revenue, w3_orders 415 SELECT gross_revenue, total_orders 416 INTO w3_revenue, w3_orders 385 417 FROM restaurant_weekly_report 386 418 WHERE restaurant_id = p_restaurant_id AND report_week = week3_start; 387 419 388 -- Calculate trends420 -- Growth calculations 389 421 IF w2_revenue > 0 THEN 390 422 revenue_growth := ROUND(((w1_revenue - w2_revenue) / w2_revenue) * 100, 2); … … 399 431 END IF; 400 432 401 -- Determine trend direction433 -- Trend direction 402 434 revenue_trend_val := CASE 403 435 WHEN revenue_growth > 5 THEN 'INCREASING' … … 412 444 END; 413 445 414 -- Generate alerts446 -- Alerts 415 447 IF revenue_growth < -20 THEN 416 alerts := alerts || '{"type": "revenue_decline", "severity": "high", "message":"Revenue declined by more than 20% this week"}'::jsonb;448 alerts := alerts || '{"type":"revenue_decline","severity":"high","message":"Revenue declined by more than 20% this week"}'::jsonb; 417 449 END IF; 418 450 419 451 IF order_growth < -15 THEN 420 alerts := alerts || '{"type": "order_decline", "severity": "medium", "message":"Order volume declined by more than 15% this week"}'::jsonb;421 END IF; 422 423 -- Generate recommendations452 alerts := alerts || '{"type":"order_decline","severity":"medium","message":"Order volume declined by more than 15% this week"}'::jsonb; 453 END IF; 454 455 -- Recommendations 424 456 IF revenue_trend_val = 'DECREASING' THEN 425 recommendations := recommendations || '{"type": "marketing", "action":"Consider promotional campaigns or menu updates"}'::jsonb;457 recommendations := recommendations || '{"type":"marketing","action":"Consider promotional campaigns or menu updates"}'::jsonb; 426 458 END IF; 427 459 428 460 IF order_trend_val = 'DECREASING' THEN 429 recommendations := recommendations || '{"type": "operations", "action":"Review menu pricing and delivery times"}'::jsonb;430 END IF; 431 432 -- Insert trend analysis461 recommendations := recommendations || '{"type":"operations","action":"Review menu pricing and delivery times"}'::jsonb; 462 END IF; 463 464 -- Insert or update 433 465 INSERT INTO restaurant_trend_analysis ( 434 466 restaurant_id, week1_revenue, week2_revenue, week3_revenue, … … 459 491 END; 460 492 $$ LANGUAGE plpgsql; 493 }}} 494 \\ 495 496 461 497 462 498 = Trigger Functions 463 == Update Restaurant Reports Trigger Function 499 == Trigger function to update reports when orders change 500 {{{#!sql 464 501 CREATE OR REPLACE FUNCTION update_restaurant_reports() 465 RETURNS TRIGGER AS $$502 RETURNS TRIGGER AS $$ 466 503 DECLARE 467 504 affected_restaurant_id INT; … … 476 513 -- Update reports for the affected week 477 514 PERFORM generate_restaurant_weekly_report(affected_restaurant_id, order_date_to_check); 478 479 515 -- Update trend analysis 480 516 PERFORM generate_trend_analysis(affected_restaurant_id); … … 493 529 END; 494 530 $$ LANGUAGE plpgsql; 495 496 == Update Reports on Items Change Trigger Function 531 }}} 532 533 534 \\ 535 == Trigger function for order items changes 536 {{{#!sql 497 537 CREATE OR REPLACE FUNCTION update_reports_on_items_change() 498 RETURNS TRIGGER AS $$538 RETURNS TRIGGER AS $$ 499 539 DECLARE 500 540 affected_restaurant_id INT; … … 502 542 BEGIN 503 543 -- Get restaurant and date from the order 504 SELECT restaurant_id, order_date::date INTO affected_restaurant_id, order_date_to_check 505 FROM orders WHERE order_id = COALESCE(NEW.order_id, OLD.order_id); 544 SELECT restaurant_id, order_date::date 545 INTO affected_restaurant_id, order_date_to_check 546 FROM orders 547 WHERE order_id = COALESCE(NEW.order_id, OLD.order_id); 506 548 507 549 -- Only update if within the last 3 weeks … … 514 556 END; 515 557 $$ LANGUAGE plpgsql; 516 517 == Create Triggers 558 }}} 559 \\ 560 == Create triggers 561 {{{#!sql 518 562 -- Trigger on orders table 519 563 CREATE TRIGGER trg_update_restaurant_reports 520 AFTER INSERT OR UPDATE OR DELETE ON orders521 FOR EACH ROW564 AFTER INSERT OR UPDATE OR DELETE ON orders 565 FOR EACH ROW 522 566 EXECUTE FUNCTION update_restaurant_reports(); 523 567 524 568 -- Trigger on order_items table 525 569 CREATE TRIGGER trg_update_reports_items 526 AFTER INSERT OR UPDATE OR DELETE ON order_items527 FOR EACH ROW570 AFTER INSERT OR UPDATE OR DELETE ON order_items 571 FOR EACH ROW 528 572 EXECUTE FUNCTION update_reports_on_items_change(); 529 530 == Report Views for Easy Access 531 Comprehensive 3-Week Restaurant Report View 573 }}} 574 \\ 575 == Report Views 576 {{{#!sql 577 -- Comprehensive 3-week restaurant report view 532 578 CREATE VIEW restaurant_3week_summary AS 533 579 SELECT … … 540 586 MAX(CASE WHEN rwr.week_number = 1 THEN rwr.completion_rate END) as current_week_completion_rate, 541 587 542 -- Previous week (Week 2) 588 -- Previous week (Week 2) 543 589 MAX(CASE WHEN rwr.week_number = 2 THEN rwr.total_orders END) as prev_week_orders, 544 590 MAX(CASE WHEN rwr.week_number = 2 THEN rwr.gross_revenue END) as prev_week_revenue, … … 558 604 MAX(CASE WHEN rwr.week_number = 1 THEN rwr.busiest_day END) as current_busiest_day, 559 605 AVG(rwr.avg_preparation_time_minutes) as avg_prep_time, 560 561 606 MAX(rwr.last_updated) as last_updated 562 607 FROM restaurant_weekly_report rwr 563 JOIN restaurant r ON rwr.restaurant_id = r.restaurant_id608 JOIN restaurant r ON rwr.restaurant_id = r.restaurant_id 564 609 WHERE rwr.report_week >= CURRENT_DATE - INTERVAL '21 days' 565 610 GROUP BY r.name, rwr.restaurant_id; 566 567 == Top Performing Restaurants View 611 }}} 612 {{{#!sql 613 -- Top performing restaurants view 568 614 CREATE VIEW top_restaurants_3weeks AS 569 615 SELECT … … 576 622 rta.revenue_growth_rate 577 623 FROM restaurant_weekly_report rwr 578 JOIN restaurant r ON rwr.restaurant_id = r.restaurant_id579 LEFT JOIN restaurant_trend_analysis rta ON rwr.restaurant_id = rta.restaurant_id624 JOIN restaurant r ON rwr.restaurant_id = r.restaurant_id 625 LEFT JOIN restaurant_trend_analysis rta ON rwr.restaurant_id = rta.restaurant_id 580 626 WHERE rwr.report_week >= CURRENT_DATE - INTERVAL '21 days' 581 627 GROUP BY r.name, rta.revenue_trend, rta.revenue_growth_rate 582 628 ORDER BY total_revenue DESC; 583 584 == Daily Reports Function 629 }}} 630 == Daily reports function 631 {{{#!sql 585 632 CREATE OR REPLACE FUNCTION generate_daily_breakdown_report( 586 633 p_restaurant_id INT, 587 p_week_start DATE,588 p_week_end DATE634 p_week_start DATE, 635 p_week_end DATE 589 636 ) RETURNS void 590 LANGUAGE plpgsql637 LANGUAGE plpgsql 591 638 AS $$ 592 639 DECLARE 593 v_day DATE := p_week_start;594 v_daily_orders INTEGER := 0;595 v_daily_revenue NUMERIC(12,2) := 0;640 v_day DATE := p_week_start; 641 v_daily_orders INTEGER := 0; 642 v_daily_revenue NUMERIC(12,2) := 0; 596 643 v_daily_items_sold INTEGER := 0; 597 v_avg_order_value NUMERIC(12,2) := 0;598 v_hourly_orders JSONB := '{}'::jsonb;644 v_avg_order_value NUMERIC(12,2) := 0; 645 v_hourly_orders JSONB := '{}'::jsonb; 599 646 BEGIN 600 647 IF p_week_end < p_week_start THEN … … 602 649 END IF; 603 650 651 -- Clear previous data 604 652 DELETE FROM restaurant_daily_breakdown 605 653 WHERE restaurant_id = p_restaurant_id 606 654 AND report_date BETWEEN p_week_start AND p_week_end; 607 655 656 -- Loop through days 608 657 WHILE v_day <= p_week_end LOOP 609 658 -- Daily metrics … … 622 671 INTO v_daily_items_sold 623 672 FROM order_items oi 624 JOIN orders o ON oi.order_id = o.order_id673 JOIN orders o ON oi.order_id = o.order_id 625 674 WHERE o.restaurant_id = p_restaurant_id 626 675 AND o.order_date::date = v_day … … 663 712 664 713 -- Next day 665 v_day := v_day + 1; -- date + integer yields date714 v_day := v_day + 1; 666 715 END LOOP; 667 716 END; 668 717 $$; 669 670 == Constraint Addition 718 }}} 719 {{{#!sql 671 720 ALTER TABLE restaurant_trend_analysis 672 ADD CONSTRAINT uq_restaurant_id UNIQUE (restaurant_id); 721 ADD CONSTRAINT uq_restaurant_id UNIQUE (restaurant_id); 722 }}} 723 724 725 726 727 728 729
