Przejdź do głównej treści
INF.04-01-25.01-SGStyczeń 2025 · 30 kroków · ok. 87 min · 35 kryteriów z klucza

Rozwiązanie krok po kroku: Aplikacja konsolowa i galeria zdjęć (C# + React)

Rozwiązujemy arkusz z galerią zdjęć w wersji C# + React: w konsoli klasa z prywatną tablicą liczb losowych i metodami wypisującymi, wyszukującymi, wybierającymi liczby nieparzyste i liczącymi średnią, a w przeglądarce jednokomponentowa aplikacja React, w której przełączniki Bootstrapa filtrują zdjęcia według kategorii, a przycisk „Pobierz” zwiększa licznik pobrań. Do tego dokumentacja metody według wzoru z arkusza, zrzuty i plik egzamin.

Kroki idą zdanie po zdaniu za treścią arkusza - każdy zaczyna się cytatem wymagania, a kod pokazuje dokładnie te linie, które je spełniają. Najpierw piszemy najprostszą wersję, której wymaga klucz, a w etapie „Dobre praktyki” przerabiamy ją na kod z prawdziwych projektów.

Co widać w podglądzie. Wyniki programu konsolowego pochodzą z prawdziwego uruchomienia kodu z danego kroku (.NET 10) - tablica jest za każdym razem inna, a liczbę do wyszukania wpisujemy dopiero po zobaczeniu tablicy. Podgląd aplikacji webowej wykonuje kod komponentu z kroku w prawdziwym Reakcie z Bootstrapem i zdjęciami z archiwum pliki3.zip: możesz przełączać kategorie i klikać „Pobierz”.

Arkusz pozwala też na C++, Javę albo Pythona w konsoli i na Angulara w części webowej - ten wariant to jedna z możliwych dróg.

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.

  1. 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ładamy folder o nazwie z numerem PESEL, a w nim konsolowa, webowa i dokumentacja. Archiwum pliki3.zip z pulpitu (hasło z arkusza: K@tegorie)) rozpakujemy przy aplikacji webowej - są w nim zdjęcia obraz1.jpg - obraz12.jpg i plik dane.txt z listą obiektów galerii.

    Projekty możemy trzymać w domyślnych folderach środowisk - na koniec pakujemy je i kopiujemy do podfolderów.

  2. Etap 2: Aplikacja konsolowa

    Kroki 2-10 · po tym etapie masz 15 z 35 kryteriów (pkt)

    Krok 2: Projekt konsola w C# 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.

    Wybieramy C#. W Visual Studio: Utwórz nowy projekt → Aplikacja konsolowa, nazwa konsola (stąd archiwum konsola.zip); w terminalu to samo daje dotnet new console -n konsola. W pliku Program.cs zastępujemy przykładowy kod dwiema klasami: Table - na tablicę i operacje na niej (arkusz dopuszcza dla klasy nazwę ogólną typu „tablica”) - oraz Program z metodą Main, od której zaczyna się program.

    konsola/Program.cs C# nowy plik
    class Table
    {
    }
    
    class Program
    {
        static void Main()
        {
        }
    }
  3. Krok 3: Dwa pola prywatne

    Kryteria: ,

    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. 2 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.

    • int[] numbers - tradycyjna tablica liczb całkowitych,
    • int count - liczba elementów,
    • private - pole widać tylko wewnątrz klasy Table; klasy potomne go nie widzą (to odróżnia private od protected, który byłby dostępny w klasie potomnej).

    Tablica nie ma jeszcze rozmiaru - utworzy ją konstruktor.

    Typowe błędy

    protected zamiast private - pole byłoby dostępne w klasach potomnych, a arkusz wprost tego zabrania (R.2.1).

    konsola/Program.cs C# zmienione: 2 linie
    class Table
    {
        private int[] numbers;
        private int count;
    }
    
    class Program
    {
        static void Main()
        {
        }
    }
  4. Krok 4: Konstruktor z losowaniem liczb 1-1000

    Kryteria: , ,

    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 ma nazwę klasy i jeden parametr size:

    • count = size - zapamiętujemy liczbę elementów,
    • new int[size] - tworzymy tablicę tej wielkości,
    • random.Next(1, 1001) - liczba losowa od 1 do 1000: górna granica w Next nie jest wliczana, dlatego piszemy 1001.

    Pętla for biegnie do count - wszystkie operacje mają być ograniczone tym polem (R.2.4).

    Typowe błędy

    random.Next(1, 1000) nigdy nie wylosuje 1000 - zakres byłby 1-999.

    konsola/Program.cs C# zmienione: 11 linii
    class Table
    {
        private int[] numbers;
        private int count;
    
        public Table(int size)
        {
            count = size;
            numbers = new int[size];
            Random random = new Random();
            for (int i = 0; i < count; i++)
            {
                numbers[i] = random.Next(1, 1001);
            }
        }
    }
    
    class Program
    {
        static void Main()
        {
        }
    }
  5. Krok 5: Metoda Display i pierwszy test w Main

    Kryteria: , , ,

    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.

    • public - metoda dostępna poza klasą (pola zostają prywatne, a dostęp do nich daje właśnie metoda),
    • void - metoda niczego nie zwraca, tylko wypisuje,
    • $"{i}: {numbers[i]}" - napis interpolowany: w klamry trafiają indeks i wartość, jak we wzorze z arkusza.

    W Main tworzymy obiekt z tablicą 50 elementów (więcej niż 20) i wywołujemy metodę. Wynik prawdziwego uruchomienia obok - każde uruchomienie losuje inne liczby.

    konsola/Program.cs C# zmienione: 11 linii
    class Table
    {
        private int[] numbers;
        private int count;
    
        public Table(int size)
        {
            count = size;
            numbers = new int[size];
            Random random = new Random();
            for (int i = 0; i < count; i++)
            {
                numbers[i] = random.Next(1, 1001);
            }
        }
    
        public void Display()
        {
            for (int i = 0; i < count; i++)
            {
                Console.WriteLine($"{i}: {numbers[i]}");
            }
        }
    }
    
    class Program
    {
        static void Main()
        {
            Table table = new Table(50);
            Console.WriteLine("Elementy tablicy:");
            table.Display();
        }
    }
    Wynik programu po tym kroku
    > konsola.exe
    Elementy tablicy:
    0: 173
    1: 856
    2: 800
    3: 665
    4: 136
    5: 574
    6: 241
    7: 558
    8: 842
    9: 845
    10: 896
    11: 429
    12: 323
    13: 428
    14: 19
    15: 818
    16: 213
    17: 777
    18: 518
    19: 544
    20: 53
    21: 308
    22: 777
    23: 14
    24: 21
    25: 903
    26: 757
    27: 935
    28: 916
    29: 103
    30: 819
    31: 546
    32: 14
    33: 42
    34: 556
    35: 970
    36: 269
    37: 867
    38: 316
    39: 693
    40: 688
    41: 858
    42: 512
    43: 99
    44: 110
    45: 569
    46: 196
    47: 595
    48: 441
    49: 669
  6. Krok 6: Metoda Find: pierwsze wystąpienie

    Kryteria: ,

    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 i od razu kończy metodę - pętla nie szuka dalej, więc zwracamy indeks pierwszego wystąpienia (R.2.5). Jeśli pętla dobiegnie końca, nic nie znaleziono i zwracamy -1 - indeks, którego w tablicy nie ma. Typ zwracany to int (R.2.3).

    Typowe błędy

    Zapamiętanie indeksu w zmiennej bez przerwania pętli zwraca OSTATNIE wystąpienie, gdy liczba powtarza się w tablicy.

    konsola/Program.cs C# zmienione: 12 linii
    class Table
    {
        private int[] numbers;
        private int count;
    
        public Table(int size)
        {
            count = size;
            numbers = new int[size];
            Random random = new Random();
            for (int i = 0; i < count; i++)
            {
                numbers[i] = random.Next(1, 1001);
            }
        }
    
        public void Display()
        {
            for (int i = 0; i < count; i++)
            {
                Console.WriteLine($"{i}: {numbers[i]}");
            }
        }
    
        public int Find(int value)
        {
            for (int i = 0; i < count; i++)
            {
                if (numbers[i] == value)
                {
                    return i;
                }
            }
            return -1;
        }
    }
    
    class Program
    {
        static void Main()
        {
            Table table = new Table(50);
            Console.WriteLine("Elementy tablicy:");
            table.Display();
        }
    }

    Wynik programu bez zmian - taki jak po kroku 5.

  7. Krok 7: Wyszukiwanie w programie głównym

    Kryteria: , ,

    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ę do wyszukania podaje użytkownik: Console.ReadLine() czyta wiersz, a int.Parse zamienia go na liczbę. Komunikat wypisujemy tylko wtedy, gdy metoda nie zwróciła -1 - przy braku liczby program nic nie mówi, jak na obrazie 1.

    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/Program.cs C# zmienione: 8 linii
    class Table
    {
        private int[] numbers;
        private int count;
    
        public Table(int size)
        {
            count = size;
            numbers = new int[size];
            Random random = new Random();
            for (int i = 0; i < count; i++)
            {
                numbers[i] = random.Next(1, 1001);
            }
        }
    
        public void Display()
        {
            for (int i = 0; i < count; i++)
            {
                Console.WriteLine($"{i}: {numbers[i]}");
            }
        }
    
        public int Find(int value)
        {
            for (int i = 0; i < count; i++)
            {
                if (numbers[i] == value)
                {
                    return i;
                }
            }
            return -1;
        }
    }
    
    class Program
    {
        static void Main()
        {
            Table table = new Table(50);
            Console.WriteLine("Elementy tablicy:");
            table.Display();
    
            Console.Write("Podaj liczbę do wyszukania: ");
            int searched = int.Parse(Console.ReadLine());
            int index = table.Find(searched);
            if (index != -1)
            {
                Console.WriteLine($"Liczba {searched} jest pod indeksem {index}");
            }
        }
    }
    Wynik programu po tym kroku
    > konsola.exe
    Elementy tablicy:
    0: 903
    1: 211
    2: 683
    3: 6
    4: 534
    5: 374
    6: 433
    7: 218
    8: 1
    9: 296
    10: 223
    11: 663
    12: 539
    13: 574
    14: 181
    15: 251
    16: 989
    17: 716
    18: 445
    19: 459
    20: 427
    21: 610
    22: 61
    23: 545
    24: 901
    25: 194
    26: 558
    27: 307
    28: 969
    29: 938
    30: 357
    31: 868
    32: 890
    33: 506
    34: 385
    35: 412
    36: 959
    37: 152
    38: 163
    39: 484
    40: 35
    41: 849
    42: 896
    43: 368
    44: 658
    45: 552
    46: 183
    47: 59
    48: 938
    49: 140
    Podaj liczbę do wyszukania: 218
    Liczba 218 jest pod indeksem 7
    
    > konsola.exe
    Elementy tablicy:
    0: 374
    1: 948
    2: 1
    3: 989
    4: 422
    5: 744
    6: 333
    7: 925
    8: 427
    9: 887
    10: 644
    11: 196
    12: 160
    13: 495
    14: 176
    15: 396
    16: 728
    17: 57
    18: 957
    19: 315
    20: 997
    21: 446
    22: 274
    23: 20
    24: 630
    25: 962
    26: 918
    27: 752
    28: 390
    29: 739
    30: 228
    31: 784
    32: 378
    33: 793
    34: 122
    35: 984
    36: 617
    37: 152
    38: 956
    39: 247
    40: 508
    41: 264
    42: 742
    43: 616
    44: 922
    45: 107
    46: 69
    47: 461
    48: 148
    49: 913
    Podaj liczbę do wyszukania: 1001
  8. Krok 8: Liczby nieparzyste i ich liczba

    Kryteria: ,

    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 oddCount, a na końcu go zwraca (int). Program główny poprzedza listę nagłówkiem i wypisuje zwróconą liczbę z komentarzem - jak na obrazie 1.

    Typowe błędy

    numbers[i] % 2 == 1 działa tylko dla liczb dodatnich - dla ujemnych reszta w C# wynosi -1. Tu liczby są od 1 do 1000, ale != 0 jest pewniejsze.

    konsola/Program.cs C# zmienione: 18 linii
    class Table
    {
        private int[] numbers;
        private int count;
    
        public Table(int size)
        {
            count = size;
            numbers = new int[size];
            Random random = new Random();
            for (int i = 0; i < count; i++)
            {
                numbers[i] = random.Next(1, 1001);
            }
        }
    
        public void Display()
        {
            for (int i = 0; i < count; i++)
            {
                Console.WriteLine($"{i}: {numbers[i]}");
            }
        }
    
        public int Find(int value)
        {
            for (int i = 0; i < count; i++)
            {
                if (numbers[i] == value)
                {
                    return i;
                }
            }
            return -1;
        }
    
        public int DisplayOdd()
        {
            int oddCount = 0;
            for (int i = 0; i < count; i++)
            {
                if (numbers[i] % 2 != 0)
                {
                    Console.WriteLine(numbers[i]);
                    oddCount++;
                }
            }
            return oddCount;
        }
    }
    
    class Program
    {
        static void Main()
        {
            Table table = new Table(50);
            Console.WriteLine("Elementy tablicy:");
            table.Display();
    
            Console.Write("Podaj liczbę do wyszukania: ");
            int searched = int.Parse(Console.ReadLine());
            int index = table.Find(searched);
            if (index != -1)
            {
                Console.WriteLine($"Liczba {searched} jest pod indeksem {index}");
            }
    
            Console.WriteLine("Liczby nieparzyste:");
            int oddCount = table.DisplayOdd();
            Console.WriteLine($"Razem nieparzystych: {oddCount}");
        }
    }
    Wynik programu po tym kroku
    > konsola.exe
    Elementy tablicy:
    0: 908
    1: 562
    2: 981
    3: 31
    4: 441
    5: 828
    6: 870
    7: 76
    8: 684
    9: 833
    10: 892
    11: 118
    12: 209
    13: 653
    14: 344
    15: 686
    16: 269
    17: 526
    18: 761
    19: 228
    20: 362
    21: 146
    22: 240
    23: 597
    24: 197
    25: 472
    26: 893
    27: 550
    28: 850
    29: 554
    30: 69
    31: 15
    32: 40
    33: 169
    34: 665
    35: 667
    36: 201
    37: 69
    38: 219
    39: 530
    40: 275
    41: 171
    42: 150
    43: 620
    44: 195
    45: 510
    46: 826
    47: 930
    48: 153
    49: 339
    Podaj liczbę do wyszukania: 1001
    Liczby nieparzyste:
    981
    31
    441
    833
    209
    653
    269
    761
    597
    197
    893
    69
    15
    169
    665
    667
    201
    69
    219
    275
    171
    195
    153
    339
    Razem nieparzystych: 24
  9. Krok 9: Średnia arytmetyczna

    Kryteria: , , ,

    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.

    Sumujemy elementy, a wynik dzielimy przez count. (double)sum zamienia sumę na liczbę rzeczywistą przed dzieleniem - inaczej C# podzieliłby dwie liczby całkowite i obciął część ułamkową. Metoda zwraca double. W Main formatem :F2 pokazujemy dwa miejsca po przecinku (przecinek zamiast kropki wynika z polskich ustawień systemu).

    Typowe błędy

    return sum / count; przy dwóch liczbach int daje dzielenie całkowite - średnia 525,73 wyjdzie jako 525.

    konsola/Program.cs C# zmienione: 13 linii
    class Table
    {
        private int[] numbers;
        private int count;
    
        public Table(int size)
        {
            count = size;
            numbers = new int[size];
            Random random = new Random();
            for (int i = 0; i < count; i++)
            {
                numbers[i] = random.Next(1, 1001);
            }
        }
    
        public void Display()
        {
            for (int i = 0; i < count; i++)
            {
                Console.WriteLine($"{i}: {numbers[i]}");
            }
        }
    
        public int Find(int value)
        {
            for (int i = 0; i < count; i++)
            {
                if (numbers[i] == value)
                {
                    return i;
                }
            }
            return -1;
        }
    
        public int DisplayOdd()
        {
            int oddCount = 0;
            for (int i = 0; i < count; i++)
            {
                if (numbers[i] % 2 != 0)
                {
                    Console.WriteLine(numbers[i]);
                    oddCount++;
                }
            }
            return oddCount;
        }
    
        public double Average()
        {
            int sum = 0;
            for (int i = 0; i < count; i++)
            {
                sum += numbers[i];
            }
            return (double)sum / count;
        }
    }
    
    class Program
    {
        static void Main()
        {
            Table table = new Table(50);
            Console.WriteLine("Elementy tablicy:");
            table.Display();
    
            Console.Write("Podaj liczbę do wyszukania: ");
            int searched = int.Parse(Console.ReadLine());
            int index = table.Find(searched);
            if (index != -1)
            {
                Console.WriteLine($"Liczba {searched} jest pod indeksem {index}");
            }
    
            Console.WriteLine("Liczby nieparzyste:");
            int oddCount = table.DisplayOdd();
            Console.WriteLine($"Razem nieparzystych: {oddCount}");
    
            double average = table.Average();
            Console.WriteLine($"Średnia wszystkich elementów: {average:F2}");
        }
    }
    Wynik programu po tym kroku
    > konsola.exe
    Elementy tablicy:
    0: 49
    1: 982
    2: 152
    3: 863
    4: 482
    5: 110
    6: 544
    7: 857
    8: 72
    9: 501
    10: 311
    11: 457
    12: 984
    13: 291
    14: 814
    15: 832
    16: 850
    17: 441
    18: 413
    19: 829
    20: 191
    21: 703
    22: 421
    23: 808
    24: 616
    25: 162
    26: 43
    27: 314
    28: 755
    29: 945
    30: 103
    31: 134
    32: 169
    33: 393
    34: 988
    35: 222
    36: 555
    37: 299
    38: 547
    39: 82
    40: 590
    41: 794
    42: 234
    43: 340
    44: 226
    45: 158
    46: 377
    47: 944
    48: 180
    49: 716
    Podaj liczbę do wyszukania: 857
    Liczba 857 jest pod indeksem 7
    Liczby nieparzyste:
    49
    863
    857
    501
    311
    457
    291
    441
    413
    829
    191
    703
    421
    43
    755
    945
    103
    169
    393
    555
    299
    547
    377
    Razem nieparzystych: 23
    Średnia wszystkich elementów: 476,86
  10. Krok 10: Folder konsolowa: archiwum, źródło i plik wykonywalny

    ok. 2 min
    Polecenie z arkusza

    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 pakujemy do konsola.zip, obok kopiujemy Program.cs i plik wykonywalny konsola.exe z bin\Debug\net... (albo bin\Release\...). Archiwum robimy na samym końcu pracy, po dopisaniu komentarza z części III - inaczej w konsola.zip zostałaby wersja bez dokumentacji.

    Wynik programu bez zmian - taki jak po kroku 9.

  11. Etap 3: Aplikacja webowa

    Kroki 11-23 · po tym etapie masz 24 z 35 kryteriów (pkt)

    Krok 11: Nowy projekt React w Vite

    ok. 4 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.

    Wybieramy React. Projekt zakładamy w terminalu narzędziem Vite:

    npm create vite@latest web -- --template react
    cd web
    npm install
    

    Nazwa web od razu pasuje do archiwum web.zip. Vite tworzy src/main.jsx (wstawia komponent App do strony) i src/App.jsx z przykładowym licznikiem, który zastąpimy. npm run dev uruchamia serwer pod http://localhost:5173.

    web/src/main.jsx JSX nowy plik
    import { StrictMode } from 'react'
    import { createRoot } from 'react-dom/client'
    import './index.css'
    import App from './App.jsx'
    
    createRoot(document.getElementById('root')).render(
      <StrictMode>
        <App />
      </StrictMode>,
    )

    Wynik programu bez zmian - taki jak po kroku 9.

  12. Krok 12: Biblioteka Bootstrap w projekcie

    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

    Przyciski oraz pola switch są stylowane zgodnie z przykładami w tabeli 1. React.js: To use Bootstrap add: import 'bootstrap/dist/css/bootstrap.css'.

    npm install bootstrap w folderze web, a potem import z tabeli 1 w main.jsx - w miejsce import './index.css', bo arkusz stylów z szablonu Vite wyśrodkowuje stronę i ustawia ciemne tło, a galeria ma zaczynać się przy lewej krawędzi (obraz 2).

    Typowe błędy

    npm install bootstrap uruchomione poza folderem web instaluje pakiet gdzie indziej - import zgłosi błąd „Failed to resolve import”.

    web/src/main.jsx JSX zmienione: 1 linia
    import { StrictMode } from 'react'
    import { createRoot } from 'react-dom/client'
    import 'bootstrap/dist/css/bootstrap.css'
    import App from './App.jsx'
    
    createRoot(document.getElementById('root')).render(
      <StrictMode>
        <App />
      </StrictMode>,
    )

    Wynik programu bez zmian - taki jak po kroku 9.

  13. Krok 13: Zdjęcia w folderze public/assets

    ok. 2 min
    Polecenie z arkusza

    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).

    Rozpakowujemy pliki3.zip (hasło K@tegorie)) i kopiujemy dwanaście zdjęć do web/public/assets. Pliki z folderu public Vite podaje pod adresem od korzenia strony, więc zdjęcie jest dostępne jako assets/obraz1.jpg - nazwę pliku z danych wystarczy dokleić do assets/. (Pliki z src/assets trzeba by importować w kodzie jeden po drugim.)

    Wynik programu bez zmian - taki jak po kroku 9.

  14. Krok 14: Komponent App z nagłówkiem

    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

    Aplikacja składa się z jednego komponentu. Komponent składa się z: Nagłówka pierwszego stopnia o treści: „Kategorie zdjęć”.

    Zastępujemy całą zawartość App.jsx. Komponent w React to funkcja zwracająca JSX - zapis podobny do HTML wewnątrz JavaScriptu. Zwracamy jeden element div, a w nim <h1>. Plik App.css i przykładowe obrazki z szablonu nie są już potrzebne.

    web/src/App.jsx JSX nowy plik
    function App() {
      return (
        <div>
          <h1>Kategorie zdjęć</h1>
        </div>
      )
    }
    
    export default App
    Podgląd strony po tym kroku
  15. Krok 15: Tablica zdjęć z pliku dane.txt w stanie komponentu

    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).

    Kopiujemy obiekty z dane.txt do tablicy i trzymamy ją w stanie komponentu: useState(...) zwraca bieżącą tablicę photos i funkcję setPhotos, którą podmienimy ją po kliknięciu „Pobierz” - wtedy React narysuje widok od nowa z nową liczbą pobrań. useState importujemy z react na górze pliku.

    Dane przepisujemy tak, jak są w pliku (także „Rotwailer” - to dane z arkusza, nie nasz tekst). Na stronie nic jeszcze nie widać.

    web/src/App.jsx JSX zmienione: 17 linii
    import { useState } from 'react'
    
    function App() {
      const [photos, setPhotos] = useState([
        { id: 0, alt: 'Mak', filename: 'obraz1.jpg', category: 1, downloads: 35 },
        { id: 1, alt: 'Bukiet', filename: 'obraz2.jpg', category: 1, downloads: 43 },
        { id: 2, alt: 'Dalmatyńczyk', filename: 'obraz3.jpg', category: 2, downloads: 2 },
        { id: 3, alt: 'Świnka morska', filename: 'obraz4.jpg', category: 2, downloads: 53 },
        { id: 4, alt: 'Rotwailer', filename: 'obraz5.jpg', category: 2, downloads: 43 },
        { id: 5, alt: 'Audi', filename: 'obraz6.jpg', category: 3, downloads: 11 },
        { id: 6, alt: 'kotki', filename: 'obraz7.jpg', category: 2, downloads: 22 },
        { id: 7, alt: 'Róża', filename: 'obraz8.jpg', category: 1, downloads: 33 },
        { id: 8, alt: 'Świnka morska', filename: 'obraz9.jpg', category: 2, downloads: 123 },
        { id: 9, alt: 'Foksterier', filename: 'obraz10.jpg', category: 2, downloads: 22 },
        { id: 10, alt: 'Szczeniak', filename: 'obraz11.jpg', category: 2, downloads: 12 },
        { id: 11, alt: 'Garbus', filename: 'obraz12.jpg', category: 3, downloads: 321 },
      ])
    
      return (
        <div>
          <h1>Kategorie zdjęć</h1>
        </div>
      )
    }
    
    export default App
    Podgląd strony po tym kroku
  16. 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 obiekt visible: kluczem jest numer kategorii, a wartością true/false - na start wszystkie true.

    Przełącznik przenosimy 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 powiązana etykieta. W JSX class piszemy jako className, for jako htmlFor, a input musi być domknięty /> (R.3.3). checked={visible[1]} pokazuje stan kategorii 1.

    React ostrzeże w konsoli przeglądarki, że pole ma checked bez onChange - obsługę dopiszemy za chwilę.

    web/src/App.jsx JSX zmienione: 5 linii
    import { useState } from 'react'
    
    function App() {
      const [photos, setPhotos] = useState([
        { id: 0, alt: 'Mak', filename: 'obraz1.jpg', category: 1, downloads: 35 },
        { id: 1, alt: 'Bukiet', filename: 'obraz2.jpg', category: 1, downloads: 43 },
        { id: 2, alt: 'Dalmatyńczyk', filename: 'obraz3.jpg', category: 2, downloads: 2 },
        { id: 3, alt: 'Świnka morska', filename: 'obraz4.jpg', category: 2, downloads: 53 },
        { id: 4, alt: 'Rotwailer', filename: 'obraz5.jpg', category: 2, downloads: 43 },
        { id: 5, alt: 'Audi', filename: 'obraz6.jpg', category: 3, downloads: 11 },
        { id: 6, alt: 'kotki', filename: 'obraz7.jpg', category: 2, downloads: 22 },
        { id: 7, alt: 'Róża', filename: 'obraz8.jpg', category: 1, downloads: 33 },
        { id: 8, alt: 'Świnka morska', filename: 'obraz9.jpg', category: 2, downloads: 123 },
        { id: 9, alt: 'Foksterier', filename: 'obraz10.jpg', category: 2, downloads: 22 },
        { id: 10, alt: 'Szczeniak', filename: 'obraz11.jpg', category: 2, downloads: 12 },
        { id: 11, alt: 'Garbus', filename: 'obraz12.jpg', category: 3, downloads: 321 },
      ])
      const [visible, setVisible] = useState({ 1: true, 2: true, 3: true })
    
      return (
        <div>
          <h1>Kategorie zdjęć</h1>
          <div className="form-check form-switch form-check-inline">
            <input className="form-check-input" type="checkbox" id="flowers" checked={visible[1]} />
            <label className="form-check-label" htmlFor="flowers">Kwiaty</label>
          </div>
        </div>
      )
    }
    
    export default App
    Podgląd strony po tym kroku
  17. 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. Identyfikatory muszą być różne - inaczej etykiety wskazywałyby pierwszy przełącznik.

    web/src/App.jsx JSX zmienione: 8 linii
    import { useState } from 'react'
    
    function App() {
      const [photos, setPhotos] = useState([
        { id: 0, alt: 'Mak', filename: 'obraz1.jpg', category: 1, downloads: 35 },
        { id: 1, alt: 'Bukiet', filename: 'obraz2.jpg', category: 1, downloads: 43 },
        { id: 2, alt: 'Dalmatyńczyk', filename: 'obraz3.jpg', category: 2, downloads: 2 },
        { id: 3, alt: 'Świnka morska', filename: 'obraz4.jpg', category: 2, downloads: 53 },
        { id: 4, alt: 'Rotwailer', filename: 'obraz5.jpg', category: 2, downloads: 43 },
        { id: 5, alt: 'Audi', filename: 'obraz6.jpg', category: 3, downloads: 11 },
        { id: 6, alt: 'kotki', filename: 'obraz7.jpg', category: 2, downloads: 22 },
        { id: 7, alt: 'Róża', filename: 'obraz8.jpg', category: 1, downloads: 33 },
        { id: 8, alt: 'Świnka morska', filename: 'obraz9.jpg', category: 2, downloads: 123 },
        { id: 9, alt: 'Foksterier', filename: 'obraz10.jpg', category: 2, downloads: 22 },
        { id: 10, alt: 'Szczeniak', filename: 'obraz11.jpg', category: 2, downloads: 12 },
        { id: 11, alt: 'Garbus', filename: 'obraz12.jpg', category: 3, downloads: 321 },
      ])
      const [visible, setVisible] = useState({ 1: true, 2: true, 3: true })
    
      return (
        <div>
          <h1>Kategorie zdjęć</h1>
          <div className="form-check form-switch form-check-inline">
            <input className="form-check-input" type="checkbox" id="flowers" checked={visible[1]} />
            <label className="form-check-label" htmlFor="flowers">Kwiaty</label>
          </div>
          <div className="form-check form-switch form-check-inline">
            <input className="form-check-input" type="checkbox" id="animals" checked={visible[2]} />
            <label className="form-check-label" htmlFor="animals">Zwierzęta</label>
          </div>
          <div className="form-check form-switch form-check-inline">
            <input className="form-check-input" type="checkbox" id="cars" checked={visible[3]} />
            <label className="form-check-label" htmlFor="cars">Samochody</label>
          </div>
        </div>
      )
    }
    
    export default App
    Podgląd strony po tym kroku
  18. 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.

    Funkcja toggleCategory(category) odwraca stan jednej kategorii. Zapis { ...visible, [category]: !visible[category] } tworzy nowy obiekt: kopię starego z jedną zmienioną wartością (React rysuje widok od nowa tylko wtedy, gdy dostanie nowy obiekt).

    Każdy przełącznik wywołuje ją zdarzeniem onChange={() => toggleCategory(1)} - przekazujemy funkcję strzałkową, a nie wynik wywołania (R.3.7). Przełączniki w podglądzie już się przestawiają.

    Typowe błędy

    onChange={toggleCategory(1)} wywoła funkcję już przy rysowaniu komponentu i zapętli aplikację. Klucz wymienia też onchange="..." w stylu HTML jako powód niezaliczenia CAŁEGO rezultatu 3.

    web/src/App.jsx JSX zmienione: 10 linii
    import { useState } from 'react'
    
    function App() {
      const [photos, setPhotos] = useState([
        { id: 0, alt: 'Mak', filename: 'obraz1.jpg', category: 1, downloads: 35 },
        { id: 1, alt: 'Bukiet', filename: 'obraz2.jpg', category: 1, downloads: 43 },
        { id: 2, alt: 'Dalmatyńczyk', filename: 'obraz3.jpg', category: 2, downloads: 2 },
        { id: 3, alt: 'Świnka morska', filename: 'obraz4.jpg', category: 2, downloads: 53 },
        { id: 4, alt: 'Rotwailer', filename: 'obraz5.jpg', category: 2, downloads: 43 },
        { id: 5, alt: 'Audi', filename: 'obraz6.jpg', category: 3, downloads: 11 },
        { id: 6, alt: 'kotki', filename: 'obraz7.jpg', category: 2, downloads: 22 },
        { id: 7, alt: 'Róża', filename: 'obraz8.jpg', category: 1, downloads: 33 },
        { id: 8, alt: 'Świnka morska', filename: 'obraz9.jpg', category: 2, downloads: 123 },
        { id: 9, alt: 'Foksterier', filename: 'obraz10.jpg', category: 2, downloads: 22 },
        { id: 10, alt: 'Szczeniak', filename: 'obraz11.jpg', category: 2, downloads: 12 },
        { id: 11, alt: 'Garbus', filename: 'obraz12.jpg', category: 3, downloads: 321 },
      ])
      const [visible, setVisible] = useState({ 1: true, 2: true, 3: true })
    
      function toggleCategory(category) {
        setVisible({ ...visible, [category]: !visible[category] })
      }
    
      return (
        <div>
          <h1>Kategorie zdjęć</h1>
          <div className="form-check form-switch form-check-inline">
            <input className="form-check-input" type="checkbox" id="flowers" checked={visible[1]}
              onChange={() => toggleCategory(1)} />
            <label className="form-check-label" htmlFor="flowers">Kwiaty</label>
          </div>
          <div className="form-check form-switch form-check-inline">
            <input className="form-check-input" type="checkbox" id="animals" checked={visible[2]}
              onChange={() => toggleCategory(2)} />
            <label className="form-check-label" htmlFor="animals">Zwierzęta</label>
          </div>
          <div className="form-check form-switch form-check-inline">
            <input className="form-check-input" type="checkbox" id="cars" checked={visible[3]}
              onChange={() => toggleCategory(3)} />
            <label className="form-check-label" htmlFor="cars">Samochody</label>
          </div>
        </div>
      )
    }
    
    export default App
    Podgląd strony po tym kroku
  19. Krok 19: Bloki zdjęć w pętli

    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ęć.

    Funkcja renderPhoto(photo) zwraca JSX jednego bloku: zdjęcie z adresem 'assets/' + photo.filename i tekstem alternatywnym z danych, <h4> z liczbą pobrań i zielony przycisk btn btn-success (R.3.3).

    Pętlą jest photos.map(...) - dla każdego obiektu tablicy wywołuje renderPhoto i zwraca listę bloków. Działa dla dowolnej liczby zdjęć (R.3.4). Atrybut key to wymóg Reacta przy listach: unikalny identyfikator, po którym React rozpoznaje bloki przy ponownym rysowaniu. Klasa d-inline-block stawia bloki obok siebie - zawijają się do kolejnego wiersza, gdy zabraknie miejsca.

    web/src/App.jsx JSX zmienione: 13 linii
    import { useState } from 'react'
    
    function App() {
      const [photos, setPhotos] = useState([
        { id: 0, alt: 'Mak', filename: 'obraz1.jpg', category: 1, downloads: 35 },
        { id: 1, alt: 'Bukiet', filename: 'obraz2.jpg', category: 1, downloads: 43 },
        { id: 2, alt: 'Dalmatyńczyk', filename: 'obraz3.jpg', category: 2, downloads: 2 },
        { id: 3, alt: 'Świnka morska', filename: 'obraz4.jpg', category: 2, downloads: 53 },
        { id: 4, alt: 'Rotwailer', filename: 'obraz5.jpg', category: 2, downloads: 43 },
        { id: 5, alt: 'Audi', filename: 'obraz6.jpg', category: 3, downloads: 11 },
        { id: 6, alt: 'kotki', filename: 'obraz7.jpg', category: 2, downloads: 22 },
        { id: 7, alt: 'Róża', filename: 'obraz8.jpg', category: 1, downloads: 33 },
        { id: 8, alt: 'Świnka morska', filename: 'obraz9.jpg', category: 2, downloads: 123 },
        { id: 9, alt: 'Foksterier', filename: 'obraz10.jpg', category: 2, downloads: 22 },
        { id: 10, alt: 'Szczeniak', filename: 'obraz11.jpg', category: 2, downloads: 12 },
        { id: 11, alt: 'Garbus', filename: 'obraz12.jpg', category: 3, downloads: 321 },
      ])
      const [visible, setVisible] = useState({ 1: true, 2: true, 3: true })
    
      function toggleCategory(category) {
        setVisible({ ...visible, [category]: !visible[category] })
      }
    
      function renderPhoto(photo) {
        return (
          <div key={photo.id} className="d-inline-block">
            <img src={'assets/' + photo.filename} alt={photo.alt} />
            <h4>Pobrań: {photo.downloads}</h4>
            <button type="button" className="btn btn-success">Pobierz</button>
          </div>
        )
      }
    
      return (
        <div>
          <h1>Kategorie zdjęć</h1>
          <div className="form-check form-switch form-check-inline">
            <input className="form-check-input" type="checkbox" id="flowers" checked={visible[1]}
              onChange={() => toggleCategory(1)} />
            <label className="form-check-label" htmlFor="flowers">Kwiaty</label>
          </div>
          <div className="form-check form-switch form-check-inline">
            <input className="form-check-input" type="checkbox" id="animals" checked={visible[2]}
              onChange={() => toggleCategory(2)} />
            <label className="form-check-label" htmlFor="animals">Zwierzęta</label>
          </div>
          <div className="form-check form-switch form-check-inline">
            <input className="form-check-input" type="checkbox" id="cars" checked={visible[3]}
              onChange={() => toggleCategory(3)} />
            <label className="form-check-label" htmlFor="cars">Samochody</label>
          </div>
          <div>
            {photos.map((photo) => renderPhoto(photo))}
          </div>
        </div>
      )
    }
    
    export default App
    Podgląd strony po tym kroku
  20. 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.

    Zaokrąglone rogi daje klasa Bootstrapa rounded. Margines 5 px piszemy stylem: w JSX atrybut style przyjmuje obiekt JavaScriptu, stąd podwójne klamry - zewnętrzne to wstawienie wyrażenia, wewnętrzne to obiekt { margin: '5px' }. (Klasy Bootstrapa m-1 i m-2 dają 4 i 8 px, nie 5.)

    Typowe błędy

    style="margin: 5px" jak w HTML to w JSX błąd - React wymaga obiektu.

    web/src/App.jsx JSX zmienione: 1 linia
    import { useState } from 'react'
    
    function App() {
      const [photos, setPhotos] = useState([
        { id: 0, alt: 'Mak', filename: 'obraz1.jpg', category: 1, downloads: 35 },
        { id: 1, alt: 'Bukiet', filename: 'obraz2.jpg', category: 1, downloads: 43 },
        { id: 2, alt: 'Dalmatyńczyk', filename: 'obraz3.jpg', category: 2, downloads: 2 },
        { id: 3, alt: 'Świnka morska', filename: 'obraz4.jpg', category: 2, downloads: 53 },
        { id: 4, alt: 'Rotwailer', filename: 'obraz5.jpg', category: 2, downloads: 43 },
        { id: 5, alt: 'Audi', filename: 'obraz6.jpg', category: 3, downloads: 11 },
        { id: 6, alt: 'kotki', filename: 'obraz7.jpg', category: 2, downloads: 22 },
        { id: 7, alt: 'Róża', filename: 'obraz8.jpg', category: 1, downloads: 33 },
        { id: 8, alt: 'Świnka morska', filename: 'obraz9.jpg', category: 2, downloads: 123 },
        { id: 9, alt: 'Foksterier', filename: 'obraz10.jpg', category: 2, downloads: 22 },
        { id: 10, alt: 'Szczeniak', filename: 'obraz11.jpg', category: 2, downloads: 12 },
        { id: 11, alt: 'Garbus', filename: 'obraz12.jpg', category: 3, downloads: 321 },
      ])
      const [visible, setVisible] = useState({ 1: true, 2: true, 3: true })
    
      function toggleCategory(category) {
        setVisible({ ...visible, [category]: !visible[category] })
      }
    
      function renderPhoto(photo) {
        return (
          <div key={photo.id} className="d-inline-block">
            <img src={'assets/' + photo.filename} alt={photo.alt} className="rounded" style={{ margin: '5px' }} />
            <h4>Pobrań: {photo.downloads}</h4>
            <button type="button" className="btn btn-success">Pobierz</button>
          </div>
        )
      }
    
      return (
        <div>
          <h1>Kategorie zdjęć</h1>
          <div className="form-check form-switch form-check-inline">
            <input className="form-check-input" type="checkbox" id="flowers" checked={visible[1]}
              onChange={() => toggleCategory(1)} />
            <label className="form-check-label" htmlFor="flowers">Kwiaty</label>
          </div>
          <div className="form-check form-switch form-check-inline">
            <input className="form-check-input" type="checkbox" id="animals" checked={visible[2]}
              onChange={() => toggleCategory(2)} />
            <label className="form-check-label" htmlFor="animals">Zwierzęta</label>
          </div>
          <div className="form-check form-switch form-check-inline">
            <input className="form-check-input" type="checkbox" id="cars" checked={visible[3]}
              onChange={() => toggleCategory(3)} />
            <label className="form-check-label" htmlFor="cars">Samochody</label>
          </div>
          <div>
            {photos.map((photo) => renderPhoto(photo))}
          </div>
        </div>
      )
    }
    
    export default App
    Podgląd strony po tym kroku
  21. Krok 21: Warunek: 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ęć.

    Funkcja renderująca dostaje warunek: jeśli kategoria zdjęcia jest wyłączona (visible[photo.category] ma wartość false), zwraca null - a null React po prostu pomija. To dokładnie zapis z klucza: funkcja renderująca z warunkiem i return (R.3.5).

    Wyłącz w podglądzie „Zwierzęta” - zostają kwiaty i samochody (obraz 3). Włączone same „Samochody” dają dwa zdjęcia.

    web/src/App.jsx JSX zmienione: 3 linie
    import { useState } from 'react'
    
    function App() {
      const [photos, setPhotos] = useState([
        { id: 0, alt: 'Mak', filename: 'obraz1.jpg', category: 1, downloads: 35 },
        { id: 1, alt: 'Bukiet', filename: 'obraz2.jpg', category: 1, downloads: 43 },
        { id: 2, alt: 'Dalmatyńczyk', filename: 'obraz3.jpg', category: 2, downloads: 2 },
        { id: 3, alt: 'Świnka morska', filename: 'obraz4.jpg', category: 2, downloads: 53 },
        { id: 4, alt: 'Rotwailer', filename: 'obraz5.jpg', category: 2, downloads: 43 },
        { id: 5, alt: 'Audi', filename: 'obraz6.jpg', category: 3, downloads: 11 },
        { id: 6, alt: 'kotki', filename: 'obraz7.jpg', category: 2, downloads: 22 },
        { id: 7, alt: 'Róża', filename: 'obraz8.jpg', category: 1, downloads: 33 },
        { id: 8, alt: 'Świnka morska', filename: 'obraz9.jpg', category: 2, downloads: 123 },
        { id: 9, alt: 'Foksterier', filename: 'obraz10.jpg', category: 2, downloads: 22 },
        { id: 10, alt: 'Szczeniak', filename: 'obraz11.jpg', category: 2, downloads: 12 },
        { id: 11, alt: 'Garbus', filename: 'obraz12.jpg', category: 3, downloads: 321 },
      ])
      const [visible, setVisible] = useState({ 1: true, 2: true, 3: true })
    
      function toggleCategory(category) {
        setVisible({ ...visible, [category]: !visible[category] })
      }
    
      function renderPhoto(photo) {
        if (!visible[photo.category]) {
          return null
        }
        return (
          <div key={photo.id} className="d-inline-block">
            <img src={'assets/' + photo.filename} alt={photo.alt} className="rounded" style={{ margin: '5px' }} />
            <h4>Pobrań: {photo.downloads}</h4>
            <button type="button" className="btn btn-success">Pobierz</button>
          </div>
        )
      }
    
      return (
        <div>
          <h1>Kategorie zdjęć</h1>
          <div className="form-check form-switch form-check-inline">
            <input className="form-check-input" type="checkbox" id="flowers" checked={visible[1]}
              onChange={() => toggleCategory(1)} />
            <label className="form-check-label" htmlFor="flowers">Kwiaty</label>
          </div>
          <div className="form-check form-switch form-check-inline">
            <input className="form-check-input" type="checkbox" id="animals" checked={visible[2]}
              onChange={() => toggleCategory(2)} />
            <label className="form-check-label" htmlFor="animals">Zwierzęta</label>
          </div>
          <div className="form-check form-switch form-check-inline">
            <input className="form-check-input" type="checkbox" id="cars" checked={visible[3]}
              onChange={() => toggleCategory(3)} />
            <label className="form-check-label" htmlFor="cars">Samochody</label>
          </div>
          <div>
            {photos.map((photo) => renderPhoto(photo))}
          </div>
        </div>
      )
    }
    
    export default App
    Podgląd strony po tym kroku
  22. 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.

    handleDownload(id) buduje nową tablicę: map przepisuje wszystkie zdjęcia, a to o klikniętym id zastępuje kopią z downloads większym o jeden. setPhotos(updated) zapisuje tablicę w stanie i React od razu pokazuje nową liczbę (R.3.10).

    Przycisk przekazuje id swojego zdjęcia: onClick={() => handleDownload(photo.id)} (R.3.6). Kliknij kilka razy „Pobierz” przy drugim zdjęciu - liczba rośnie (obraz 4).

    Typowe błędy

    photo.downloads++ bez setPhotos zmienia liczbę w tablicy, ale React o tym nie wie - ekran się nie odświeży.

    web/src/App.jsx JSX zmienione: 9 linii
    import { useState } from 'react'
    
    function App() {
      const [photos, setPhotos] = useState([
        { id: 0, alt: 'Mak', filename: 'obraz1.jpg', category: 1, downloads: 35 },
        { id: 1, alt: 'Bukiet', filename: 'obraz2.jpg', category: 1, downloads: 43 },
        { id: 2, alt: 'Dalmatyńczyk', filename: 'obraz3.jpg', category: 2, downloads: 2 },
        { id: 3, alt: 'Świnka morska', filename: 'obraz4.jpg', category: 2, downloads: 53 },
        { id: 4, alt: 'Rotwailer', filename: 'obraz5.jpg', category: 2, downloads: 43 },
        { id: 5, alt: 'Audi', filename: 'obraz6.jpg', category: 3, downloads: 11 },
        { id: 6, alt: 'kotki', filename: 'obraz7.jpg', category: 2, downloads: 22 },
        { id: 7, alt: 'Róża', filename: 'obraz8.jpg', category: 1, downloads: 33 },
        { id: 8, alt: 'Świnka morska', filename: 'obraz9.jpg', category: 2, downloads: 123 },
        { id: 9, alt: 'Foksterier', filename: 'obraz10.jpg', category: 2, downloads: 22 },
        { id: 10, alt: 'Szczeniak', filename: 'obraz11.jpg', category: 2, downloads: 12 },
        { id: 11, alt: 'Garbus', filename: 'obraz12.jpg', category: 3, downloads: 321 },
      ])
      const [visible, setVisible] = useState({ 1: true, 2: true, 3: true })
    
      function toggleCategory(category) {
        setVisible({ ...visible, [category]: !visible[category] })
      }
    
      function handleDownload(id) {
        const updated = photos.map((photo) =>
          photo.id === id ? { ...photo, downloads: photo.downloads + 1 } : photo
        )
        setPhotos(updated)
      }
    
      function renderPhoto(photo) {
        if (!visible[photo.category]) {
          return null
        }
        return (
          <div key={photo.id} className="d-inline-block">
            <img src={'assets/' + photo.filename} alt={photo.alt} className="rounded" style={{ margin: '5px' }} />
            <h4>Pobrań: {photo.downloads}</h4>
            <button type="button" className="btn btn-success"
              onClick={() => handleDownload(photo.id)}>Pobierz</button>
          </div>
        )
      }
    
      return (
        <div>
          <h1>Kategorie zdjęć</h1>
          <div className="form-check form-switch form-check-inline">
            <input className="form-check-input" type="checkbox" id="flowers" checked={visible[1]}
              onChange={() => toggleCategory(1)} />
            <label className="form-check-label" htmlFor="flowers">Kwiaty</label>
          </div>
          <div className="form-check form-switch form-check-inline">
            <input className="form-check-input" type="checkbox" id="animals" checked={visible[2]}
              onChange={() => toggleCategory(2)} />
            <label className="form-check-label" htmlFor="animals">Zwierzęta</label>
          </div>
          <div className="form-check form-switch form-check-inline">
            <input className="form-check-input" type="checkbox" id="cars" checked={visible[3]}
              onChange={() => toggleCategory(3)} />
            <label className="form-check-label" htmlFor="cars">Samochody</label>
          </div>
          <div>
            {photos.map((photo) => renderPhoto(photo))}
          </div>
        </div>
      )
    }
    
    export default App
    Podgląd strony po tym kroku
  23. 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.

    Pakujemy folder web do web.zip, ale bez node_modules - npm install odtwarza go z package.json, a z nim archiwum miałoby setki megabajtów. Obok kopiujemy zmienione pliki: App.jsx i main.jsx.

    Podgląd strony bez zmian - taki jak po kroku 22.

  24. Etap 4: Dokumentacja aplikacji

    Kroki 24-27 · po tym etapie masz 35 z 35 kryteriów (pkt)

    Krok 24: Komentarz dokumentujący metodę 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.

    Komentarz wieloliniowy /* ... */ nad metodą Find, z polami z listingu 1: nazwa i opis działania (R.4.2), parametr value z opisem - metoda ma jeden argument, więc nie piszemy „brak” (R.4.3), zwracany typ int z opisem (R.4.4) i autor (R.4.5). Na egzaminie w polu autor wpisujemy numer PESEL - tu stoi Bartosz Bryniarski. Liczba gwiazdek jest dowolna.

    Typowe błędy

    Opis niezgodny z kodem (np. „parametry: brak” przy metodzie z argumentem) nie spełnia R.4.3 - klucz sprawdza zgodność ze stanem faktycznym.

    konsola/Program.cs C# zmienione: 9 linii
    class Table
    {
        private int[] numbers;
        private int count;
    
        public Table(int size)
        {
            count = size;
            numbers = new int[size];
            Random random = new Random();
            for (int i = 0; i < count; i++)
            {
                numbers[i] = random.Next(1, 1001);
            }
        }
    
        public void Display()
        {
            for (int i = 0; i < count; i++)
            {
                Console.WriteLine($"{i}: {numbers[i]}");
            }
        }
    
        /*
         **********************************************
         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
         **********************************************
         */
        public int Find(int value)
        {
            for (int i = 0; i < count; i++)
            {
                if (numbers[i] == value)
                {
                    return i;
                }
            }
            return -1;
        }
    
        public int DisplayOdd()
        {
            int oddCount = 0;
            for (int i = 0; i < count; i++)
            {
                if (numbers[i] % 2 != 0)
                {
                    Console.WriteLine(numbers[i]);
                    oddCount++;
                }
            }
            return oddCount;
        }
    
        public double Average()
        {
            int sum = 0;
            for (int i = 0; i < count; i++)
            {
                sum += numbers[i];
            }
            return (double)sum / count;
        }
    }
    
    class Program
    {
        static void Main()
        {
            Table table = new Table(50);
            Console.WriteLine("Elementy tablicy:");
            table.Display();
    
            Console.Write("Podaj liczbę do wyszukania: ");
            int searched = int.Parse(Console.ReadLine());
            int index = table.Find(searched);
            if (index != -1)
            {
                Console.WriteLine($"Liczba {searched} jest pod indeksem {index}");
            }
    
            Console.WriteLine("Liczby nieparzyste:");
            int oddCount = table.DisplayOdd();
            Console.WriteLine($"Razem nieparzystych: {oddCount}");
    
            double average = table.Average();
            Console.WriteLine($"Średnia wszystkich elementów: {average:F2}");
        }
    }
    Wynik programu po tym kroku
    > konsola.exe
    Elementy tablicy:
    0: 752
    1: 294
    2: 551
    3: 149
    4: 659
    5: 166
    6: 336
    7: 41
    8: 531
    9: 910
    10: 988
    11: 112
    12: 50
    13: 887
    14: 53
    15: 202
    16: 284
    17: 982
    18: 390
    19: 228
    20: 590
    21: 607
    22: 43
    23: 230
    24: 870
    25: 635
    26: 642
    27: 475
    28: 199
    29: 14
    30: 786
    31: 138
    32: 360
    33: 318
    34: 27
    35: 315
    36: 842
    37: 177
    38: 164
    39: 913
    40: 65
    41: 396
    42: 218
    43: 593
    44: 535
    45: 843
    46: 672
    47: 886
    48: 399
    49: 37
    Podaj liczbę do wyszukania: 41
    Liczba 41 jest pod indeksem 7
    Liczby nieparzyste:
    551
    149
    659
    41
    531
    887
    53
    607
    43
    635
    475
    199
    27
    315
    177
    913
    65
    593
    535
    843
    399
    37
    Razem nieparzystych: 22
    Średnia wszystkich elementów: 431,08
  25. Krok 25: Zrzuty konsola1 i konsola2

    Kryteria: , ,

    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 robimy dwa zrzuty całego ekranu z Visual Studio i oknem konsoli: 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 zapisujemy w folderze dokumentacja.

    Wynik programu po tym kroku
    > konsola.exe
    Elementy tablicy:
    0: 752
    1: 294
    2: 551
    3: 149
    4: 659
    5: 166
    6: 336
    7: 41
    8: 531
    9: 910
    10: 988
    11: 112
    12: 50
    13: 887
    14: 53
    15: 202
    16: 284
    17: 982
    18: 390
    19: 228
    20: 590
    21: 607
    22: 43
    23: 230
    24: 870
    25: 635
    26: 642
    27: 475
    28: 199
    29: 14
    30: 786
    31: 138
    32: 360
    33: 318
    34: 27
    35: 315
    36: 842
    37: 177
    38: 164
    39: 913
    40: 65
    41: 396
    42: 218
    43: 593
    44: 535
    45: 843
    46: 672
    47: 886
    48: 399
    49: 37
    Podaj liczbę do wyszukania: 41
    Liczba 41 jest pod indeksem 7
    Liczby nieparzyste:
    551
    149
    659
    41
    531
    887
    53
    607
    43
    635
    475
    199
    27
    315
    177
    913
    65
    593
    535
    843
    399
    37
    Razem nieparzystych: 22
    Średnia wszystkich elementów: 431,08
    
    > konsola.exe
    Elementy tablicy:
    0: 182
    1: 601
    2: 73
    3: 484
    4: 165
    5: 621
    6: 101
    7: 1000
    8: 241
    9: 210
    10: 517
    11: 602
    12: 623
    13: 633
    14: 335
    15: 274
    16: 834
    17: 49
    18: 238
    19: 777
    20: 720
    21: 112
    22: 712
    23: 410
    24: 965
    25: 555
    26: 646
    27: 810
    28: 888
    29: 627
    30: 571
    31: 53
    32: 99
    33: 256
    34: 375
    35: 487
    36: 456
    37: 705
    38: 688
    39: 731
    40: 471
    41: 179
    42: 689
    43: 380
    44: 914
    45: 921
    46: 754
    47: 983
    48: 543
    49: 690
    Podaj liczbę do wyszukania: 1001
    Liczby nieparzyste:
    601
    73
    165
    621
    101
    241
    517
    623
    633
    335
    49
    777
    965
    555
    627
    571
    53
    99
    375
    487
    705
    731
    471
    179
    689
    921
    983
    543
    Razem nieparzystych: 28
    Średnia wszystkich elementów: 519,00
  26. Krok 26: Zrzuty web1 - web4

    Kryteria: ,

    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”).

    npm run dev i http://localhost:5173. 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 obok to aplikacja z poprzedniego kroku - 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
  27. 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 piszemy w edytorze tekstu pakietu biurowego (np. Word albo Writer) i zapisujemy jako egzamin w folderze dokumentacja. Trzy informacje: system, środowiska i języki - te, których naprawdę używaliśmy. Obok jest ta sama treść jako plik tekstowy.

    egzamin.txt Tekst nowy plik
    System operacyjny: Windows 11
    Środowiska programistyczne: Visual Studio 2022 (aplikacja konsolowa), Visual Studio Code z Node.js i Vite (aplikacja webowa)
    Języki programowania: C# (aplikacja konsolowa), JavaScript z JSX - biblioteka React (aplikacja webowa)

    Podgląd strony bez zmian - taki jak po kroku 26.

  28. Etap 5: Dobre praktyki (poza kluczem)

    Kroki 28-29 · po tym etapie masz 35 z 35 kryteriów (pkt)

    Krok 28: Konsola: bezpieczne wczytanie liczby

    ok. 3 min
    Polecenie z arkusza

    Komunikacja z użytkownikiem musi być zrozumiała.

    int.Parse przy wpisaniu słowa, pustego wiersza albo liczby z przecinkiem przerywa program wyjątkiem - użytkownik widzi czerwony komunikat po angielsku. int.TryParse nie rzuca wyjątku: zwraca true, gdy udało się odczytać liczbę (i zapisuje ją w searched), a false w przeciwnym razie. Pętla while pyta więc do skutku. Stara linia zostaje w komentarzu.

    Na egzaminie wystarczy wersja wcześniejsza - klucz tego nie sprawdza.

    konsola/Program.cs C# zmienione: 6 linii
    class Table
    {
        private int[] numbers;
        private int count;
    
        public Table(int size)
        {
            count = size;
            numbers = new int[size];
            Random random = new Random();
            for (int i = 0; i < count; i++)
            {
                numbers[i] = random.Next(1, 1001);
            }
        }
    
        public void Display()
        {
            for (int i = 0; i < count; i++)
            {
                Console.WriteLine($"{i}: {numbers[i]}");
            }
        }
    
        /*
         **********************************************
         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
         **********************************************
         */
        public int Find(int value)
        {
            for (int i = 0; i < count; i++)
            {
                if (numbers[i] == value)
                {
                    return i;
                }
            }
            return -1;
        }
    
        public int DisplayOdd()
        {
            int oddCount = 0;
            for (int i = 0; i < count; i++)
            {
                if (numbers[i] % 2 != 0)
                {
                    Console.WriteLine(numbers[i]);
                    oddCount++;
                }
            }
            return oddCount;
        }
    
        public double Average()
        {
            int sum = 0;
            for (int i = 0; i < count; i++)
            {
                sum += numbers[i];
            }
            return (double)sum / count;
        }
    }
    
    class Program
    {
        static void Main()
        {
            Table table = new Table(50);
            Console.WriteLine("Elementy tablicy:");
            table.Display();
    
            Console.Write("Podaj liczbę do wyszukania: ");
            int searched;
            while (!int.TryParse(Console.ReadLine(), out searched))
            {
                Console.Write("To nie jest liczba całkowita. Podaj liczbę do wyszukania: ");
            }
            // int searched = int.Parse(Console.ReadLine());
            int index = table.Find(searched);
            if (index != -1)
            {
                Console.WriteLine($"Liczba {searched} jest pod indeksem {index}");
            }
    
            Console.WriteLine("Liczby nieparzyste:");
            int oddCount = table.DisplayOdd();
            Console.WriteLine($"Razem nieparzystych: {oddCount}");
    
            double average = table.Average();
            Console.WriteLine($"Średnia wszystkich elementów: {average:F2}");
        }
    }
    Wynik programu po tym kroku
    > konsola.exe
    Elementy tablicy:
    0: 33
    1: 955
    2: 249
    3: 927
    4: 157
    5: 851
    6: 988
    7: 21
    8: 126
    9: 585
    10: 18
    11: 941
    12: 497
    13: 296
    14: 214
    15: 729
    16: 716
    17: 774
    18: 339
    19: 58
    20: 829
    21: 427
    22: 666
    23: 253
    24: 280
    25: 837
    26: 829
    27: 89
    28: 132
    29: 860
    30: 78
    31: 68
    32: 484
    33: 424
    34: 111
    35: 195
    36: 404
    37: 962
    38: 357
    39: 671
    40: 575
    41: 472
    42: 496
    43: 993
    44: 593
    45: 947
    46: 106
    47: 663
    48: 675
    49: 199
    Podaj liczbę do wyszukania: 21
    Liczba 21 jest pod indeksem 7
    Liczby nieparzyste:
    33
    955
    249
    927
    157
    851
    21
    585
    941
    497
    729
    339
    829
    427
    253
    837
    829
    89
    111
    195
    357
    671
    575
    993
    593
    947
    663
    675
    199
    Razem nieparzystych: 29
    Średnia wszystkich elementów: 482,98
  29. Krok 29: React: filter zamiast null w funkcji renderującej

    ok. 3 min
    Polecenie z arkusza

    Wyświetlane są tylko zdjęcia z kategorii dla której jest włączone pole switch.

    Wersja z kluczem wybiera zdjęcia w trakcie rysowania: funkcja renderująca zwraca null dla ukrytych. Czytelniej jest oddzielić wybór danych od rysowania: photos.filter(...) tworzy listę samych widocznych zdjęć, a renderPhoto tylko rysuje blok. Taką listę łatwo też policzyć albo posortować (np. „widocznych zdjęć: 5”). Działanie aplikacji jest takie samo.

    Na egzaminie zostań przy wersji wcześniejszej - klucz (R.3.5) opisuje właśnie funkcję renderującą z warunkiem.

    web/src/App.jsx JSX zmienione: 3 linie
    import { useState } from 'react'
    
    function App() {
      const [photos, setPhotos] = useState([
        { id: 0, alt: 'Mak', filename: 'obraz1.jpg', category: 1, downloads: 35 },
        { id: 1, alt: 'Bukiet', filename: 'obraz2.jpg', category: 1, downloads: 43 },
        { id: 2, alt: 'Dalmatyńczyk', filename: 'obraz3.jpg', category: 2, downloads: 2 },
        { id: 3, alt: 'Świnka morska', filename: 'obraz4.jpg', category: 2, downloads: 53 },
        { id: 4, alt: 'Rotwailer', filename: 'obraz5.jpg', category: 2, downloads: 43 },
        { id: 5, alt: 'Audi', filename: 'obraz6.jpg', category: 3, downloads: 11 },
        { id: 6, alt: 'kotki', filename: 'obraz7.jpg', category: 2, downloads: 22 },
        { id: 7, alt: 'Róża', filename: 'obraz8.jpg', category: 1, downloads: 33 },
        { id: 8, alt: 'Świnka morska', filename: 'obraz9.jpg', category: 2, downloads: 123 },
        { id: 9, alt: 'Foksterier', filename: 'obraz10.jpg', category: 2, downloads: 22 },
        { id: 10, alt: 'Szczeniak', filename: 'obraz11.jpg', category: 2, downloads: 12 },
        { id: 11, alt: 'Garbus', filename: 'obraz12.jpg', category: 3, downloads: 321 },
      ])
      const [visible, setVisible] = useState({ 1: true, 2: true, 3: true })
    
      function toggleCategory(category) {
        setVisible({ ...visible, [category]: !visible[category] })
      }
    
      function handleDownload(id) {
        const updated = photos.map((photo) =>
          photo.id === id ? { ...photo, downloads: photo.downloads + 1 } : photo
        )
        setPhotos(updated)
      }
    
      const visiblePhotos = photos.filter((photo) => visible[photo.category])
    
      function renderPhoto(photo) {
        return (
          <div key={photo.id} className="d-inline-block">
            <img src={'assets/' + photo.filename} alt={photo.alt} className="rounded" style={{ margin: '5px' }} />
            <h4>Pobrań: {photo.downloads}</h4>
            <button type="button" className="btn btn-success"
              onClick={() => handleDownload(photo.id)}>Pobierz</button>
          </div>
        )
      }
    
      return (
        <div>
          <h1>Kategorie zdjęć</h1>
          <div className="form-check form-switch form-check-inline">
            <input className="form-check-input" type="checkbox" id="flowers" checked={visible[1]}
              onChange={() => toggleCategory(1)} />
            <label className="form-check-label" htmlFor="flowers">Kwiaty</label>
          </div>
          <div className="form-check form-switch form-check-inline">
            <input className="form-check-input" type="checkbox" id="animals" checked={visible[2]}
              onChange={() => toggleCategory(2)} />
            <label className="form-check-label" htmlFor="animals">Zwierzęta</label>
          </div>
          <div className="form-check form-switch form-check-inline">
            <input className="form-check-input" type="checkbox" id="cars" checked={visible[3]}
              onChange={() => toggleCategory(3)} />
            <label className="form-check-label" htmlFor="cars">Samochody</label>
          </div>
          <div>
            {visiblePhotos.map((photo) => renderPhoto(photo))}
          </div>
        </div>
      )
    }
    
    export default App
    Podgląd strony po tym kroku
  30. Etap 6: Oddanie pracy

    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.

    Sprawdzamy zawartość przed nagraniem:

    • dokumentacja: konsola1.png, konsola2.png, web1.png - web4.png, egzamin,
    • konsolowa: konsola.zip (spakowany po dopisaniu komentarza), Program.cs, konsola.exe,
    • webowa: web.zip (bez node_modules), App.jsx, main.jsx.

    Nagrywamy, sprawdzamy odczyt płyty, opisujemy ją numerem zdającego.

    Podgląd strony bez zmian - taki jak po kroku 29.

Podgląd

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: . 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). Treść arkusza i klucza oceniania pochodzi z CKE.