Każdy folder z zadaniem składa się z czterech składników:
README.md– Zawiera treść zadania, główne rozwiązanie. Okazjonalnie, opis rozwiązania.Rozwiązania– folder z gotowymi rozwiązaniami zadania.szablon.py– Zawiera szablon do wypełnienia rozwiązaniem.testy.py– plik z testami jednostkowymi.
Wypełnij plik szablon.py i uruchom go, odkomentowując funkcję odpal_testy()
testowanie.mp4
Niektóre zadania zawierają wizualne wyjaśnienia algorytmów, które są hostowane w chmurze na stronie internetowej lub zrealizowane w Pygame, na przykład Kolokwium 2022 A3, 160 lub 112
Niektóre zadania mają w szablon.py trzy podpowiedzi: 1 Lekko nakieruje, 2 Wyjaśni, 3 Poprowadzi przez zadanie, na przykład Kolokwium 2021 6, 226 lub 56
Każdy zestaw oraz każde zadanie zawiera plik README z opisem zadań. Jeśli masz ustawiony czarny motyw na GitHubie, zestaw ten będzie wyświetlany w ciemnej wersji.
Nierozwiązane zadania znajdują się w plikach prototyp.py i czekają na rozwiązanie. Po rozwiązaniu zadania można stworzyć pełne zadanie, automatycznie generując wszystkie pliki.
Błędy w rozwiązaniach, testach lub treściach zgłaszaj na Issues lub prywatnie.
Część rozwiązań została zaczerpnięta (bezczelnie podkradziona) z poniższych repozytoriów.
Jestem ogromnie wdzięczny ich autorom za świetne prace, które bardzo pomogły!
- 🌟 WDI-2023 - Filip Pawłowicz
- 🌟 WDI2020 - Wisien999
🤝 Jak pomóc i zostać współtwórcą?
- Zalecam utworzenie forka i zgłaszanie swoich zmian za pomocą pull requestów.
- ✏️ Stworzenie Zadania
- 🛠️ Poprawienie treści zadania, jeśli jest niejasna lub brakuje np. znaków potęgowania.
- 🔧 Ulepszanie testow poprzez komendy lub stworzeniej własnej Szczegóły
- 🧠 Tworzenie Strategi Tworzenia Zadań Szczegóły
ASRT opiera się na rozszerzaniu funkcjonalności. Dzięki temu możesz dodawać nowe funkcje i strategie bez modyfikacji istniejącego kodu, co ułatwia wdrożenie bez potrzeby wiedzy o całym systemie i unika konfliktów.
- Błędy w rozwiązaniach, testach lub treściach można zgłaszać tutaj
- Sam feedback na temat tego, jak się pracuje, w jakim kierunku można pójść oraz czego brakuje, również będzie mile widziany. kontakt.
🧪 Testowanie Zadania
Przykładowy szablon.py wygląda tak:
# ====================================================================================================>
# Zadanie 1
# Proszę napisać program poszukujący trójkątów Pitagorejskich w których długość przekątnej
# jest mniejsza od liczby N wprowadzonej z klawiatury.
# ====================================================================================================>
# print(a,b,c)
def Zadanie_1(n): ...
if __name__ == "__main__":
from testy01 import odpal_testy
Zadanie_1(input('Podaj n: '))
# odpal_testy()Wypełniasz funkcję kodem, o który prosi opis zadania. Wyniki można zwracać lub wypisywać. Jeśli to nie będzie oczywiste, pod opisem zadania powinna być wskazówka od autora testów, jakiego sposobu zwracania wyników oczekuje. W tym przypadku widać, że boki trójkąta powinny być wypisywane kolejno, bez żadnych dodatkowych napisów.
Po tym, jak zrobisz zadanie i będziesz pewny jego poprawności, możesz odkomentować funkcję odpal_testy() i uruchomić program normalnie:
# ====================================================================================================>
# Zadanie 1
# Proszę napisać program poszukujący trójkątów Pitagorejskich w których długość przekątnej
# jest mniejsza od liczby N wprowadzonej z klawiatury.
# ====================================================================================================>
# print(a,b,c)
def Zadanie_1(n):
for a in range(1, n):
for b in range(a, n):
c = (a * a + b * b) ** 0.5
if c.is_integer() and c <= n:
print(a, b, c)
if __name__ == "__main__":
from testy01 import odpal_testy
odpal_testy()
Wynik testu wskazuje na błąd: widzimy komunikat AssertionError: '3 4 5' not found in ['']. Oznacza to, że test oczekiwał pustego stringa '', a otrzymał '3 4 5', co sugeruje, że wynik dla c = 5 został niepotrzebnie wypisany.
Po chwili namysłu i ponownym przeczytaniu treści zadania, można zauważyć, że warunek mówi o długości przekątnej mniejszej, niż liczba N. Kod należy poprawić i ponownie uruchomić testy z nową nadzieją.
Widzimy, że mimo poprawnego wyniku mamy błędny test, ponieważ wypisujemy wynik w innym typie lub kolejności. W takim przypadku możemy:
- Cieszyć się poprawnym rozwiązaniem i pójść dalej.
- Zmienić typ lub format wyjścia na taki, jaki jest oczekiwany w teście.
- Zainteresować się pomocą w rozwijaniu projektu i za pomocą komendy dodać swoją funkcję wraz z jej rozwiązaniem do listy poprawnych odpowiedzi, aby inni użytkownicy mieli dobre testy dla takich samych wyników jak twój.
Często też zadania mają kilka poprawnych odpowiedzi, więc na każdy test trzeba być czujnym i analizować jego poprawność. W takich przypadkach również nalegam, by zgłaszać mi takie zadania, ponieważ będą dodawane inne warianty poprawnej odpowiedzi.
Więcej o tym, jak działa cały projekt w
✏️ Tworzenie Zadania z prototypu
- Po rozwiązaniu zadania na
prototyp.pymożna stworzyć pełne zadanie, odkomentowując funkcjęstworz_zadaniei przekazując w tablicy funkcje, które mają być objęte testami. - Funkcja
stworz_zadanieautomatycznie przygotuje testy na podstawie przekazanych funkcji. Poprosi również o podanie argumentów testowych, które Twoim zdaniem mogą być interesujące lub problematyczne. - Następnie utworzy folder zadania zawierający pliki:
rozwiazanie.pyorazszablon.pyna podstawieprototyp.py, a takżetesty.pyna podstawie wcześniej wygenerowanych testów.
Nagranie.z.ekranu.2024-11-30.o.11.25.37.mov
Każdy prototyp zawiera funkcję stworz_zadanie, importowaną z pliku Develop. Funkcja stworz_zadanie przesyła funkcje, które chcemy by obejmowały testy oraz wchodziły w skład szablonu do wypelnienia. Wiec przykładowo wypełniony prototyp powinnien wygladać tak:
# ====================================================================================================>
# Zadanie 0
# Stworz 2 funkcje jedna dodaje 2 liczby druga mnoży 2 liczby
# ====================================================================================================>
def dodaj(a, b):
return a + b
def mnoż(a, b):
return a * b
if __name__ == "__main__":
from Develop import stworz_zadanie
# stworz_zadanie([dodaj, mnoż])Funkcja stworz_zadanie działa podobnie jak funkcja print. Można ją uruchomić bez dodatkowych parametrów, aby wygenerować domyślną strukturę plików: README.md, testy.py szablon.py oraz folder Rozwiązania.
Można modyfikować sposób, w jaki generowane są pliki, ustawiając argumenty nazw plików. Modyfikacje są podawane jako stringi, które określają strategie, z jaką wygenerują się pliki. Dla podstawowego użycia projektu przydatne będą trzy modyfikacje:
# Stworzy testy, których wyniki będą zaokrąglone.
# Przydatne w zadaniach zwracających wartości typu `float`, gdzie wyniki mogą się różnić od ustawionego epsilonu.
stworz_zadanie([dodaj, mnoż], testy="float")# Stworzy testy, których wyniki będą w typie `set`.
# Przydatne w zadaniach, w których kolejność lub częstotliwość występowania wyników nie ma znaczenia.
stworz_zadanie([dodaj, mnoż], testy="bez_kolejnosci")# Nie stworzy pliku.
# Przydatne w zadaniach abstrakcyjnych, które nie są możliwe do przetestowania.
stworz_zadanie([dodaj, mnoż], testy="brak", szablon="brak")# Stworzy zadanie które polega na listach przesyłaczowych
stworz_zadanie([dodaj, mnoż], testy="przesylaczowe_t", szablon="przesylaczowe_s", README="przesylaczowe_rm")Dokładniej o modyfikacjach jest w sekcji strategie
Domyślna konfiguracja plików
- Dodaje kod do wyświetlania treści zależnych od motywu.
- Dodaje linię, aby README formatowało się jak Python.
- Z szablonu usuwa treści i zaczyna od pierwszej niezkomentowanej linijki.
- Po napotkaniu
mainprzestaje pisać. - Kończy formatowanie Pythonowe.
- przepisuje prototyp do napotkania linijki main
# ====================================================================================================>
# Zadanie 0
# Stworz 2 funkcje jedna dodaje 2 liczby druga mnoży dwie liczby
# ====================================================================================================>
def dodaj(a, b):
return a + b
def mnoż(a, b):
return a * b- Przepisuje pierwsze linie, które są komentarzami, aby zostawić opis zadania wraz z ewentualnymi komentarzami twórcy zadania.
- Następnie usuwa wszystkie linijki poza linijką zaczynającą się od
def FunkcjaKtoraTestujemy(. Tę linijkę pozostawia i dopisuje trzy kropki, aby użytkownik wiedział, że te funkcje są do napisania. - Usuwa wszystkie linie do momentu napotkania bloku
if __name__ == "__main__":. - Zapisuje import funkcji
odpal_testy. orazpodpowiedz - Prosi użytkownika o wprowadzenie argumentów, które pojawią się w szablonie wraz z jego zakomentowanym wynikiem.
- zakomentwane funkcje podpowiedz
- Zakomentowana metoda
odpal_testy(), która będzie uruchamiać testy.
# ====================================================================================================>
# Zadanie 0
# Stworz 2 funkcje jedna dodaje 2 liczby druga mnoży dwie liczby
# ====================================================================================================>
def dodaj(a, b): ...
def mnoż(a, b): ...
if __name__ == "__main__":
from testy01 import odpal_testy, podpowiedz
dodaj(2, 3) # return 5
mnoż(2,2) # return 4
# podpowiedz(1)
# podpowiedz(2)
# podpowiedz(3)
# odpal_testy()- Napisze importy, funkcje oraz nagłówek klasy
Testy - Następnie dla każdej funkcji przekazanej do testowania:
- Sprawdza liczbę argumentów, jaką funkcja przyjmuje.
- Jeśli liczba argumentów nie wynosi zero, prosi użytkownika o wpisanie argumentów testowych.
- Przetwarza input użytkownika, zmieniając go na argumenty według algorytmów.
- Uruchamia funkcję z argumentami testowymi, monitorując jednocześnie wartości wypisywane przez
printoraz wartości zwracane przez funkcję. - Jeśli funkcja nic nie zwróci, wynikiem zostanie to, co zostało przechwycone przez
print. Jeśli funkcja zwróci inną wartość, to ona będzie wynikiem, a dane wypisane przezprintzostaną zignorowane. - Z argumentów i wyniku napisze metodę testową o nazwie
test_numerTestu_funkcjaTestowalna.
def test_Nr1_dodaj(self):
self.assertIn( dodaj(2, 2), 4)- Będzie powtarzać proces od punktów 3–8, aż do napotkania argumentu
stopod użytkownika, który zakończy testy.
Po uruchomieniu funkcji stworz_testy, jeśli liczba argumentów przekazanych do testowania funkcji jest większa niż zero, program poprosi użytkownika o podanie argumentów testowych. Argumenty należy wpisywać w tej samej formie, jak w wywołaniu funkcji w Pythonie.
Najpierw program poprosi o wpisanie argumentów do szablonu, aby użytkownik mógł zobaczyć przykładowe wywołanie w szablonie wraz z wynikiem.
Następnie program poprosi o wpisywanie argumentów testowych, które posłużą do testowania funkcji w pliku testy.py.
Po stworzeniu odpowiedniej ilości testów, można zakończyć proces tworzenia testów, podając argument stop, co zakończy Twój wkład w tworzenie testów.
Na sam koniec program poprosi o podanie trzech podpowiedzi dla użytkownika.

Po stworzeniu trzech plików funkcja utworzy plik prototypBackup.py, aby bezpiecznie móc usunąć prototyp. Plik prototypBackup.py jest ignorowany przez .gitignore, więc nie będzie dodawany do głównego repozytorium. Został stworzony, aby w przypadku błędnego stworzenia zadania z różnych powodów móc utworzyć zadanie na nowo. Funkcja stworz_zadanie dba o to, by nie usunąć pliku prototypBackup, dzięki czemu można tworzyć zadania do momentu zadowolenia z efektu końcowego.
Na tym kończy się funkcja stworz_zadanie. Jeśli jednak komuś nie podoba się sposób w jaki pliki README.py, szablon.py, testy.py lub folder Rozwiazania są tworzone, chciałby dodać jakąś funkcjonalność lub inaczej tworzyć testy zawsze może stworzyć własną Strategię!
🧠 Strategie
Strategie definiują sposób, w jaki będziemy tworzyć nasze pliki w projekcie. Aktualna lista strategii znajduje się w folderach o odpowiednich nazwach: srt/README srt/Szablon, srt/Rozwiazania, srt/Testy. Każda z nich jest klasą z krótkim komentarzem opisującym jej przeznaczenie i jest dostępna do użycia przez każdego twórcę zadania.
Taki układ projektu pozwala na prosty rozwój i umożliwia rozwijanie go przez każdego, bez potrzeby znajomości całego systemu. Każdy może napisać własną klasę domyślną, która będzie następnie testowana w użyciu. Po tym, jak stanie się powszechniejsza, szybsza lub lepsza, zostanie ustawiona jako domyślna. Można również dodać klasę dodatkową, która obsługuje testy dla określonej puli zadań, dla których domyślne tworzenie zadania nie jest wystarczające.
Stworzymy kilka przykładowych klas strategii:
Data– Jest to strategia szablonu, która działa jak domyślna, z tą różnicą, że na górze pliku zostanie dodana data rozwiązania.
W folderze srt/Szablon, tworzymy nowy plik z klasa o takiej samej nazwie. Klasa dziedziczy po jednej z klas w jej folderze albo po klasie bazowej. Klasa srt/Bazowa.py jest abstrakcyjną klasą, z której będą pochodzić wszystkie klasy pochodne.
Klasa bazowa ma abstrakcyjną metodę __str__, w której musimy zwrócić wynik w postaci stringa, który później znajdzie się w pliku szablonu. Dla naszego pomysłu ta klasa będzie wyglądać tak:
# srt/Szablon/data.py
# Dziedzicze po klasie z pliku szablonów do której metody __str__ mógłbym coś dodać
from domyslne_s import domyslne_s
from datetime import date
class data(domyslne_s):
""" na górze pliku zostanie dodana data rozwiązania. """
def __str__(self):
res = str(date.today().day)
res += "\n"
res += super().__str__()
return resAby dostosować sposób generowania pliku, można skorzystać z atrybutów klasy bazowej, które są dostępne w klasach pochodnych:
linie_prototypu– lista stringów reprezentujących linie prototypu.nr_zadania– numer zadania, które rozwiązujemy.funkcje– funkcje przekazane do testów szablonu oraz inne pomocnicze funkcje.sciezka– ścieżka folderu, w którym znajduje się tworzone zadanie.nazwa_pliku– domyślna nazwa pliku, która pochodzi od nazwy folderu zawierającego klasę. Na przykład, w folderze Rozwiazanie, klasy dziedziczące mają atrybut ustawiony na "rozwiazanie{nr_zadania}.py".
Te atrybuty mogą być wykorzystywane w klasach pochodnych od klasy bazowej, a poniżej przedstawiamy przykład użycia jednego z nich.
# srt/Rozwiazanie/meritum.py
from bazowa import bazowa
import inspect
class meritum(bazowa):
"""rozwiazania która koncentruje się wyłącznie na samym rozwiązaniu, pomijając opis zadania oraz sekcję `main`"""
def __str__(self):
res = ""
for funkcja in self.funkcje:
res += inspect.getsource(funkcja)
return resTak stworzoną klasę możemy już używać w funkcji stworz_zadanie, podając argument rozwiazanie="meritum".
floatstrategia testów, która będzie zaokrąglać wyniki.
Strategie testów będą najtrudniejszych do napisania. Najczęściej będą nadpisywały metody już istniejących strategii i modyfikować sposób sprawdzania wyników testów.
Aby skutecznie zaimplementować taką strategię, będziemy musieli nadpisać dwie specjalnie wyodrębnione metody klasy prime:
from prime import prime
DOKLADNOSCI = int(input("podaj dokładność, z jaką testy mogą zaokrąglać: "))
class float(prime):
"""testy beda zaaokroglac oczekiwany wynik"""
def metoda_zwracajaca_testow_bez_kolejnosci(
self, NazwaTestu, numerTestu, zmienne, wynikWywolania, zmienne_nazwa
):
return f""" def test_Nr{numerTestu:02}_{NazwaTestu}_argumenty_{'_'.join(zmienne_nazwa)}(self):
wynik = {NazwaTestu}({', '.join(map(str, zmienne))})
self.assertAlmostEqual(wynik, { wynikWywolania }, places={DOKLADNOSCI})\n"""
def metoda_nasluchujaca_testow_bez_kolejnosci(
self, NazwaTestu, numerTestu, zmienne, wynikWywolania, zmienne_nazwa
):
return f""" def test_Nr{numerTestu:02}_{NazwaTestu}_argumenty_{'_'.join(zmienne_nazwa)}(self):
f = io.StringIO()
with redirect_stdout(f):
{NazwaTestu}({', '.join(map(str, zmienne))})
wynik = f.getvalue().strip()
self.assertAlmostEqual(wynik, { wynikWywolania }, places={DOKLADNOSCI})\n"""Klasa prime ma wiele metod specjalnie wyodrębnionych do nadpisywania.
Strategie nie mogą od siebie zależeć i muszą być niezależne. Można je odpalić w dowolnej konfiguracji.
Ograniczeniem strategii jest to, że nie przyjmuje argumentów innych niż input i jest to ustalenie stałe. Jednak, jeśli chcemy utworzyć zadanie, dodając pewne zmienne, możemy skorzystać z komend
💻 Komendy
Działanie
W folderze srt/Komendy znajdują się pliki Python z komendami. Każdy plik zawiera funkcje o takiej samej nazwie, które wykonują odpowiednią komendę.
przykładowa komenda wyglada tak.
# srt/Komendy/hello_name.py
def hello_name(imie):
print("hello", imie)Takiej komendy możemy użyć w szablon.py, importując z testy funkcję komenda i przekazując w pierwszym argumencie nazwę komendy, a następnie kolejne argumenty.
# ====================================================================================================>
# Zadanie 1
# Wypisac swoje imie
# ====================================================================================================>
def Zadanie_1(): ...
if __name__ == "__main__":
from testy01 import odpal_testy, komenda
komenda("hello_name", "kamil")
# Zadanie_1()
# odpal_testy()- w pliku
prototyp.pyimportujemy zDevelopfunkcje komenda
Wynik odpalenia takiego programu będzie: hello kamil
Taka funkcjonalność pozwala w prosty sposób rozszerzać projekt o nowe komendy, umożliwiając ulepszanie testów, na przykład poprzez dodawanie dodatkowych testów lub wariacji poprawnego wyniku, a także wprowadzanie własnych preferencji, takich jak dodatkowe zachowanie po przejsciu testów na szablonie.
SPIS KOMEND
nazwaKomendy,mozliiwy do uzycia skrot- w budowie oznacza, że nie chce mi sie jej robić
- lokalna oznacza, że jej działanie nie moze wyjść poza lokalne repozytorium. By uniknąć przypadkow, że ktos nie spodziwal ze mu poleci najlepsza domyslna piosenka zwycieska po napisaniu szablonu
- Zapis
link_do_muzyki="https://www.youtube.com/watch?v=CpeJiGDVMGooznacza ze zmiennalink_do_muzykijest opcjonalna i domyslnie uzyjemyhttps://www.youtube.com/watch?v=CpeJiGDVMGo
-
zmien_testy# tworzy na nowo zadanie na bazie szablonu jako prototyp komenda("zmien_testy", [funkcje], testy="domyslna", rozwiazanie="domyslna", szablon="domyslna")
-
dodaj_testy,dt- w budowie# dodaje dodatkowe testy komenda("dodaj_testy", funkcja, ilosci_dodatkowych_testow)
-
dodaj_wariancje,dw- w budowie# Do istniejacych juz wynikow testow funkcji dodaje kolejne mozliwe warienty na podstawie funkcji przeslanej komenda("dodaj_wariancje", funkcja)
-
zwycieska_muzyka,zm- w budowie, lokalna# Do testow danego zadania dodaje muzyka po zaliczeniu testow w szablonie # imo must have komenda("zwycieska_muzyka", link_do_muzyki="https://www.youtube.com/watch?v=CpeJiGDVMGo" )
-
funkcja_input,fi- w budowie# szybkie testowanie funkcji na parametrach # dopoki nie przerwiesz bedziesz wpisywac input a komenda uzyje jej na funkcji i wypisze output komenda("szybka_funkcja", funkcja )
-
StworzStruktureWDI# Nie bedzie wiecej uzywana i nawet nie da sie jej odpalic z poziomu plikow zadań - Takie zabezpieczenie # Ale dodaje jako taka ciekawostka oraz na przyszlosci do tworzenia struktur innych zadan komenda("StworzStruktureWDI")
Ogólne
Funkcja komenda przyjmuje "nazwaKomendy", *args oraz **kwargs, co pozwala na przesyłanie dowolnych argumentów zarówno w postaci argumentów pozycyjnych, jak i nazwanych. Aby ułatwić korzystanie, dodatkowo są dodawane dwa argumenty, jeśli komenda ich wymaga. Nie ma obowiązku ich podawania podczas wywołania komendy, są to:
nr_zadaniasciezkaWięc komenda:
# srt/Komendy/hello_zadanie.py
def hello_zadanie(nr_zadania, sciezka):# trzeba pamietac by nazwac te argumenty dokladnie tak
print("hello", nr_zadania, "from ", sciezka)Może być wywołana w następujący sposób:
# prototyp01.py
komenda("hello_zadanie")Wynik takiej komendy to:
hello 01 from /Users/user/Desktop/projekty/WDI-RST/Zestaw_1:_Proste_programy_z_pętlami/prototyp01.py
Jesli komenda jest czesto używana może miec swój skrót w pliku _skroty.py, który tylko importuje komendę i ją odpala.
def hz(nr_zadania, sciezka):
from hello_zadanie import hello_zadanie
hello_zadanie(nr_zadania, sciezka)- Każda ma mieć swój plik i ograniczać sie tylko do niego nawet jakby plik miałby mieć 20 linijek lub 100000 linijek.
- Każda komenda musi być w pełni niezależna i działać poprawnie samodzielnie, ale może wywoływać inne komendy w ramach swoich działań zgodnie z wzorcem łańcucha zobowiązań