Skip to content

Commit 9113582

Browse files
authored
Merge pull request #10 from CompEng0001/list-progress-fix
fix: issue where list and progress bar not updating as intended
2 parents 7743c85 + f792911 commit 9113582

File tree

3 files changed

+74
-34
lines changed

3 files changed

+74
-34
lines changed

pylings/exercises.py

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ def _initialize_exercises(self):
5858

5959
self.current_exercise = EXERCISES_DIR / self.config_manager.get_lasttime_exercise()
6060
self.current_exercise_state = self.exercises[self.current_exercise.name]["status"]
61+
self.completed_count = sum(1 for ex in self.exercises.values() if ex["status"] == "DONE")
6162

6263
def _evaluate_exercises_ordered(self, exercise_paths):
6364
"""Runs each exercise and returns results in original order.
@@ -100,16 +101,15 @@ def _update_exercise_status(self, name, result):
100101
result (CompletedProcess): The result of executing the file.
101102
102103
Returns:
103-
tuple[str, str]: Previous and new status values.
104+
str: new status values.
104105
"""
105-
prev_status = self.exercises[name]["status"]
106106
new_status = "DONE" if result.returncode == 0 else "PENDING"
107107
self.exercises[name].update({
108108
"status": new_status,
109109
"output": self._format_output(result.stdout) if result.returncode == 0 else "",
110110
"error": self._format_output(result.stderr) if result.returncode != 0 else None
111111
})
112-
return prev_status, new_status
112+
return new_status
113113

114114
def run_exercise(self, path: Path, source: str = "workspace"):
115115
"""Runs a Python exercise file and returns the result.
@@ -183,16 +183,17 @@ def update_exercise_output(self):
183183

184184
result = self.run_exercise(self.current_exercise)
185185
name = self.current_exercise.name
186-
prev_status, new_status = self._update_exercise_status(name, result)
186+
new_status = self._update_exercise_status(name, result)
187187
self.current_exercise_state = new_status
188188

189-
if prev_status != "DONE" and new_status == "DONE":
190-
self.completed_count += 1
191-
elif prev_status == "DONE" and new_status != "DONE":
192-
self.completed_count -= 1
193-
194-
self.completed_count = max(0, min(self.completed_count, len(self.exercises)))
189+
#if prev_status != "DONE" and new_status == "DONE":
190+
# self.completed_count += 1
191+
#elif prev_status == "DONE" and new_status != "DONE":
192+
# self.completed_count -= 1
193+
self.completed_count = sum(1 for ex in self.exercises.values() if ex["status"] == "DONE")
195194

195+
#self.completed_count = max(0, min(self.completed_count, len(self.exercises)))
196+
log.debug(f"ExerciseManager.update_exercise_output.self.completed_count: ${self.completed_count}")
196197
if self.completed_count == len(self.exercises) and not self.completed_flag:
197198
print(FINISHED)
198199
self.completed_flag = True
@@ -216,6 +217,7 @@ def check_all_exercises(self, progress_callback=None):
216217
progress_callback(path.name, i, len(results))
217218

218219
self.completed_count = sum(1 for ex in self.exercises.values() if ex["status"] == "DONE")
220+
log.debug(f"ExerciseManager.check_all_exercises.self.completed_count: ${self.completed_count}")
219221
self.current_exercise = current_exercise_path
220222

221223
def next_exercise(self):

pylings/ui.py

Lines changed: 61 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -184,16 +184,32 @@ def update_progress_bar(self):
184184
completed_exercises = self.exercise_manager.completed_count
185185

186186
bar_length = 55
187-
progress_fraction = completed_exercises / total_exercises if total_exercises > 0 else 0
187+
if total_exercises > 0:
188+
progress_fraction = completed_exercises / total_exercises
189+
else:
190+
progress_fraction = 0
191+
log.debug("PylingsUI.update_progress_bar.progress_fraction: %s", progress_fraction)
188192

189193
filled = int(progress_fraction * bar_length)
190-
remaining = bar_length - filled - 1
194+
if filled > bar_length:
195+
filled = bar_length
191196

192197
progress_bar = Text("Progress: [", style="bold")
193-
progress_bar.append("#" * filled, style="green")
194-
progress_bar.append(">", style="green")
195-
progress_bar.append("-" * remaining, style="red")
196-
progress_bar.append(f"] {completed_exercises}/{total_exercises}", style="bold")
198+
199+
# Green part
200+
if filled > 0:
201+
progress_bar.append("#" * filled, style="green")
202+
203+
# Arrow if not full
204+
if filled < bar_length:
205+
progress_bar.append(">", style="green")
206+
207+
# Remaining red part
208+
remaining = bar_length - filled - 1
209+
if remaining > 0:
210+
progress_bar.append("-" * remaining, style="red")
211+
212+
progress_bar.append(f"] {completed_exercises:>3}/{total_exercises:>3}", style="bold")
197213
log.debug("PylingsUI.update_progress_bar.progress_bar: %s", progress_bar)
198214
progress_bar_widget.update(progress_bar)
199215

@@ -258,7 +274,14 @@ def toggle_list_view(self):
258274
log.debug("PylingsUI.toggle_list_view.sidebar.selected_index: %s",selected_index)
259275

260276
def update_exercise_content(self):
261-
"""Update displayed exercise details, refresh output, and efficiently update the list."""
277+
"""Update exercise rows in the list view.
278+
279+
If no index is supplied, all rows will be updated. This ensures that the display
280+
text for each exercise reflects its current status.
281+
282+
Args:
283+
index (int | None): The index of the exercise to update. If None, all rows are updated.
284+
"""
262285
log.debug("PylingsUI.update_exercise_content: Entered")
263286

264287
exercise_path = self.current_exercise or "No exercise selected"
@@ -269,42 +292,56 @@ def update_exercise_content(self):
269292

270293
list_view = self.query_one("#exercise-list", ListView)
271294
selected_index = list_view.index or 0
272-
self.update_list_row(selected_index)
295+
self.update_list_row()
273296
self.restore_list_selection(selected_index)
274-
275-
# Show completion notice briefly
276-
#self.finished_check_progress_notice(clear=False)
277-
#self.set_timer(2.0, lambda: self.finished_check_progress_notice(clear=True))
278297
self.update_progress_bar()
279298

280-
def update_list_row(self, index: int):
281-
"""Update the display text for the exercise at the given index in the list.
299+
def update_list_row(self, index: int | None = None):
300+
"""Update the display text for a single exercise row at the given index.
282301
283-
If the exercise status has changed, the line is updated accordingly.
302+
The display text is updated only if it differs from the current display.
303+
The row text is formatted as "<STATUS> <EXERCISE_NAME>".
284304
285305
Args:
286-
index (int): Index of the exercise to update.
306+
idx (int): The index of the list view row to update.
307+
name (str): The name of the exercise corresponding to the row.
308+
list_view (ListView): The ListView widget containing the exercise rows.
309+
exercise_keys (list): The list of exercise names in order.
287310
"""
288311
list_view = self.query_one("#exercise-list", ListView)
289312
exercise_keys = list(self.exercise_manager.exercises.keys())
290313

