INF.04-01-25.01-SGStyczeń 2025 · 30 kroków · ok. 89 min · 35 kryteriów z klucza
Rozwiązanie krok po kroku: Aplikacja konsolowa i galeria zdjęć (Python + Angular)
Rozwiązanie dotyczy arkusza z galerią zdjęć w wersji Python + Angular: w konsoli klasa z prywatną listą liczb losowych i metodami wypisującymi, wyszukującymi, wybierającymi liczby nieparzyste i liczącymi średnią, a w przeglądarce jednokomponentowa aplikacja Angular, w której przełączniki Bootstrapa filtrują zdjęcia według kategorii, a przycisk „Pobierz” zwiększa licznik pobrań. To technologia, w której CKE pokazało obrazy 2-4 arkusza, a klucz podaje przykłady właśnie z Angulara (*ngFor, *ngIf, (click)). Do tego dokumentacja metody według wzoru z arkusza, zrzuty i dokument egzamin.
Kroki idą zdanie po zdaniu za treścią arkusza - każdy zaczyna się poleceniem z arkusza, a kod pokazuje dokładnie te linie, które je spełniają. Kod od początku jest pisany tak, jak w prawdziwych projektach; na końcu dwa kroki porównują ten wariant z rozwiązaniem w C# i React (druga zakładka).
Co widać w podglądzie. Wyniki programu konsolowego pochodzą z prawdziwego uruchomienia kodu z danego kroku (Python 3.12) - tablica jest za każdym razem inna, a liczbę do wyszukania wpisuje się dopiero po zobaczeniu tablicy. Podgląd aplikacji webowej wykonuje kod komponentu z kroku (klasę z app.ts i szablon app.html z blokami @for i @if) ze zdjęciami z archiwum pliki3.zip: możesz przełączać kategorie i klikać „Pobierz”. Scenariusz z obrazów 2-4 przeszedłem w podglądzie i w prawdziwej aplikacji zbudowanej poleceniem ng build (Angular 22) - z tym samym wynikiem.
Jak liczą się punkty. Klucz ma 35 kryteriów (R.1.1 - R.4.8), każde za 1 punkt. Próg zdania części praktycznej to 75%, czyli 27 punktów - można stracić 8 kryteriów i nadal zdać. Przy każdym kroku jest napisane, które kryteria spełnia; egzaminator ocenia spełnienie kryteriów z klucza, nie zgodność z tym kodem.
Suwak i strzałki obok niego - albo ←→ na klawiaturze - przechodzą krok po kroku; wyjaśnienie, pliki i podgląd zmieniają się razem z krokiem.
Lista „Etap” przenosi od razu do wybranego etapu.
„Rozwiązuję samodzielnie” chowa rozwiązanie kroku - najpierw własna próba, potem odsłonięcie.
„Tryb prezentacji” pokazuje same kroki, bez menu strony - na rzutnik.
Etap 1: Przygotowanie
Krok 1 · po tym etapie masz 0 z 35 kryteriów (pkt)
Krok 1: Folder zdającego i trzy podfoldery
ok. 3 min
Polecenie z arkusza
Utwórz folder i nazwij go numerem zdającego. W folderze utwórz podfoldery: konsolowa, webowa, dokumentacja.
Na pulpicie zakładasz folder o nazwie z numerem PESEL, a w nim konsolowa, webowa i dokumentacja. Archiwum pliki3.zip z pulpitu (hasło z arkusza: K@tegorie)) rozpakujesz przy aplikacji webowej - są w nim zdjęcia obraz1.jpg - obraz12.jpg i plik dane.txt z listą obiektów galerii.
Etap 2: Aplikacja konsolowa
Kroki 2-10 · po tym etapie masz 15 z 35 kryteriów (pkt)
Krok 2: Projekt konsola w PyCharm i pusta klasa
ok. 3 min
Polecenie z arkusza
Zastosowany obiektowy język programowania zgodny z zainstalowanym na stanowisku egzaminacyjnym: C++ lub C#, lub Java, lub Python. Tablica oraz operacje na niej wykonywane są implementowane z wykorzystaniem klasy.
Wybierasz Pythona. W PyCharm: New Project, nazwa konsola (stąd archiwum konsola.zip), plik main.py. Zakładasz dwie rzeczy: klasę Table na tablicę i operacje na niej (arkusz dopuszcza dla klasy nazwę ogólną typu „tablica”) oraz funkcję main() - program główny, wywoływany pod warunkiem if __name__ == "__main__":. pass to pusta instrukcja: klasa i funkcja nie mogą mieć pustego ciała, za chwilę je wypełnisz.
R.2.1 W programie zdefiniowana klasa z dwoma polami private, jedno jest typu tablicowego, drugie typu całkowitego, w programie głównym jest powołany obiekt klasy z liczbą > 20 (W języku Python, dla pola typu private, dwa podkreślniki przed nazwą )
R.1.5 Zastosowane typy pól pasujące do problemu, np. tablica przechowuje liczby całkowite, rozmiar i wartość szukana są liczbami całkowitymi, średnia jest liczbą. W przypadku Python, typ wynika z przypisanych danych
ok. 3 min
Polecenie z arkusza
Pola klasy: Tablica liczb całkowitych (ma być tradycyjną tablicą, a w Python listą). Liczba elementów tablicy zapisana jako liczba całkowita. Oba pola są dostępne tylko w tej klasie oraz niedostępne dla klas potomnych.
W Pythonie pola obiektu tworzy się w konstruktorze__init__ przez przypisanie do self - self to tworzony obiekt:
self.__numbers = [] - lista na liczby (arkusz mówi wprost: w Pythonie listą),
self.__count = size - liczba elementów, liczba całkowita (typ wynika z przypisanej wartości, R.1.5),
dwa podkreślniki na początku nazwy to w Pythonie pole „prywatne”: Python zmienia jego nazwę (na _Table__numbers), więc spoza klasy i z klas potomnych nie da się do niego sięgnąć zwykłą nazwą. Klucz wymienia ten zapis wprost (R.2.1). Jeden podkreślnik to tylko umowa „nie ruszaj”, odpowiednik protected.
Typowe błędy
self._numbers z jednym podkreślnikiem nie spełnia wymagania „niedostępne dla klas potomnych” - klasa potomna widzi takie pole bez przeszkód. Klucz (R.2.1) wymaga dwóch.
R.2.2 Konstruktor klasy ma jeden parametr i wypełnia tablicę wartościami losowymi od 1 do 1000 (tolerancja +/-1 ) oraz ustawia pole liczby elementów tablicy wartością parametru konstruktora
R.2.4 Przynajmniej jedna pętla działająca na tablicy jest ograniczona do liczby wypełnionych elementów w tablicy
R.1.2 Kod zapisany z wcięciami dla zagnieżdżeń bloków
ok. 3 min
Polecenie z arkusza
Konstruktor klasy: Przyjmuje jako argument rozmiar tablicy. Ustawia wartość pola liczby elementów tablicy na wartość argumentu. Wypełnia tablicę, będącą polem klasy, pseudolosowymi liczbami całkowitymi z zakresu od 1 do 1000.
konstruktor przyjmuje jeden argument size i zapisuje go w polu liczby elementów,
random.randint(1, 1000) - liczba pseudolosowa od 1 do 1000; w Pythonie obie granice wchodzą do zakresu (moduł random importujesz na górze pliku),
append dokleja liczbę na koniec listy, a pętla wykonuje się self.__count razy - wszystkie operacje mają być ograniczone tym polem (R.2.4).
Wcięcie o 4 spacje wyznacza, co należy do pętli, a co do konstruktora (R.1.2).
Typowe błędy
random.randrange(1, 1000) nigdy nie wylosuje 1000 - w randrange górna granica nie wchodzi. Klucz toleruje różnicę o 1 (R.2.2), ale warto znać różnicę.
R.2.3 Metoda szukająca pobiera argument typu liczbowego. Metody są typu: wyświetlająca - bez typu (np. void) wyszukująca - typ całkowity wyświetlająca nieparzyste - typ całkowity licząca średnią - typ liczbowy (wystarczy, że zastosowane dla trzech metod)
R.2.7 Program wyświetla wszystkie elementy tablicy wg. wzoru "<index_tablicy>: <wartość>"
R.1.7 Program podejmuje komunikację z użytkownikiem, wyświetlane komunikaty są znaczące
R.1.3 Użyte znaczące polskie lub angielskie nazewnictwo metod
ok. 3 min
Polecenie z arkusza
Metoda wyświetlająca wszystkie elementy tablicy w postaci „<index_tablicy>: <wartość>”. Nie zwraca wartości. Wszystkie metody są dostępne poza klasą. Program główny tworzy obiekt klasy z rozmiarem tablicy większym od 20.
metoda bez podkreślnika na początku nazwy jest publiczna - dostępna poza klasą,
-> None to adnotacja typu: metoda nic nie zwraca. Python adnotacji nie wymaga, ale dopisujesz je w każdej metodzie - wtedy typ metody, o który pyta klucz (R.2.3), widać w kodzie, a PyCharm podkreśla niezgodności,
f-napis f"{index}: {self.__numbers[index]}" - indeks i wartość jak we wzorze z arkusza.
W main tworzysz obiekt z tablicą 50 elementów (więcej niż 20) i wywołujesz metodę. Podgląd to wynik prawdziwego uruchomienia - każde losuje inne liczby.
konsola/main.pyPythonzmienione: 7 linii
import randomclassTable:def__init__(self, size):self.__count = sizeself.__numbers = []for _ in range(self.__count):self.__numbers.append(random.randint(1, 1000))defdisplay(self) -> None:for index in range(self.__count):print(f"{index}: {self.__numbers[index]}")defmain(): table = Table(50)print("Elementy tablicy:") table.display()if__name__ == "__main__": main()
R.2.3 Metoda szukająca pobiera argument typu liczbowego. Metody są typu: wyświetlająca - bez typu (np. void) wyszukująca - typ całkowity wyświetlająca nieparzyste - typ całkowity licząca średnią - typ liczbowy (wystarczy, że zastosowane dla trzech metod)
R.2.5 W metodzie wyszukującej, gdy odnaleziono pierwsze wystąpienie wartości szukanej w tablicy, działanie pętli jest zakończone lub została zastosowana wbudowana metoda lub funkcja
ok. 3 min
Polecenie z arkusza
Metoda wyszukująca pierwsze wystąpienie wartości, przekazanej jako argument. Metoda zwraca indeks szukanego elementu lub liczbę -1, gdy elementu nie znaleziono.
Pętla porównuje kolejne elementy z szukaną wartością. Przy pierwszym trafieniu return index od razu kończy metodę - pętla nie szuka dalej, więc zwracasz indeks pierwszego wystąpienia (R.2.5). Jeśli pętla dobiegnie końca, zwracasz -1. Adnotacje mówią, że argument i wynik to liczby całkowite (value: int, -> int, R.2.3).
Typowe błędy
self.__numbers.index(value) też znajduje pierwsze wystąpienie, ale gdy wartości nie ma, zgłasza błąd ValueError zamiast zwrócić -1 - trzeba by go obsłużyć osobno.
konsola/main.pyPythonzmienione: 6 linii
import randomclassTable:def__init__(self, size):self.__count = sizeself.__numbers = []for _ in range(self.__count):self.__numbers.append(random.randint(1, 1000))defdisplay(self) -> None:for index in range(self.__count):print(f"{index}: {self.__numbers[index]}")deffind(self, value: int) -> int:for index in range(self.__count):ifself.__numbers[index] == value:return indexreturn -1defmain(): table = Table(50)print("Elementy tablicy:") table.display()if__name__ == "__main__": main()
R.2.8 Program wyszukuje liczbę w tablicy i wyświetla jej indeks, gdy nie odnaleziono liczby nic nie wyświetla
R.1.7 Program podejmuje komunikację z użytkownikiem, wyświetlane komunikaty są znaczące
R.1.4 Użyte polskie lub angielskie nazewnictwo zmiennych i obiektów. Nazewnictwo jest znaczące. Wyjątkami od reguły są zmienne: bufor, tmp, iteratory pętli. Kryterium nie jest spełnione tylko wtedy, gdy nazwy zmiennych nic nie znaczą, np.: x, foo (Uwaga! klasa i pole tablicowe klasy może mieć nazwę ogólną np tab, tablica itp )
ok. 3 min
Polecenie z arkusza
Program główny sprawdza działanie metody wyszukującej: jeżeli wartość została wyszukana, wyświetlany jest w programie głównym komunikat z wartością indeksu wyszukanej. W przeciwnym wypadku nic nie jest wyświetlane (taka sytuacja ma miejsce na obrazie 1).
Liczbę podaje użytkownik: input(...) zwraca tekst, a int(...) zamienia go na liczbę całkowitą. Komunikat wypisujesz tylko wtedy, gdy metoda nie zwróciła -1 - przy braku liczby program nic nie mówi, jak na obrazie 1 (R.2.8).
Podgląd to dwa prawdziwe uruchomienia: najpierw wpisana liczba, która jest w tablicy (spod indeksu 7 - wynik pokazuje indeks jej pierwszego wystąpienia), potem 1001 - spoza zakresu, więc bez komunikatu.
konsola/main.pyPythonzmienione: 5 linii
import randomclassTable:def__init__(self, size):self.__count = sizeself.__numbers = []for _ in range(self.__count):self.__numbers.append(random.randint(1, 1000))defdisplay(self) -> None:for index in range(self.__count):print(f"{index}: {self.__numbers[index]}")deffind(self, value: int) -> int:for index in range(self.__count):ifself.__numbers[index] == value:return indexreturn -1defmain(): table = Table(50)print("Elementy tablicy:") table.display() searched = int(input("Podaj liczbę do wyszukania: ")) index = table.find(searched)if index != -1:print(f"Liczba {searched} jest pod indeksem {index}")if__name__ == "__main__": main()
R.2.3 Metoda szukająca pobiera argument typu liczbowego. Metody są typu: wyświetlająca - bez typu (np. void) wyszukująca - typ całkowity wyświetlająca nieparzyste - typ całkowity licząca średnią - typ liczbowy (wystarczy, że zastosowane dla trzech metod)
R.2.9 Program wyświetla na ekranie liczby nieparzyste oraz liczbę ich wystąpień
ok. 3 min
Polecenie z arkusza
Metoda wyświetlająca wszystkie wartości nieparzyste z tablicy i zwracająca ich liczbę. Program główny wyświetla liczby nieparzyste oraz ich ilość z odpowiednim komentarzem.
Liczba jest nieparzysta, gdy reszta z dzielenia przez 2 (%) jest różna od zera. Metoda wypisuje każdą taką liczbę i zwiększa licznik odd_count, a na końcu go zwraca (-> int). Program główny poprzedza listę nagłówkiem i wypisuje zwróconą liczbę z komentarzem (R.2.9).
konsola/main.pyPythonzmienione: 12 linii
import randomclassTable:def__init__(self, size):self.__count = sizeself.__numbers = []for _ in range(self.__count):self.__numbers.append(random.randint(1, 1000))defdisplay(self) -> None:for index in range(self.__count):print(f"{index}: {self.__numbers[index]}")deffind(self, value: int) -> int:for index in range(self.__count):ifself.__numbers[index] == value:return indexreturn -1defdisplay_odd(self) -> int: odd_count = 0for index in range(self.__count):ifself.__numbers[index] % 2 != 0:print(self.__numbers[index]) odd_count += 1return odd_countdefmain(): table = Table(50)print("Elementy tablicy:") table.display() searched = int(input("Podaj liczbę do wyszukania: ")) index = table.find(searched)if index != -1:print(f"Liczba {searched} jest pod indeksem {index}")print("Liczby nieparzyste:") odd_count = table.display_odd()print(f"Razem nieparzystych: {odd_count}")if__name__ == "__main__": main()
R.2.3 Metoda szukająca pobiera argument typu liczbowego. Metody są typu: wyświetlająca - bez typu (np. void) wyszukująca - typ całkowity wyświetlająca nieparzyste - typ całkowity licząca średnią - typ liczbowy (wystarczy, że zastosowane dla trzech metod)
R.2.10 Program liczy średnią arytmetyczną (suma / rozmiar) oraz wyświetla ją na ekranie
R.1.5 Zastosowane typy pól pasujące do problemu, np. tablica przechowuje liczby całkowite, rozmiar i wartość szukana są liczbami całkowitymi, średnia jest liczbą. W przypadku Python, typ wynika z przypisanych danych
R.1.1 Kod źródłowy zapisany w sposób czytelny: instrukcje w osobnych liniach, stosowane spacje pomiędzy operatorami, konsekwentnie stosowana wybrana konwencja dla nawiasów klamrowych instrukcji blokowej
ok. 3 min
Polecenie z arkusza
Metoda licząca średnią arytmetyczną wartości w tablicy i zwracająca tą wartość. Program główny wyświetla wartość średniej.
Sumujesz elementy w zmiennej total i dzielisz przez liczbę elementów. W Pythonie operator / daje zawsze wynik rzeczywisty (float) - także dla dwóch liczb całkowitych - więc nie trzeba rzutowania jak w C#. Metoda zwraca float (R.2.10). W main format :.2f pokazuje dwa miejsca po kropce dziesiętnej.
Typowe błędy
total // self.__count (podwójny ukośnik) to dzielenie całkowite - średnia 525,73 wyjdzie jako 525. W Pythonie zwykłe / liczy dokładnie.
konsola/main.pyPythonzmienione: 9 linii
import randomclassTable:def__init__(self, size):self.__count = sizeself.__numbers = []for _ in range(self.__count):self.__numbers.append(random.randint(1, 1000))defdisplay(self) -> None:for index in range(self.__count):print(f"{index}: {self.__numbers[index]}")deffind(self, value: int) -> int:for index in range(self.__count):ifself.__numbers[index] == value:return indexreturn -1defdisplay_odd(self) -> int: odd_count = 0for index in range(self.__count):ifself.__numbers[index] % 2 != 0:print(self.__numbers[index]) odd_count += 1return odd_countdefaverage(self) -> float: total = 0for index in range(self.__count): total += self.__numbers[index]return total / self.__countdefmain(): table = Table(50)print("Elementy tablicy:") table.display() searched = int(input("Podaj liczbę do wyszukania: ")) index = table.find(searched)if index != -1:print(f"Liczba {searched} jest pod indeksem {index}")print("Liczby nieparzyste:") odd_count = table.display_odd()print(f"Razem nieparzystych: {odd_count}") average = table.average()print(f"Średnia wszystkich elementów: {average:.2f}")if__name__ == "__main__": main()
Kod aplikacji przygotuj do nagrania na płytę. W folderze konsolowa powinno znaleźć się archiwum całego projektu o nazwie konsola.zip, plik z kodem źródłowym programu oraz plik wykonywalny, jeżeli istnieje.
Folder projektu pakujesz do konsola.zip, obok kopiujesz main.py. Python nie tworzy pliku wykonywalnego. Archiwum robisz na samym końcu pracy, po dopisaniu dokumentacji z części III.
Kroki 11-23 · po tym etapie masz 24 z 35 kryteriów (pkt)
Krok 11: Nowy projekt Angular
ok. 5 min
Polecenie z arkusza
Z zastosowaniem dostępnego na stanowisku egzaminacyjnym frameworka Angular lub biblioteki React.js wykonaj aplikację internetową typu front-end realizującą funkcję kategoryzacji zdjęć w galerii.
Wybierasz Angular. Projekt zakładasz w terminalu narzędziem Angular CLI:
npx @angular/cli@latest new web
cd web
npx ng serve
(z globalnie zainstalowanym CLI wystarczy ng new web). Kreator pyta o format stylów - CSS - i o renderowanie po stronie serwera (SSR) - nie. ng serve uruchamia serwer pod http://localhost:4200.
Komponent główny App to trzy pliki w src/app: app.ts (klasa z danymi i metodami), app.html (szablon) i app.css (style tylko tego komponentu). Dekorator @Component łączy je w całość. (W Angularze 19 i starszych pliki nazywają się app.component.*.)
R.3.2 Do projektu dołączona biblioteka Bootstrap oraz sformatowany obraz: margin: 5px; oraz zaokrąglone rogi (border-radius lub klasa rounded )
ok. 2 min
Polecenie z arkusza
Przyciski oraz pola switch są stylowane zgodnie z przykładami w tabeli 1. Angular: To use Bootstrap add to styles.css: @import "~bootstrap/dist/css/bootstrap.css";
npm install bootstrap w folderze web, a potem import w src/styles.css - globalnym arkuszu stylów całej aplikacji (R.3.2). Tabela 1 podaje ścieżkę z tyldą (~bootstrap/...) - tak działał starszy Angular. Obecny Angular buduje projekt narzędziem esbuild i tyldy nie rozumie: ng build kończy się błędem „Could not resolve "~bootstrap/..."” (sprawdziłem w Angular 22). Piszesz więc ścieżkę bez tyldy - zadziała też w starszym Angularze.
Typowe błędy
Import z tyldą skopiowany z tabeli 1 w nowym Angularze: aplikacja się nie zbuduje, a bez działającej aplikacji przepadają kryteria R.3.8 - R.3.10.
Do utworzenia aplikacji należy wykorzystać zdjęcia oraz plik dane.txt wypakowane z archiwum. Obrazy należy umieścić w folderze assets (egzamin/src/assets lub egzamin/public/assets).
Rozpakowujesz pliki3.zip (hasło K@tegorie)) i kopiujesz dwanaście zdjęć do web/public/assets. Folder public obecny Angular kopiuje bez zmian do gotowej aplikacji, więc zdjęcie jest dostępne pod adresem assets/obraz1.jpg. (Starszy Angular używał w tym celu folderu src/assets - arkusz wymienia obie możliwości.)
R.3.1 Zapisany w aplikacji jeden komponent, który zawiera nagłówek h1, trzy pola switch lub checkbox zaznaczone, przynajmniej jeden obraz, nagłówek h4 i przycisk "Pobierz" oraz tablicę obiektów z pliku dane.txt
ok. 2 min
Polecenie z arkusza
Aplikacja składa się z jednego komponentu. Komponent składa się z: Nagłówka pierwszego stopnia o treści: „Kategorie zdjęć”.
Zastępujesz całą zawartość app.html (szablon z kreatora to strona powitalna Angulara) nagłówkiem <h1> (R.3.1). W app.ts usuwasz RouterOutlet i przykładowe pole title - zostaje pusta klasa komponentu, którą za chwilę wypełnisz.
Podgląd strony po tym kroku (web/src/app/app.html)
Krok 15: Tablica zdjęć z pliku dane.txt w polu klasy
Kryteria: ,
R.3.1 Zapisany w aplikacji jeden komponent, który zawiera nagłówek h1, trzy pola switch lub checkbox zaznaczone, przynajmniej jeden obraz, nagłówek h4 i przycisk "Pobierz" oraz tablicę obiektów z pliku dane.txt
R.1.5 Zastosowane typy pól pasujące do problemu, np. tablica przechowuje liczby całkowite, rozmiar i wartość szukana są liczbami całkowitymi, średnia jest liczbą. W przypadku Python, typ wynika z przypisanych danych
ok. 3 min
Polecenie z arkusza
Dokument dane.txt zawiera listę obiektów zdjęć, którą należy skopiować jako elementy tablicy. Każdy obiekt zdjęcia zawiera pola: id, alt (tekst alternatywny dla zdjęcia), filename (nazwa pliku ze zdjęciem), category (1 dla kategorii kwiaty, 2 zwierzęta, 3 samochody), downloads (liczba pobrań zdjęcia).
Kopiujesz obiekty z dane.txt do tablicy photos - pola klasy komponentu (R.3.1). Szablon widzi wszystkie pola klasy, więc tablicę wyświetlisz bezpośrednio z niego.
TypeScript pozwala opisać kształt obiektu interfejsem: interface Photo wymienia pola zdjęcia z ich typami (number, string), a zapis photos: Photo[] mówi, że to tablica takich obiektów. Interfejs nie jest konieczny, ale literówka w nazwie pola (np. downlods) od razu kończy się błędem kompilacji zamiast pustym miejscem na stronie (R.1.5). Dane przepisujesz tak, jak są w pliku (także „Rotwailer” - to dane z arkusza). Na stronie nic jeszcze nie widać.
Podgląd strony po tym kroku (web/src/app/app.html)
Krok 16: Pierwszy przełącznik: Kwiaty, domyślnie włączony
Kryteria:
R.3.3 Przycisk ma przypisaną klasę btn btn-success oraz przynajmniej jedno pole switch zdefiniowane za pomocą biblioteki Bootstrap jako blok formatowany stylami form-check form-switch (form-check-inline opcjonalnie ) i złożony z kontrolki input i przypisanej do niej etykiety label, np.: <div class="form-check form-switch"> <input class="form-check-input" type="checkbox" id="chk3" ...> <label class="form-check-label" for="chk3">Samochody</label> </div> W React.js zastosowane className zamiast class oraz domknięte wszystkie znaczniki
ok. 3 min
Polecenie z arkusza
Komponent składa się z trzech pól switch (checkbox) domyślnie włączonych, o etykietach: Kwiaty, Zwierzęta, Samochody.
Stan przełączników to pole visible: obiekt, w którym kluczem jest numer kategorii, a wartością true/false - na start wszystkie włączone. Typ Record<number, boolean> to w TypeScripcie właśnie „słownik: liczba → wartość logiczna”.
Przełącznik przenosisz z tabeli 1: div z klasami form-check form-switch (plus form-check-inline, żeby przełączniki stały w jednym wierszu, jak na obrazie 2), w nim input typu checkbox i etykieta powiązana atrybutem for (R.3.3). W szablonie Angulara HTML zostaje HTML-em - class i for bez zmian. [checked]="visible[1]" wiąże właściwość checked z wartością z klasy: nawiasy kwadratowe oznaczają, że to wyrażenie, a nie tekst.
Podgląd strony po tym kroku (web/src/app/app.html)
Krok 17: Przełączniki Zwierzęta i Samochody
Kryteria:
R.3.1 Zapisany w aplikacji jeden komponent, który zawiera nagłówek h1, trzy pola switch lub checkbox zaznaczone, przynajmniej jeden obraz, nagłówek h4 i przycisk "Pobierz" oraz tablicę obiektów z pliku dane.txt
ok. 2 min
Polecenie z arkusza
Trzech pól switch (checkbox) domyślnie włączonych, o etykietach: Kwiaty, Zwierzęta, Samochody.
Dwa kolejne przełączniki według tego samego wzoru, z innymi identyfikatorami (animals, cars) i kategoriami 2 i 3 (R.3.1). Identyfikatory muszą być różne - inaczej etykiety wskazywałyby pierwszy przełącznik.
Podgląd strony po tym kroku (web/src/app/app.html)
Krok 18: Zmiana stanu przełącznika: toggleCategory
Kryteria:
R.3.7 W kodzie zdefiniowana przynajmniej jedna funkcja (lub operacja), która jest uruchamiana po zmianie stanu switch np. (change)="flowersChanged() lub onChange={this.flowersChanged()}
ok. 3 min
Polecenie z arkusza
Wyświetlane są tylko zdjęcia z kategorii dla której jest włączone pole switch.
Metoda toggleCategory(category) odwraca stan jednej kategorii (! to zaprzeczenie). Każdy przełącznik wywołuje ją zdarzeniem (change)="toggleCategory(1)" - nawiasy okrągłe to powiązanie zdarzenia; klucz podaje dokładnie taki zapis jako przykład (R.3.7). Angular po obsłużeniu zdarzenia sam odświeża widok.
W Angularze można zmienić pole obiektu wprost (this.visible[category] = ...) - wykrywanie zmian to zauważy. W React trzeba było budować nowy obiekt stanu.
Typowe błędy
onchange="toggleCategory(1)" w stylu HTML - klucz wymienia przypisywanie metod do zdarzeń w ten sposób jako powód niezaliczenia CAŁEGO rezultatu 3. W Angularze: (change)="...".
Podgląd strony po tym kroku (web/src/app/app.html)
Krok 19: Bloki zdjęć w pętli @for
Kryteria: , ,
R.3.1 Zapisany w aplikacji jeden komponent, który zawiera nagłówek h1, trzy pola switch lub checkbox zaznaczone, przynajmniej jeden obraz, nagłówek h4 i przycisk "Pobierz" oraz tablicę obiektów z pliku dane.txt
R.3.3 Przycisk ma przypisaną klasę btn btn-success oraz przynajmniej jedno pole switch zdefiniowane za pomocą biblioteki Bootstrap jako blok formatowany stylami form-check form-switch (form-check-inline opcjonalnie ) i złożony z kontrolki input i przypisanej do niej etykiety label, np.: <div class="form-check form-switch"> <input class="form-check-input" type="checkbox" id="chk3" ...> <label class="form-check-label" for="chk3">Samochody</label> </div> W React.js zastosowane className zamiast class oraz domknięte wszystkie znaczniki
R.3.4 Do generowania obrazów zastosowana pętla działająca na elementach galerii np. Angular: *ngFor = "let zdjecie of galeria" React.js: {this.state.galeria.map(zdjecie => ... (lub bez this.state ) oraz pętla generuje obraz i nagłówek h4 z odwołaniem do pól obiektu np. <img src="assets/{{ zdjecie.filename }}" alt="{{ zdjecie.alt }}"> <h4>Pobrań: {{ zdjecie.downloads }}</h4> W React.js pojedyncze nawiasy klamrowe (uwaga! może poprawnie działać również bez nazwy katalogu assets )
ok. 4 min
Polecenie z arkusza
Bloków zdjęć. Bloki są wyświetlone jeden obok drugiego, zawierają zdjęcie, nagłówek 4 stopnia z liczbą pobrań oraz przycisk o treści „Pobierz”. W aplikacji zastosowano pętle do wyświetlenia bloków zdjęć. Aplikacja jest napisana uniwersalnie i działa poprawnie też dla innej liczby zdjęć.
Pętlą jest blok @for (photo of photos; track photo.id) { ... } - wbudowana składnia Angulara (od wersji 17): dla każdego obiektu tablicy powtarza swoją zawartość, a w środku pod nazwą photo jest bieżące zdjęcie. track photo.id podaje, po czym Angular rozpoznaje elementy przy odświeżaniu (jak key w React) - w @for jest obowiązkowe. W starszym zapisie to samo robi dyrektywa *ngFor="let photo of photos" z klucza (R.3.4).
W środku blok zdjęcia: <img> z adresem i tekstem alternatywnym z danych, wstawionymi interpolacją{{ ... }} - dokładnie jak w przykładzie z klucza, <h4> z liczbą pobrań i zielony przycisk btn btn-success (R.3.3). Klasa d-inline-block stawia bloki obok siebie; zawijają się do kolejnego wiersza, gdy zabraknie miejsca. Pętla działa dla dowolnej liczby zdjęć.
Typowe błędy
@for (photo of photos) bez track to błąd kompilacji szablonu - w nowym zapisie Angular go wymaga. Gdy dane nie mają id, wystarczy track $index.
Podgląd strony po tym kroku (web/src/app/app.html)
Krok 20: Zdjęcia: margines 5 px i zaokrąglone rogi
Kryteria:
R.3.2 Do projektu dołączona biblioteka Bootstrap oraz sformatowany obraz: margin: 5px; oraz zaokrąglone rogi (border-radius lub klasa rounded )
ok. 2 min
Polecenie z arkusza
Zdjęcia są formatowane stylem: marginesy zewnętrzne 5 px, zaokrąglone rogi.
Styl dopisujesz w app.css - arkuszu stylów komponentu. Angular ogranicza takie style do swojego komponentu: reguła img nie dotknie obrazków w innych komponentach (zwykły CSS na stronie zadziałałby globalnie). margin: 5px i border-radius: 8px to dokładnie to, o co pyta klucz (R.3.2); równie dobra jest klasa Bootstrapa rounded przy <img>.
web/src/app/app.cssCSSnowy plik
img {margin: 5px;border-radius: 8px;}
Podgląd strony po tym kroku (web/src/app/app.html)
Krok 21: Warunek @if: tylko kategorie włączone przełącznikiem
Kryteria: ,
R.3.5 Do sprawdzenia czy switch jest zaznaczony lub czy kategoria obrazu zgadza się z zaznaczeniem zastosowany warunek np.: Angular: *ngIf = "kwiaty == true", *ngIf = "zdjecie.category == 1" React.js: funkcja renderująca, w której znajduje się warunek i i instrukcja return, która odpowiednio zwraca kod wyświetlający obrazek
R.3.9 Zdjęcia wyświetlają się w zależności od stanu pól switch (w uruchomionej aplikacji lub na zrzucie oraz obowiązkowo w kodzie )
ok. 2 min
Polecenie z arkusza
Bloków zdjęć, które są wyświetlane warunkowo, w zależności od ustawień pól switch. W aplikacji zastosowano pętle oraz warunki do wyświetlenia bloków zdjęć.
Wewnątrz pętli blok zdjęcia obejmujesz warunkiem @if (visible[photo.category]) { ... }: zdjęcie pojawia się tylko wtedy, gdy przełącznik jego kategorii jest włączony. To odpowiednik *ngIf z przykładu w kluczu (R.3.5) - w starszym zapisie: <div *ngIf="visible[photo.category]">.
Wyłącz w podglądzie „Zwierzęta” - zostają kwiaty i samochody (obraz 3, R.3.9). Włączone same „Samochody” dają dwa zdjęcia.
Podgląd strony po tym kroku (web/src/app/app.html)
Krok 22: Przycisk Pobierz zwiększa liczbę pobrań
Kryteria: ,
R.3.6 Zapisane zdarzenie kliknięcia przycisku i obsłużone w kodzie np. Angular: <button ... (click)="onPobierzClick(i)">Pobierz</button> onPobierzClick(i: any):void ... zmienna i z przykładu jest indeksem w pętli (let i=index) React.js: onClick={this.OnPobierzClick(this.state.i)} OnPobierzClick() {... }
R.3.10 Po wciśnięciu przycisku zwiększany jest o jeden licznik pobrań, zmiany są widoczne na stronie, są modyfikowane w tablicy dla odpowiedniego obiektu (w uruchomionej aplikacji lub na zrzucie oraz obowiązkowo w kodzie )
ok. 3 min
Polecenie z arkusza
Gdy przycisk „Pobierz” zostanie kliknięty, wzrasta o jeden liczba pobrań dla danego zdjęcia. Liczba pobrań jest zapisywana w tablicy z obiektami zdjęć, co na bieżąco powoduje wyświetlenie tej modyfikacji na ekranie.
Przycisk przekazuje metodzie swoje zdjęcie: (click)="download(photo)" - zmienna pętli photo jest dostępna w całym bloku (R.3.6). Metoda zwiększa photo.downloads o jeden. To ten sam obiekt, który leży w tablicy photos, więc zmiana jest zapisana w tablicy, a Angular po zdarzeniu odświeża widok i liczba rośnie od razu (R.3.10). Kliknij kilka razy „Pobierz” przy drugim zdjęciu (obraz 4).
Podgląd strony po tym kroku (web/src/app/app.html)
Krok 23: Folder webowa: web.zip i zmienione pliki
ok. 3 min
Polecenie z arkusza
Kod aplikacji przygotuj do nagrania na płytę. W folderze webowa powinno znaleźć się archiwum całego folderu projektu o nazwie web.zip oraz pliki z kodem źródłowym, które były modyfikowane przez zdającego.
Pakujesz folder web do web.zip, ale bez node_modules i bez ukrytego folderu .angular (odtwarza je npm install i budowanie) - z nimi archiwum miałoby setki megabajtów. Obok kopiujesz zmienione pliki: app.ts, app.html, app.css i styles.css.
Kroki 24-27 · po tym etapie masz 35 z 35 kryteriów (pkt)
Krok 24: Dokumentacja metody find
Kryteria: , , , ,
R.4.1 Dla metody z aplikacji konsolowej zapisany nagłówek w postaci komentarza zgodny z Listingiem 1 z arkusza egzaminacyjnego (nie należy brać pod uwagę liczby gwiazdek), komentarz może być wieloliniowy lub kilka jednoliniowy lub Docstrings (potrójny cudzysłów)
R.4.2 W komentarzu podana nazwa metody oraz opisane jej działanie
R.4.3 W komentarzu podane wszystkie argumenty metody: nazwy i krótki opis lub zapisane "brak"
R.4.4 W komentarzu podana nazwa typu zwracanego i krótki opis co jest zwracane lub zapisane "brak"
R.4.5 W komentarzu podany numer zdającego
ok. 4 min
Polecenie z arkusza
W kodzie źródłowym aplikacji konsolowej za pomocą komentarza utwórz nagłówek dowolnej metody, według wzoru z listingu 1. Komentarz powinien znaleźć się nad lub pod nazwą metody. W sekcji parametry należy umieścić opis wszystkich argumentów metody lub zapisać „brak” w przypadku metody bezparametrowej.
W Pythonie opis metody to docstring - napis w potrójnym cudzysłowie zaraz pod linią def (klucz dopuszcza go wprost, R.4.1). Pola z listingu 1: nazwa i działanie (R.4.2), parametrvalue z opisem (R.4.3) - self się nie wlicza, to obiekt, na którym metodę wywołano, zwracany typ int z opisem (R.4.4) i autor (R.4.5) - na egzaminie numer PESEL, tu Bartosz Bryniarski.
Typowe błędy
Wpisanie self jako parametru w dokumentacji nie jest błędem, ale „parametry: brak” przy metodzie find już tak - ma ona jeden argument (R.4.3).
konsola/main.pyPythonzmienione: 9 linii
import randomclassTable:def__init__(self, size):self.__count = sizeself.__numbers = []for _ in range(self.__count):self.__numbers.append(random.randint(1, 1000))defdisplay(self) -> None:for index in range(self.__count):print(f"{index}: {self.__numbers[index]}")deffind(self, value: int) -> int:""" ********************************************** nazwa metody: find opis metody: wyszukuje w tablicy pierwsze wystąpienie podanej wartości parametry: value - liczba całkowita, której szukamy w tablicy zwracany typ i opis: int - indeks pierwszego wystąpienia wartości albo -1, gdy jej nie ma autor: Bartosz Bryniarski ********************************************** """for index in range(self.__count):ifself.__numbers[index] == value:return indexreturn -1defdisplay_odd(self) -> int: odd_count = 0for index in range(self.__count):ifself.__numbers[index] % 2 != 0:print(self.__numbers[index]) odd_count += 1return odd_countdefaverage(self) -> float: total = 0for index in range(self.__count): total += self.__numbers[index]return total / self.__countdefmain(): table = Table(50)print("Elementy tablicy:") table.display() searched = int(input("Podaj liczbę do wyszukania: ")) index = table.find(searched)if index != -1:print(f"Liczba {searched} jest pod indeksem {index}")print("Liczby nieparzyste:") odd_count = table.display_odd()print(f"Razem nieparzystych: {odd_count}") average = table.average()print(f"Średnia wszystkich elementów: {average:.2f}")if__name__ == "__main__": main()
R.1.6 Podjęta próba skompilowania kodu, udokumentowana zrzutem przedstawiającym uruchomiony program lub jego kompilację
R.2.6 Program uruchamia się w konsoli, co jest udokumentowane zrzutem ekranu
R.4.6 Zapisany przynajmniej jeden zrzut ekranu z uruchomienia lub kompilacji aplikacji konsolowej, na zrzucie widoczne jest środowisko, w którym powstała aplikacja
ok. 3 min
Polecenie z arkusza
Wykonaj zrzuty ekranu dokumentujące uruchomienie aplikacji. Zrzuty powinny obejmować cały obszar ekranu monitora z widocznym paskiem zadań. Na zrzucie należy umieścić okno z wynikiem działania programu oraz otwarte środowisko programistyczne z projektem. Aplikacja konsolowa – dowolna liczba zrzutów nazwanych konsola1, konsola2, ...
Program pyta o liczbę, więc robisz dwa zrzuty całego ekranu z PyCharm i panelem Run: konsola1.png - wyszukana liczba, która jest w tablicy (z komunikatem o indeksie), konsola2.png - liczba spoza tablicy (bez komunikatu, jak na obrazie 1). Zrzuty zapisujesz w folderze dokumentacja.
R.3.8 Aplikacja jest interpretowana bez błędów. W stanie początkowym są zaznaczone wszystkie pola switch i wyświetlone wszystkie obrazy co jest udokumentowane zrzutem ekranu
R.4.7 Zapisany przynajmniej jeden zrzut ekranu z uruchomienia aplikacji webowej lub z błędami interpretacji, na zrzucie widoczne jest środowisko, w którym powstała aplikacja
ok. 4 min
Polecenie z arkusza
Aplikacja webowa – dowolna liczba zrzutów nazwanych web1, web2, ... (stan początkowy, różne kombinacje dla pól switch, działanie przycisku „Pobierz”).
npx ng serve i http://localhost:4200. Zrzuty całego ekranu z przeglądarką i środowiskiem albo terminalem:
web1.png - stan początkowy, wszystkie zdjęcia (obraz 2, R.3.8),
web2.png - wyłączone „Zwierzęta” (obraz 3),
web3.png - włączone same „Samochody”,
web4.png - po kilku kliknięciach „Pobierz” (obraz 4).
Podgląd pokazuje kompletną aplikację - możesz w niej przejść te stany.
Typowe błędy
Zrzut samego okna przeglądarki, bez środowiska albo terminala, nie spełnia R.4.7.
Podgląd strony po tym kroku (web/src/app/app.html)
Krok 27: Dokument egzamin
Kryteria:
R.4.8 Dokumentacja zawiera wykorzystane oprogramowanie: nazwa systemu operacyjnego, nazwy środowisk programistycznych, nazwy języków programowania
ok. 2 min
Polecenie z arkusza
W edytorze tekstu pakietu biurowego utwórz plik z dokumentacją i nazwij go egzamin. Dokument powinien zawierać zapisane informacje o wykorzystanych w czasie egzaminu narzędziach: Nazwę systemu operacyjnego, Nazwy środowisk programistycznych, Nazwy języków programowania. Zrzuty ekranu i dokument umieść w podfolderze dokumentacja.
W tym arkuszu dokument piszesz w edytorze tekstu pakietu biurowego (np. Word albo Writer) i zapisujesz jako egzamin w folderze dokumentacja. Trzy informacje: system, środowiska i języki (R.4.8). W plikach kroku jest ta sama treść jako plik tekstowy.
egzamin.txtTekstnowy plik
System operacyjny: Windows 11Środowiska programistyczne: PyCharm Community (aplikacja konsolowa), Visual Studio Code z Node.js i Angular CLI (aplikacja webowa)Języki programowania: Python (aplikacja konsolowa), TypeScript i HTML - framework Angular (aplikacja webowa)
Kroki 28-29 · po tym etapie masz 35 z 35 kryteriów (pkt)
Krok 28: Klasa w Pythonie i w C#
ok. 3 min
Polecenie z arkusza
Pola klasy: Tablica liczb całkowitych (ma być tradycyjną tablicą, a w Python listą). Oba pola są dostępne tylko w tej klasie oraz niedostępne dla klas potomnych.
Ta sama klasa, różne środki:
prywatność: C# ma słowo private, Python - umowę i dwa podkreślniki (self.__numbers), które zmieniają nazwę pola,
pola: w C# deklarowane w ciele klasy z typem (private int[] numbers;), w Pythonie tworzone w __init__ przez self.,
tablica: w C# stały rozmiar new int[size], w Pythonie lista rośnie przez append,
losowanie 1-1000: random.Next(1, 1001) w C# (górna granica nie wchodzi), random.randint(1, 1000) w Pythonie (wchodzi),
średnia: w C# (double)sum / count (bez rzutowania byłoby dzielenie całkowite), w Pythonie / zawsze daje float,
typy metod (R.2.3): w C# obowiązkowe (void, int, double), w Pythonie widać je dzięki adnotacjom -> None, -> int, -> float.
Z zastosowaniem dostępnego na stanowisku egzaminacyjnym frameworka Angular lub biblioteki React.js wykonaj aplikację internetową typu front-end realizującą funkcję kategoryzacji zdjęć w galerii.
Ta sama galeria w obu technologiach:
pętla: @for (photo of photos; track photo.id) w szablonie Angulara, photos.map(...) z key w JSX,
warunek: @if (...) wokół bloku w Angularze; w React funkcja renderująca zwraca null dla ukrytych zdjęć,
przełączniki: [checked] i (change) w Angularze, checked i onChange w React,
licznik pobrań: w Angularze wystarczy photo.downloads++ - wykrywanie zmian zauważy zmianę obiektu; w React taka zmiana nie odświeży ekranu - trzeba zbudować nową tablicę i przekazać ją do setPhotos,
style zdjęć: w Angularze osobny app.css ograniczony do komponentu, w React styl w obiekcie style={{ margin: '5px' }}.
Klucz tego arkusza podaje przykłady z obu technologii (R.3.4 - R.3.7). Angular wymaga więcej plików i kreatora, za to szablon jest zwykłym HTML z dodatkami; React - jeden plik JSX, ale z regułami niezmienności stanu. Wybierz tę, którą ćwiczyłeś.
Krok 30 · po tym etapie masz 35 z 35 kryteriów (pkt)
Krok 30: Nagranie płyty
ok. 5 min
Polecenie z arkusza
UWAGA: Nagraj płytę z rezultatami pracy. W folderze z numerem zdającego powinny się znajdować podfoldery dokumentacja, konsolowa, webowa. W folderze dokumentacja: pliki ze zrzutami oraz plik egzamin. W folderze konsolowa: spakowany cały projekt aplikacji konsolowej, źródło, opcjonalnie plik wykonywalny. W folderze webowa: spakowany cały projekt aplikacji web, pliki ze źródłami komponentu, html, css i inne modyfikowane pliki.
Symulacja ekranu w przeglądarce - w Android Studio / Visual Studio wygląd może się nieco różnić.
To jedno z możliwych rozwiązań. Egzaminator ocenia spełnienie kryteriów z klucza odpowiedzi, a nie zgodność z tym kodem - inna, poprawna droga też daje punkty.
Autor rozwiązania: Bartosz Bryniarski. Licencja CC BY-NC 4.0 - kopiowanie i przeróbki do celów niekomercyjnych, z podaniem autora i źródła (link do tej strony) (zasady wykorzystania). Treść arkusza i klucza oceniania pochodzi z CKE.