Repository navigation
Expand file tree
/
Copy pathstart_course.py
More file actions
348 lines (296 loc) · 12.7 KB
/
Copy pathstart_course.py
File metadata and controls
348 lines (296 loc) · 12.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
# -*- coding: utf-8 -*-
import os
import sys
import json
import subprocess
import argparse
# Importujemy dane kursu
try:
from course_content import COURSE_DATA
except ImportError:
print("[BŁĄD] Nie można zaimportować course_content.py. Upewnij się, że plik znajduje się w tym samym katalogu.")
sys.exit(1)
STATE_FILE = ".agents/course_state.json"
LOG_FILE = ".agents/test_run.log"
def load_state():
if not os.path.exists(STATE_FILE):
os.makedirs(os.path.dirname(STATE_FILE), exist_ok=True)
default_state = {
"current_module": 1,
"current_task": 1,
"completed_tasks": []
}
save_state(default_state)
return default_state
try:
with open(STATE_FILE, 'r', encoding='utf-8') as f:
return json.load(f)
except Exception:
# Awaryjny reset w razie uszkodzenia JSON
return {
"current_module": 1,
"current_task": 1,
"completed_tasks": []
}
def save_state(state):
os.makedirs(os.path.dirname(STATE_FILE), exist_ok=True)
with open(STATE_FILE, 'w', encoding='utf-8') as f:
json.dump(state, f, indent=4, ensure_ascii=False)
def check_dependencies():
missing = []
for lib in ["pandas", "numpy", "pytest"]:
try:
__import__(lib)
except ImportError:
missing.append(lib)
if missing:
print("\n\033[91m[UWAGA] Brakujące biblioteki wymagane do kursu: {}\033[0m".format(", ".join(missing)))
print("Uruchom poniższe polecenie w terminalu, aby zainstalować zależności:")
print(" \033[93mpip install -r requirements.txt\033[0m\n")
return False
return True
def get_next_task(module, task):
# Znajdź następne zadanie w strukturze COURSE_DATA
if module not in COURSE_DATA:
return None, None
tasks = sorted(COURSE_DATA[module]["tasks"].keys())
current_idx = tasks.index(task)
if current_idx + 1 < len(tasks):
return module, tasks[current_idx + 1]
# Przejdź do kolejnego modułu
modules = sorted(COURSE_DATA.keys())
current_mod_idx = modules.index(module)
if current_mod_idx + 1 < len(modules):
next_mod = modules[current_mod_idx + 1]
next_tasks = sorted(COURSE_DATA[next_mod]["tasks"].keys())
if next_tasks:
return next_mod, next_tasks[0]
return None, None
def ensure_exercise_exists(state):
mod = state["current_module"]
task = state["current_task"]
if mod in COURSE_DATA and task in COURSE_DATA[mod]["tasks"]:
task_data = COURSE_DATA[mod]["tasks"][task]
ex_path = task_data["exercise_path"]
if not os.path.exists(ex_path):
os.makedirs(os.path.dirname(ex_path), exist_ok=True)
with open(ex_path, 'w', encoding='utf-8') as f:
f.write(task_data["template"])
print("\033[94m[INFO] Wygenerowano szablon zadania w: {}\033[0m".format(ex_path))
def display_lesson(state):
mod = state["current_module"]
task = state["current_task"]
if mod not in COURSE_DATA or task not in COURSE_DATA[mod]["tasks"]:
print("\n\033[92m[GRATULACJE] Ukończyłeś wszystkie dostępne zadania w kursie!\033[0m")
display_git_instructions()
return
module_title = COURSE_DATA[mod]["title"]
task_data = COURSE_DATA[mod]["tasks"][task]
task_title = task_data["title"]
lesson_text = task_data["lesson"]
ex_path = task_data["exercise_path"]
ensure_exercise_exists(state)
print("\n" + "=" * 60)
print("\033[96m{}\033[0m".format(module_title))
print("\033[93mZadanie {}.{}: {}\033[0m".format(mod, task, task_title))
print("=" * 60)
print(lesson_text.strip())
print("-" * 60)
print("Lokalizacja zadania do edycji: \033[94m{}\033[0m".format(ex_path))
print("=" * 60)
def display_theory(state):
mod = state["current_module"]
task = state["current_task"]
if mod not in COURSE_DATA or task not in COURSE_DATA[mod]["tasks"]:
print("\n[INFO] Brak aktywnego modułu do wyświetlenia teorii.")
return
task_data = COURSE_DATA[mod]["tasks"][task]
theory_text = task_data.get("theory", "Brak dedykowanej teorii do tego zadania.")
print("\n" + "=" * 60)
print("\033[95mWPROWADZENIE TEORETYCZNE & PODSTAWY PYTHONA\033[0m")
print("=" * 60)
print(theory_text.strip())
print("=" * 60)
def display_hint(state):
mod = state["current_module"]
task = state["current_task"]
if mod not in COURSE_DATA or task not in COURSE_DATA[mod]["tasks"]:
print("\n[INFO] Brak aktywnego zadania.")
return
task_data = COURSE_DATA[mod]["tasks"][task]
hint_text = task_data.get("hint", "Brak wskazówek do tego zadania.")
print("\n" + "=" * 60)
print("\033[93mWSKAZÓWKA DO KODU\033[0m")
print("=" * 60)
print(hint_text.strip())
print("=" * 60)
def verify_solution(state):
mod = state["current_module"]
task = state["current_task"]
if mod not in COURSE_DATA or task not in COURSE_DATA[mod]["tasks"]:
print("[INFO] Brak aktywnych zadań do weryfikacji.")
return
task_data = COURSE_DATA[mod]["tasks"][task]
test_path = task_data["test_path"]
ex_path = task_data["exercise_path"]
print("\n\033[94m[URUCHAMIANIE WERYFIKACJI] Zadanie {}.{}: {}...\033[0m".format(mod, task, task_data["title"]))
if not os.path.exists(ex_path):
print("\033[91m[BŁĄD] Plik z rozwiązaniem nie istnieje: {}\033[0m".format(ex_path))
return
# Uruchomienie testów przez pytest
cmd = [sys.executable, "-m", "pytest", test_path, "-v"]
result = subprocess.run(cmd, capture_output=True, text=True)
with open(LOG_FILE, 'w', encoding='utf-8') as log_f:
log_f.write(result.stdout)
log_f.write(result.stderr)
if result.returncode == 0:
print("\033[92m[SUKCES] Zadanie zweryfikowane pomyślnie!\033[0m")
task_str = "{}.{}".format(mod, task)
if task_str not in state["completed_tasks"]:
state["completed_tasks"].append(task_str)
next_mod, next_task = get_next_task(mod, task)
if next_mod is not None:
state["current_module"] = next_mod
state["current_task"] = next_task
print("\033[96mOdblokowano kolejne zadanie: Zadanie {}.{}!\033[0m".format(next_mod, next_task))
else:
state["current_module"] = 999 # Oznaczenie końca kursu
state["current_task"] = 999
print("\033[92m[SUKCES] Wszystkie moduły ukończone!\033[0m")
save_state(state)
else:
print("\033[91m[BŁĄD] Testy nie przeszły. Sprawdź swój kod w: {}\033[0m".format(ex_path))
print("Szczegółowe informacje o błędzie zostały zapisane w: \033[93m{}\033[0m".format(LOG_FILE))
print("\nSkrócony błąd:")
lines = result.stdout.splitlines()
failures = [l for l in lines if l.startswith("FAIL") or "AssertionError" in l or "Error:" in l]
if failures:
for f_line in failures[-5:]:
print(" " + f_line)
else:
for l in lines[-10:]:
print(" " + l)
print("-" * 60)
def display_status(state):
print("\n" + "=" * 60)
print("\033[95mSTAN POSTĘPU KURSU\033[0m")
print("=" * 60)
total_tasks = 0
completed_count = 0
for m_id, m_data in COURSE_DATA.items():
print("\n\033[96m{} (Moduł {})\033[0m".format(m_data["title"], m_id))
for t_id, t_data in m_data["tasks"].items():
task_str = "{}.{}".format(m_id, t_id)
total_tasks += 1
is_completed = task_str in state["completed_tasks"]
if is_completed:
status_icon = "\033[92m[X]\033[0m"
completed_count += 1
elif state["current_module"] == m_id and state["current_task"] == t_id:
status_icon = "\033[93m[*]\033[0m (Aktualne)"
else:
status_icon = "\033[90m[ ]\033[0m"
print(" {} Zadanie {}: {}".format(status_icon, t_id, t_data["title"]))
print("\n" + "-" * 60)
progress_pct = (completed_count / total_tasks * 100) if total_tasks > 0 else 0
print("Postęp ogólny: {:.1f}% ({}/{} zadań)".format(progress_pct, completed_count, total_tasks))
print("=" * 60)
def display_git_instructions():
print("\n\033[95mINSTRUKCJE INTEGRACJI (Git / GitHub)\033[0m")
print("-" * 60)
print("1. Aby sprawdzić status zmian w repozytorium:")
print(" \033[93mgit status\033[0m")
print("2. Aby złożyć zmiany i wysłać je na GitHub (wymaga gh cli):")
print(" \033[93mgit add .\033[0m")
print(" \033[93mgit commit -m \"feat: completed data analytics module\"\033[0m")
print(" \033[93mgh pr create --title \"Completed Module\" --body \"Check my solutions\"\033[0m")
print("-" * 60)
def display_ai_instructions():
print("\n" + "=" * 60)
print("\033[95mPOMOC AGENTA AI (ANTIGRAVITY)\033[0m")
print("=" * 60)
print("Pracujesz w parze z agentem Antigravity. Jeśli utkniesz:")
print("1. Otwórz CLI Antigravity komendą agy.")
print("2. Poproś go o pomoc, np.:")
print(" - 'Antigravity, wytłumacz mi dokładniej teorię do Zadania 1.1'")
print(" - 'Antigravity, pomóż mi zrozumieć błędy z pliku .agents/test_run.log'")
print(" - 'Antigravity, co to jest list comprehension w Pythonie?'")
print("3. Agent ma pełen wgląd w Twoje postępy i pliki zadania, więc pomoże Ci bez konieczności kopiowania kodu.")
print("=" * 60)
def reset_course():
state = {
"current_module": 1,
"current_task": 1,
"completed_tasks": []
}
save_state(state)
print("\033[93m[INFO] Stan kursu został zresetowany do Modułu 1 Zadania 1.\033[0m")
return state
def interactive_menu(state):
ensure_exercise_exists(state)
while True:
mod = state["current_module"]
task = state["current_task"]
# Jeśli ukończono wszystkie zadania
if mod == 999:
print("\n\033[92m[GRATULACJE] Ukończyłeś wszystkie dostępne zadania w kursie!\033[0m")
display_git_instructions()
break
task_data = COURSE_DATA[mod]["tasks"][task]
task_title = task_data["title"]
print("\n" + "=" * 60)
print("\033[96mAKTYWNE ZADANIE: {}.{} - {}\033[0m".format(mod, task, task_title))
print("=" * 60)
print("Wybierz opcję:")
print(" \033[93m1.\033[0m [Lekcja] Pokaż opis zadania i cele")
print(" \033[93m2.\033[0m [Teoria] Wyjaśnij podstawy i pojęcia teoretyczne")
print(" \033[93m3.\033[0m [Wskazówka] Daj mi wskazówkę do kodu")
print(" \033[93m4.\033[0m [Weryfikacja] Sprawdź moje rozwiązanie")
print(" \033[93m5.\033[0m [Status] Zobacz mój ogólny postęp")
print(" \033[93m6.\033[0m [Pomoc AI] Jak pracować z agentem Antigravity")
print(" \033[93m7.\033[0m Wyjście")
print("=" * 60)
try:
choice = input("\nWpisz numer opcji (1-7): ").strip()
except (KeyboardInterrupt, EOFError):
print("\nDo zobaczenia! Kontynuuj naukę w wolnej chwili.")
break
if choice == "1":
display_lesson(state)
elif choice == "2":
display_theory(state)
elif choice == "3":
display_hint(state)
elif choice == "4":
verify_solution(state)
state = load_state() # przeładuj zmieniony stan
elif choice == "5":
display_status(state)
display_git_instructions()
elif choice == choice == "6":
display_ai_instructions()
elif choice == "7":
print("\nDo zobaczenia! Kontynuuj naukę w wolnej chwili.")
break
else:
print("\n\033[91m[BŁĄD] Nieprawidłowy wybór. Wybierz liczbę od 1 do 7.\033[0m")
def main():
parser = argparse.ArgumentParser(description="Asystent Kursu Python dla Ekonomistów CLI")
parser.add_argument("--verify", action="store_true", help="Uruchamia weryfikację aktualnego zadania")
parser.add_argument("--status", action="store_true", help="Wyświetla stan postępów ucznia")
parser.add_argument("--reset", action="store_true", help="Resetuje stan kursu")
args = parser.parse_args()
state = load_state()
check_dependencies()
if args.reset:
state = reset_course()
display_lesson(state)
elif args.verify:
verify_solution(state)
elif args.status:
display_status(state)
display_git_instructions()
else:
interactive_menu(state)
if __name__ == "__main__":
main()