ZahirJS commited on
Commit
d48c4b6
·
verified ·
1 Parent(s): b453d91

Update timeline_generator.py

Browse files
Files changed (1) hide show
  1. timeline_generator.py +63 -102
timeline_generator.py CHANGED
@@ -82,118 +82,79 @@ def generate_timeline_diagram(json_input: str, output_format: str) -> str:
82
  fontcolor=base_color
83
  )
84
 
85
- # Calculate positions and create serpentine layout
86
  total_events = len(events)
87
- previous_event_id = None
88
 
89
- # Create invisible nodes for positioning and rank control
90
- rows = []
91
- current_row = []
92
-
93
- # Group events into rows
94
  for i, event in enumerate(events):
95
- current_row.append(event)
96
- if len(current_row) == events_per_row or i == total_events - 1:
97
- rows.append(current_row)
98
- current_row = []
99
-
100
- # Process each row and create serpentine connections
101
- for row_idx, row in enumerate(rows):
102
- # Determine if row should be reversed (serpentine pattern)
103
- is_reversed = row_idx % 2 == 1
104
- if is_reversed:
105
- row = row[::-1] # Reverse the row for serpentine effect
106
 
107
- # Create invisible nodes for row positioning
108
- row_nodes = []
 
 
 
 
 
 
 
109
 
110
- for event_idx, event in enumerate(row):
111
- original_idx = events.index(event)
112
- event_id = event.get('id', f'event_{original_idx}')
113
- event_label = event.get('label', f'Event {original_idx+1}')
114
- event_date = event.get('date', '')
115
- event_description = event.get('description', '')
116
-
117
- # Create full label with date and description
118
- if event_date and event_description:
119
- full_label = f"{event_date}\\n{event_label}\\n{event_description}"
120
- elif event_date:
121
- full_label = f"{event_date}\\n{event_label}"
122
- elif event_description:
123
- full_label = f"{event_label}\\n{event_description}"
124
- else:
125
- full_label = event_label
126
-
127
- # Calculate color opacity based on original position in timeline
128
- if total_events == 1:
129
- opacity = 'FF'
130
- else:
131
- opacity_value = int(255 * (1.0 - (original_idx * 0.7 / (total_events - 1))))
132
- opacity = format(opacity_value, '02x')
133
-
134
- node_color = f"{base_color}{opacity}"
135
- font_color = 'white' if original_idx < total_events * 0.7 else 'black'
136
-
137
- # Add the event node
138
- dot.node(
139
- event_id,
140
- full_label,
141
- shape='box',
142
- style='filled,rounded',
143
- fillcolor=node_color,
144
- fontcolor=font_color,
145
- fontsize='12',
146
- width='2.5',
147
- height='1.2'
148
- )
149
-
150
- row_nodes.append(event_id)
151
 
152
- # Create horizontal connections within the row
153
- for i in range(len(row_nodes) - 1):
154
- dot.edge(
155
- row_nodes[i],
156
- row_nodes[i + 1],
157
- color='#666666',
158
- arrowsize='0.8',
159
- penwidth='2'
160
- )
161
 
162
- # Connect to previous row (serpentine connection)
163
- if row_idx > 0:
164
- # Connect last node of previous row to first node of current row
165
- prev_row_nodes = getattr(generate_timeline_diagram, 'prev_row_nodes', [])
166
- if prev_row_nodes:
167
- # Connect the end of previous row to start of current row
168
- if (row_idx - 1) % 2 == 0: # Previous row was left-to-right
169
- connection_start = prev_row_nodes[-1] # Last node of previous row
170
- else: # Previous row was right-to-left
171
- connection_start = prev_row_nodes[0] # First node of previous row (which was last visually)
172
-
173
- if row_idx % 2 == 0: # Current row is left-to-right
174
- connection_end = row_nodes[0] # First node of current row
175
- else: # Current row is right-to-left
176
- connection_end = row_nodes[-1] # Last node of current row (which will be first visually)
177
-
178
- dot.edge(
179
- connection_start,
180
- connection_end,
181
- color='#666666',
182
- arrowsize='0.8',
183
- penwidth='2'
184
- )
185
 
