dbwatcher 1.1.2 → 1.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/app/assets/config/dbwatcher_manifest.js +1 -0
- data/app/assets/javascripts/dbwatcher/components/changes_table_hybrid.js +184 -97
- data/app/assets/javascripts/dbwatcher/components/timeline.js +211 -0
- data/app/assets/javascripts/dbwatcher/dbwatcher.js +5 -0
- data/app/assets/stylesheets/dbwatcher/application.css +298 -1
- data/app/assets/stylesheets/dbwatcher/application.scss +1 -0
- data/app/assets/stylesheets/dbwatcher/components/_timeline.scss +326 -0
- data/app/controllers/dbwatcher/api/v1/sessions_controller.rb +18 -4
- data/app/controllers/dbwatcher/sessions_controller.rb +1 -1
- data/app/views/dbwatcher/sessions/_layout.html.erb +5 -2
- data/app/views/dbwatcher/sessions/_summary.html.erb +1 -1
- data/app/views/dbwatcher/sessions/{_changes.html.erb → _tables.html.erb} +84 -5
- data/app/views/dbwatcher/sessions/_timeline.html.erb +260 -0
- data/app/views/dbwatcher/sessions/show.html.erb +3 -1
- data/app/views/layouts/dbwatcher/application.html.erb +1 -0
- data/config/routes.rb +2 -1
- data/lib/dbwatcher/services/analyzers/table_summary_builder.rb +102 -1
- data/lib/dbwatcher/services/api/{changes_data_service.rb → tables_data_service.rb} +6 -6
- data/lib/dbwatcher/services/timeline_data_service/enhancement_utilities.rb +100 -0
- data/lib/dbwatcher/services/timeline_data_service/entry_builder.rb +125 -0
- data/lib/dbwatcher/services/timeline_data_service/metadata_builder.rb +93 -0
- data/lib/dbwatcher/services/timeline_data_service.rb +130 -0
- data/lib/dbwatcher/storage/api/concerns/table_analyzer.rb +1 -1
- data/lib/dbwatcher/version.rb +1 -1
- data/lib/dbwatcher.rb +1 -1
- metadata +11 -4
@@ -0,0 +1,93 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Dbwatcher
|
4
|
+
module Services
|
5
|
+
class TimelineDataService
|
6
|
+
# Module for building timeline metadata
|
7
|
+
module MetadataBuilder
|
8
|
+
private
|
9
|
+
|
10
|
+
# Build timeline metadata
|
11
|
+
#
|
12
|
+
# @return [Hash] timeline metadata
|
13
|
+
def build_timeline_metadata
|
14
|
+
return {} if @timeline_entries.empty?
|
15
|
+
|
16
|
+
{
|
17
|
+
total_operations: @timeline_entries.length,
|
18
|
+
time_range: calculate_time_range,
|
19
|
+
session_duration: calculate_session_duration,
|
20
|
+
tables_affected: extract_affected_tables,
|
21
|
+
operation_counts: count_operations_by_type,
|
22
|
+
peak_activity_periods: find_peak_activity_periods
|
23
|
+
}
|
24
|
+
end
|
25
|
+
|
26
|
+
# Calculate time range for the session
|
27
|
+
#
|
28
|
+
# @return [Hash] time range with start and end
|
29
|
+
def calculate_time_range
|
30
|
+
return {} if @timeline_entries.empty?
|
31
|
+
|
32
|
+
start_time = Time.at(@timeline_entries.first[:raw_timestamp])
|
33
|
+
end_time = Time.at(@timeline_entries.last[:raw_timestamp])
|
34
|
+
|
35
|
+
{
|
36
|
+
start: start_time.iso8601,
|
37
|
+
end: end_time.iso8601
|
38
|
+
}
|
39
|
+
end
|
40
|
+
|
41
|
+
# Calculate total session duration
|
42
|
+
#
|
43
|
+
# @return [String] formatted session duration
|
44
|
+
def calculate_session_duration
|
45
|
+
return "00:00" if @timeline_entries.length < 2
|
46
|
+
|
47
|
+
duration = @timeline_entries.last[:raw_timestamp] - @timeline_entries.first[:raw_timestamp]
|
48
|
+
format_duration(duration)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Extract list of affected tables
|
52
|
+
#
|
53
|
+
# @return [Array<String>] unique table names
|
54
|
+
def extract_affected_tables
|
55
|
+
@timeline_entries.map { |entry| entry[:table_name] }.uniq.sort
|
56
|
+
end
|
57
|
+
|
58
|
+
# Count operations by type
|
59
|
+
#
|
60
|
+
# @return [Hash] operation counts
|
61
|
+
def count_operations_by_type
|
62
|
+
@timeline_entries.group_by { |entry| entry[:operation] }
|
63
|
+
.transform_values(&:count)
|
64
|
+
end
|
65
|
+
|
66
|
+
# Find peak activity periods
|
67
|
+
#
|
68
|
+
# @return [Array<Hash>] peak activity periods
|
69
|
+
def find_peak_activity_periods
|
70
|
+
return [] if @timeline_entries.length < 10
|
71
|
+
|
72
|
+
# Group operations by 1-minute windows
|
73
|
+
windows = @timeline_entries.group_by do |entry|
|
74
|
+
timestamp = Time.at(entry[:raw_timestamp])
|
75
|
+
Time.new(timestamp.year, timestamp.month, timestamp.day, timestamp.hour, timestamp.min, 0)
|
76
|
+
end
|
77
|
+
|
78
|
+
# Find windows with more than average activity
|
79
|
+
average_ops = @timeline_entries.length / windows.length.to_f
|
80
|
+
|
81
|
+
windows.select { |_, ops| ops.length > average_ops * 1.5 }
|
82
|
+
.map do |window_start, ops|
|
83
|
+
{
|
84
|
+
start: window_start.iso8601,
|
85
|
+
end: (window_start + 1.minute).iso8601,
|
86
|
+
operations_count: ops.length
|
87
|
+
}
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "timeline_data_service/metadata_builder"
|
4
|
+
require_relative "timeline_data_service/entry_builder"
|
5
|
+
require_relative "timeline_data_service/enhancement_utilities"
|
6
|
+
|
7
|
+
module Dbwatcher
|
8
|
+
module Services
|
9
|
+
# Timeline Data Service for processing session data into chronological timeline format
|
10
|
+
#
|
11
|
+
# This service transforms session changes into a chronologically ordered timeline
|
12
|
+
# with enhanced metadata for visualization and filtering.
|
13
|
+
#
|
14
|
+
# @example
|
15
|
+
# service = TimelineDataService.new(session)
|
16
|
+
# result = service.call
|
17
|
+
# timeline = result[:timeline]
|
18
|
+
# metadata = result[:metadata]
|
19
|
+
class TimelineDataService
|
20
|
+
include MetadataBuilder
|
21
|
+
include EntryBuilder
|
22
|
+
include EnhancementUtilities
|
23
|
+
# Initialize the timeline data service
|
24
|
+
#
|
25
|
+
# @param session [Session] session object containing changes data
|
26
|
+
def initialize(session)
|
27
|
+
@session = session
|
28
|
+
@timeline_entries = []
|
29
|
+
@start_time = Time.current
|
30
|
+
end
|
31
|
+
|
32
|
+
# Process session data into timeline format
|
33
|
+
#
|
34
|
+
# @return [Hash] processed timeline data with metadata
|
35
|
+
def call
|
36
|
+
Rails.logger.info("Processing timeline data for session #{@session.id}")
|
37
|
+
|
38
|
+
validate_session_data
|
39
|
+
build_timeline_entries
|
40
|
+
sort_chronologically
|
41
|
+
enhance_with_metadata
|
42
|
+
result = build_result
|
43
|
+
|
44
|
+
Rails.logger.info(
|
45
|
+
"Timeline processing completed for session #{@session.id} (#{@timeline_entries.length} entries)"
|
46
|
+
)
|
47
|
+
|
48
|
+
result
|
49
|
+
rescue StandardError => e
|
50
|
+
Rails.logger.error("Timeline processing failed for session #{@session.id}: #{e.message}")
|
51
|
+
build_error_result(e)
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
# Validate session data before processing
|
57
|
+
#
|
58
|
+
# @raise [ArgumentError] if session data is invalid
|
59
|
+
def validate_session_data
|
60
|
+
raise ArgumentError, "Session is required" unless @session
|
61
|
+
raise ArgumentError, "Session ID is required" unless @session.id
|
62
|
+
raise ArgumentError, "Session changes are required" unless @session.changes
|
63
|
+
end
|
64
|
+
|
65
|
+
# Build timeline entries from session changes
|
66
|
+
#
|
67
|
+
# @return [void]
|
68
|
+
def build_timeline_entries
|
69
|
+
@session.changes.each_with_index do |change, index|
|
70
|
+
next unless valid_change?(change)
|
71
|
+
|
72
|
+
@timeline_entries << create_timeline_entry(change, index)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# Check if a change is valid for timeline processing
|
77
|
+
#
|
78
|
+
# @param change [Hash] change data
|
79
|
+
# @return [Boolean] true if change is valid
|
80
|
+
def valid_change?(change)
|
81
|
+
change.is_a?(Hash) &&
|
82
|
+
change[:table_name] &&
|
83
|
+
change[:operation] &&
|
84
|
+
change[:timestamp]
|
85
|
+
end
|
86
|
+
|
87
|
+
# Sort timeline entries chronologically
|
88
|
+
#
|
89
|
+
# @return [void]
|
90
|
+
def sort_chronologically
|
91
|
+
@timeline_entries.sort_by! { |entry| entry[:raw_timestamp] }
|
92
|
+
end
|
93
|
+
|
94
|
+
# Build final result hash
|
95
|
+
#
|
96
|
+
# @return [Hash] complete timeline result
|
97
|
+
def build_result
|
98
|
+
{
|
99
|
+
timeline: @timeline_entries,
|
100
|
+
metadata: build_timeline_metadata,
|
101
|
+
summary: build_timeline_summary,
|
102
|
+
errors: []
|
103
|
+
}
|
104
|
+
end
|
105
|
+
|
106
|
+
# Build timeline summary
|
107
|
+
#
|
108
|
+
# @return [Hash] timeline summary
|
109
|
+
def build_timeline_summary
|
110
|
+
{
|
111
|
+
total_entries: @timeline_entries.length,
|
112
|
+
processing_time: (Time.current - @start_time).round(3)
|
113
|
+
}
|
114
|
+
end
|
115
|
+
|
116
|
+
# Build error result
|
117
|
+
#
|
118
|
+
# @param error [StandardError] error that occurred
|
119
|
+
# @return [Hash] error result
|
120
|
+
def build_error_result(error)
|
121
|
+
{
|
122
|
+
timeline: [],
|
123
|
+
metadata: {},
|
124
|
+
summary: { error: error.message },
|
125
|
+
errors: [{ type: "processing_error", message: error.message }]
|
126
|
+
}
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
@@ -24,7 +24,7 @@ module Dbwatcher
|
|
24
24
|
# @return [Hash] tables summary hash
|
25
25
|
def build_tables_summary(session)
|
26
26
|
# Delegate to new service while maintaining interface compatibility
|
27
|
-
Dbwatcher::Services::Analyzers::TableSummaryBuilder.
|
27
|
+
Dbwatcher::Services::Analyzers::TableSummaryBuilder.new(session).call
|
28
28
|
end
|
29
29
|
|
30
30
|
# Process all changes in a session (legacy method for backward compatibility)
|
data/lib/dbwatcher/version.rb
CHANGED
data/lib/dbwatcher.rb
CHANGED
@@ -74,7 +74,7 @@ require_relative "dbwatcher/services/diagram_system"
|
|
74
74
|
|
75
75
|
# API services
|
76
76
|
require_relative "dbwatcher/services/api/base_api_service"
|
77
|
-
require_relative "dbwatcher/services/api/
|
77
|
+
require_relative "dbwatcher/services/api/tables_data_service"
|
78
78
|
require_relative "dbwatcher/services/api/summary_data_service"
|
79
79
|
require_relative "dbwatcher/services/api/diagram_data_service"
|
80
80
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dbwatcher
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.1.
|
4
|
+
version: 1.1.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Huy Nguyen
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-07-
|
11
|
+
date: 2025-07-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -172,6 +172,7 @@ files:
|
|
172
172
|
- app/assets/javascripts/dbwatcher/components/dashboard.js
|
173
173
|
- app/assets/javascripts/dbwatcher/components/diagrams.js
|
174
174
|
- app/assets/javascripts/dbwatcher/components/summary.js
|
175
|
+
- app/assets/javascripts/dbwatcher/components/timeline.js
|
175
176
|
- app/assets/javascripts/dbwatcher/core/alpine_store.js
|
176
177
|
- app/assets/javascripts/dbwatcher/core/api_client.js
|
177
178
|
- app/assets/javascripts/dbwatcher/core/component_loader.js
|
@@ -190,6 +191,7 @@ files:
|
|
190
191
|
- app/assets/stylesheets/dbwatcher/components/_forms.scss
|
191
192
|
- app/assets/stylesheets/dbwatcher/components/_navigation.scss
|
192
193
|
- app/assets/stylesheets/dbwatcher/components/_tabulator.scss
|
194
|
+
- app/assets/stylesheets/dbwatcher/components/_timeline.scss
|
193
195
|
- app/assets/stylesheets/dbwatcher/core/_base.scss
|
194
196
|
- app/assets/stylesheets/dbwatcher/core/_variables.scss
|
195
197
|
- app/assets/stylesheets/dbwatcher/vendor/_tabulator_overrides.scss
|
@@ -212,11 +214,12 @@ files:
|
|
212
214
|
- app/views/dbwatcher/dashboard/_system_info_content.html.erb
|
213
215
|
- app/views/dbwatcher/dashboard/index.html.erb
|
214
216
|
- app/views/dbwatcher/queries/index.html.erb
|
215
|
-
- app/views/dbwatcher/sessions/_changes.html.erb
|
216
217
|
- app/views/dbwatcher/sessions/_diagrams.html.erb
|
217
218
|
- app/views/dbwatcher/sessions/_layout.html.erb
|
218
219
|
- app/views/dbwatcher/sessions/_session_header.html.erb
|
219
220
|
- app/views/dbwatcher/sessions/_summary.html.erb
|
221
|
+
- app/views/dbwatcher/sessions/_tables.html.erb
|
222
|
+
- app/views/dbwatcher/sessions/_timeline.html.erb
|
220
223
|
- app/views/dbwatcher/sessions/index.html.erb
|
221
224
|
- app/views/dbwatcher/sessions/show.html.erb
|
222
225
|
- app/views/dbwatcher/shared/_badge.html.erb
|
@@ -244,9 +247,9 @@ files:
|
|
244
247
|
- lib/dbwatcher/services/analyzers/session_data_processor.rb
|
245
248
|
- lib/dbwatcher/services/analyzers/table_summary_builder.rb
|
246
249
|
- lib/dbwatcher/services/api/base_api_service.rb
|
247
|
-
- lib/dbwatcher/services/api/changes_data_service.rb
|
248
250
|
- lib/dbwatcher/services/api/diagram_data_service.rb
|
249
251
|
- lib/dbwatcher/services/api/summary_data_service.rb
|
252
|
+
- lib/dbwatcher/services/api/tables_data_service.rb
|
250
253
|
- lib/dbwatcher/services/base_service.rb
|
251
254
|
- lib/dbwatcher/services/dashboard_data_aggregator.rb
|
252
255
|
- lib/dbwatcher/services/diagram_analyzers/base_analyzer.rb
|
@@ -281,6 +284,10 @@ files:
|
|
281
284
|
- lib/dbwatcher/services/system_info/runtime_info_collector.rb
|
282
285
|
- lib/dbwatcher/services/system_info/system_info_collector.rb
|
283
286
|
- lib/dbwatcher/services/table_statistics_collector.rb
|
287
|
+
- lib/dbwatcher/services/timeline_data_service.rb
|
288
|
+
- lib/dbwatcher/services/timeline_data_service/enhancement_utilities.rb
|
289
|
+
- lib/dbwatcher/services/timeline_data_service/entry_builder.rb
|
290
|
+
- lib/dbwatcher/services/timeline_data_service/metadata_builder.rb
|
284
291
|
- lib/dbwatcher/sql_logger.rb
|
285
292
|
- lib/dbwatcher/storage.rb
|
286
293
|
- lib/dbwatcher/storage/api/base_api.rb
|