291-
if 0 <= index < len(exercise_keys):
314+
if index is not None:
315+
if not (0 <= index < len(exercise_keys)):
316+
log.warning(f"update_list_row: supplied index {index} out of range")
317+
return
292318
name = exercise_keys[index]
293-
if self.exercise_manager.exercises[name]["status"] == "DONE":
294-
new_status = DONE
295-
else:
296-
new_status = PENDING
319+
log.debug(f"update_list_row: using supplied index {index}, name: {name}")
320+
self._update_list_row_at(index, name, list_view, exercise_keys)
321+
else:
322+
log.debug("update_list_row: updating entire list")
323+
for idx, name in enumerate(exercise_keys):
324+
self._update_list_row_at(idx, name, list_view, exercise_keys)
297325

298-
new_display = f"{new_status} {name}"
299326

300-
list_item = list_view.children[index]
327+
def _update_list_row_at(self, idx: int, name: str, list_view, exercise_keys):
328+
exercise_data = self.exercise_manager.exercises[name]
329+
new_status = DONE if exercise_data["status"] == "DONE" else PENDING
330+
new_display = f"{new_status} {name}"
331+
332+
if 0 <= idx < len(list_view.children):
333+
list_item = list_view.children[idx]
301334
static_widget = list_item.query_one(Static)
302335
renderable = static_widget.renderable
303336
current_display = renderable.plain if hasattr(renderable, 'plain') else str(renderable)
304337

305338
if current_display != new_display:
306-
log.debug("Updating line %s: %s -> %s",index,current_display,new_display)
339+
log.debug("Updating line %s: %s -> %s", idx, current_display, new_display)
307340
static_widget.update(new_display)
341+
else:
342+
log.warning(f"update_list_row: index {idx} out of range of list_view.children")
343+
344+
308345

309346
def restore_list_selection(self, index: int):
310347
"""Restore focus and visibility for the given list item index.

pylings/watcher.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ def restart(self, new_exercise_path: str | Path):
7272
self.stop()
7373
log.debug("Watcher.restart.new_exercise_path: %s",new_exercise_path)
7474
self.start(new_exercise_path)
75+
log.debug(f"Watcher.restart.start: ${new_exercise_path}")
7576
log.debug("Watcher.restart: started")
7677

7778
class ChangeHandler(FileSystemEventHandler):

0 commit comments

Comments
 (0)