186
- # Store current row nodes for next iteration
187
- generate_timeline_diagram.prev_row_nodes = row_nodes
 
 
 
188
 
189
- # Connect title to first event if title exists and this is the first row
190
- if title and row_idx == 0:
191
- first_event = row_nodes[0] if row_idx % 2 == 0 else row_nodes[-1]
192
- dot.edge('title', first_event, style='invis')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
193
 
194
- # Clean up the stored attribute
195
- if hasattr(generate_timeline_diagram, 'prev_row_nodes'):
196
- delattr(generate_timeline_diagram, 'prev_row_nodes')
197
 
198
  with NamedTemporaryFile(delete=False, suffix=f'.{output_format}') as tmp:
199
  dot.render(tmp.name, format=output_format, cleanup=True)
 
82
  fontcolor=base_color
83
  )
84
 
 
85
  total_events = len(events)
 
86
 
87
+ # Create all event nodes first
 
 
 
 
88
  for i, event in enumerate(events):
89
+ event_id = event.get('id', f'event_{i}')
90
+ event_label = event.get('label', f'Event {i+1}')
91
+ event_date = event.get('date', '')
92
+ event_description = event.get('description', '')
 
 
 
 
 
 
 
93
 
94
+ # Create full label with date and description
95
+ if event_date and event_description:
96
+ full_label = f"{event_date}\\n{event_label}\\n{event_description}"
97
+ elif event_date:
98
+ full_label = f"{event_date}\\n{event_label}"
99
+ elif event_description:
100
+ full_label = f"{event_label}\\n{event_description}"
101
+ else:
102
+ full_label = event_label
103
 
104
+ # Calculate color opacity based on position in timeline
105
+ if total_events == 1:
106
+ opacity = 'FF'
107
+ else:
108
+ opacity_value = int(255 * (1.0 - (i * 0.7 / (total_events - 1))))
109
+ opacity = format(opacity_value, '02x')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
110
 
111
+ node_color = f"{base_color}{opacity}"
112
+ font_color = 'white' if i < total_events * 0.7 else 'black'
 
 
 
 
 
 
 
113
 
114
+ # Calculate position for serpentine layout
115
+ row = i // events_per_row
116
+ col = i % events_per_row
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
117
 
118
+ # For odd rows, reverse the column position to create serpentine effect
119
+ if row % 2 == 1:
120
+ visual_col = events_per_row - 1 - col
121
+ else:
122
+ visual_col = col
123
 
124
+ # Add the event node with position attributes for layout
125
+ dot.node(
126
+ event_id,
127
+ full_label,
128
+ shape='box',
129
+ style='filled,rounded',
130
+ fillcolor=node_color,
131
+ fontcolor=font_color,
132
+ fontsize='12',
133
+ width='2.5',
134
+ height='1.2',
135
+ pos=f"{visual_col * 3},{-row * 2}!" # Fixed positions for serpentine layout
136
+ )
137
+
138
+ # Connect events in chronological order (1→2→3→4...)
139
+ for i in range(len(events) - 1):
140
+ current_event_id = events[i].get('id', f'event_{i}')
141
+ next_event_id = events[i + 1].get('id', f'event_{i + 1}')
142
+
143
+ dot.edge(
144
+ current_event_id,
145
+ next_event_id,
146
+ color='#666666',
147
+ arrowsize='0.8',
148
+ penwidth='2'
149
+ )
150
+
151
+ # Connect title to first event if title exists
152
+ if title:
153
+ first_event_id = events[0].get('id', 'event_0')
154
+ dot.edge('title', first_event_id, style='invis')
155
 
156
+ # Set the layout engine to handle fixed positions
157
+ dot.engine = 'neato'
 
158
 
159
  with NamedTemporaryFile(delete=False, suffix=f'.{output_format}') as tmp:
160
  dot.render(tmp.name, format=output_format, cleanup=True)