diff --git a/models/marts/fact_bookings.sql b/models/marts/fact_bookings.sql new file mode 100644 index 0000000..fe83b21 --- /dev/null +++ b/models/marts/fact_bookings.sql @@ -0,0 +1,39 @@ +with bookings as ( + + select + booking_id, + member_id, + class_id, + booking_timestamp, + attendance_status, + check_in_timestamp, + cancellation_timestamp + from {{ ref('stg_bookings') }} + +), + +final as ( + + select + booking_id, + member_id, + class_id, + + booking_timestamp, + date(booking_timestamp) as booking_date, + + attendance_status, + + case when attendance_status = 'attended' then true else false end as is_attended, + case when attendance_status = 'cancelled' then true else false end as is_cancelled, + case when attendance_status = 'no_show' then true else false end as is_no_show, + + check_in_timestamp, + cancellation_timestamp + + from bookings + +) + +select * +from final \ No newline at end of file diff --git a/models/marts/fact_bookings.yml b/models/marts/fact_bookings.yml new file mode 100644 index 0000000..3899938 --- /dev/null +++ b/models/marts/fact_bookings.yml @@ -0,0 +1,97 @@ +version: 2 + +models: + - name: fact_bookings + description: Final fact table for class bookings with one row per booking. + + columns: + - name: booking_id + description: Unique identifier for each booking. + tests: + - not_null + - unique + + - name: member_id + description: Identifier for the member who made the booking. + tests: + - not_null + - relationships: + arguments: + to: ref('dim_members') + field: member_id + + - name: class_id + description: Identifier for the class that was booked. + tests: + - not_null + + - name: booking_timestamp + description: Timestamp of when the booking was made. + tests: + - not_null + + - name: booking_date + description: Date when the booking was made. + tests: + - not_null + + - name: attendance_status + description: Cleaned attendance status for the booking. + tests: + - not_null + - accepted_values: + arguments: + values: ['attended', 'cancelled', 'no_show'] + + - name: is_attended + description: Flag indicating whether the booking was attended. + tests: + - not_null + - accepted_values: + arguments: + values: [true, false] + quote: false + + - name: is_cancelled + description: Flag indicating whether the booking was cancelled. + tests: + - not_null + - accepted_values: + arguments: + values: [true, false] + quote: false + + - name: is_no_show + description: Flag indicating whether the booking was a no-show. + tests: + - not_null + - accepted_values: + arguments: + values: [true, false] + quote: false + + - name: check_in_timestamp + description: Timestamp when the member checked in, if they attended. + + - name: cancellation_timestamp + description: Timestamp when the booking was cancelled, if cancelled. + +unit_tests: + - name: test_fact_bookings_attendance_flags + description: Test that the attendance_status is consistent with the attendance flags. + model: fact_bookings + + given: + - input: ref('stg_bookings') + rows: + - {booking_id: 1, member_id: 101, class_id: 1001, booking_timestamp: "timestamp('2025-01-10 09:00:00')", attendance_status: 'attended', check_in_timestamp: "timestamp('2025-01-10 09:55:00')", cancellation_timestamp: null} + - {booking_id: 2, member_id: 102, class_id: 1002, booking_timestamp: "timestamp('2025-01-11 10:00:00')", attendance_status: 'cancelled', check_in_timestamp: null, cancellation_timestamp: "timestamp('2025-01-10 15:00:00')"} + - {booking_id: 3, member_id: 103, class_id: 1003, booking_timestamp: "timestamp('2025-01-12 11:00:00')", attendance_status: 'no_show', check_in_timestamp: null, cancellation_timestamp: null} + - {booking_id: 4, member_id: 104, class_id: 1004, booking_timestamp: "timestamp('2025-01-13 12:00:00')", attendance_status: null, check_in_timestamp: null, cancellation_timestamp: null} + + expect: + rows: + - {booking_id: 1, member_id: 101, class_id: 1001, booking_timestamp: "timestamp('2025-01-10 09:00:00')", booking_date: "date('2025-01-10')", attendance_status: 'attended', is_attended: true, is_cancelled: false, is_no_show: false, check_in_timestamp: "timestamp('2025-01-10 09:55:00')", cancellation_timestamp: null} + - {booking_id: 2, member_id: 102, class_id: 1002, booking_timestamp: "timestamp('2025-01-11 10:00:00')", booking_date: "date('2025-01-11')", attendance_status: 'cancelled', is_attended: false, is_cancelled: true, is_no_show: false, check_in_timestamp: null, cancellation_timestamp: "timestamp('2025-01-10 15:00:00')"} + - {booking_id: 3, member_id: 103, class_id: 1003, booking_timestamp: "timestamp('2025-01-12 11:00:00')", booking_date: "date('2025-01-12')", attendance_status: 'no_show', is_attended: false, is_cancelled: false, is_no_show: true, check_in_timestamp: null, cancellation_timestamp: null} + - {booking_id: 4, member_id: 104, class_id: 1004, booking_timestamp: "timestamp('2025-01-13 12:00:00')", booking_date: "date('2025-01-13')", attendance_status: null, is_attended: false, is_cancelled: false, is_no_show: false, check_in_timestamp: null, cancellation_timestamp: null} diff --git a/tests/singular_tests/fact_bookings_status_flags_consistent.sql b/tests/singular_tests/fact_bookings_status_flags_consistent.sql new file mode 100644 index 0000000..370da4c --- /dev/null +++ b/tests/singular_tests/fact_bookings_status_flags_consistent.sql @@ -0,0 +1,9 @@ +select * +from {{ ref('fact_bookings') }} +where + (attendance_status = 'attended' and (is_attended = false or is_cancelled = true or is_no_show = true)) + or + (attendance_status = 'cancelled' and (is_attended = true or is_cancelled = false or is_no_show = true)) + or + (attendance_status = 'no_show' and (is_attended = true or is_cancelled = true or is_no_show = false)) +