Przejdź do treści
INF.04Styczeń 2025180 min

INF.04 styczeń 2025 — klasa z tablicą i galeria zdjęć w Bootstrapie

Rozbiór arkusza INF.04 ze stycznia 2025: konsolowa klasa operacji na tablicy, front-end galerii z filtrowaniem po kategoriach w React lub Angularze oraz dokumentacja. Plan na 180 minut i gotowy kod.

Oryginalny arkusz CKE (PDF)

Pobierz plik źródłowy i rozwiąż go samodzielnie przed sprawdzeniem rozwiązań.

Pobierz PDF

Dwie aplikacje, cztery rezultaty, trzy godziny

Styczeń 2025 to klasyczny układ INF.04: prosta klasa operująca na tablicy w języku obiektowym plus front-end galerii zdjęć z filtrowaniem po kategoriach. Trudność nie leży w algorytmach — one są na poziomie pierwszej klasy technikum. Leży w tym, że w 180 minut trzeba postawić dwa niezależne projekty, uruchomić oba, udokumentować oba i jeszcze spakować wszystko według bardzo szczegółowej instrukcji o folderach.

Jak korzystać z tego rozbioru

Każdy oceniany rezultat ma tu własną sekcję. Zaczyna się od karty „Czego wymaga arkusz" — ta jest widoczna od razu. Rozwiązanie z pełnym kodem jest schowane pod przyciskiem. Odsłaniaj je dopiero wtedy, gdy sam spróbujesz napisać kod: na egzaminie nie ma czego kopiować, wszystko musi wyjść z głowy.

1

Metryka arkusza i klucz oceniania

Dane arkusza
  • Kwalifikacja: INF.04 — projektowanie, programowanie i testowanie aplikacji
  • Sesja: styczeń 2025, zadanie 01, wersja arkusza SG
  • Czas: 180 minut, plus 10 minut na zapoznanie się z zadaniem (niewliczane)
  • Próg zdania: 75% punktów
  • Temat: operacje na tablicy w aplikacji konsolowej oraz kategoryzacja zdjęć w galerii front-end
  • Konto na stanowisku: Egzamin, bez hasła
  • Materiały: archiwum pliki3.zip na pulpicie, hasło K@tegorie)
Oceniane rezultaty

Arkusz wymienia cztery rezultaty. To dosłownie lista, po której idzie egzaminator:

  1. implementacja, kompilacja, uruchomienie programu
  2. aplikacja konsolowa
  3. aplikacja web
  4. dokumentacja aplikacji

Zwróć uwagę: pierwszy rezultat jest osobny od dwóch kolejnych. Aplikacja, która się nie kompiluje, przegrywa dwa razy — raz w rezultacie 1, drugi raz w rezultacie za samą aplikację.

Co musi znaleźć się w folderze

Folder nazwany numerem zdającego, a w nim trzy podfoldery o narzuconych nazwach:

PESEL/
├── konsolowa/
│   ├── konsola.zip        archiwum CALEGO folderu projektu
│   ├── Program.cs         (albo main.py / Main.java / main.cpp)
│   └── konsola.exe        plik wykonywalny, jesli istnieje
├── webowa/
│   ├── web.zip            archiwum CALEGO folderu projektu
│   ├── App.jsx            komponent (albo app.component.ts + .html)
│   └── App.css            i kazdy inny modyfikowany plik zrodlowy
└── dokumentacja/
  ├── konsola1.png       zrzuty aplikacji konsolowej
  ├── konsola2.png
  ├── web1.png           zrzuty aplikacji webowej
  ├── web2.png
  ├── web3.png
  └── egzamin.odt        (albo .docx) opis narzedzi

Arkusz mówi wprost: po spakowaniu w podfolderze zostawiasz tylko archiwum, modyfikowane pliki źródłowe i plik wykonywalny. Nie zostawiaj tam folderu node_modules ani bin/obj — zajmą całą płytę i nagranie się nie skończy przed dzwonkiem.

Czego tu nie ma, a bywa w INF.04: w tej sesji nie ma bazy danych, nie ma back-endu, nie ma testów jednostkowych. Aplikacja webowa jest jawnie opisana jako typu front-end, cały stan siedzi w tablicy w pamięci komponentu. Jeśli zaczniesz kombinować z API albo z zapisem do pliku, tracisz czas na coś, czego nikt nie sprawdza.

2

Plan czasowy na 180 minut

Cztery rezultaty ważą podobnie, więc porzucona dokumentacja kosztuje mniej więcej tyle, co porzucona aplikacja webowa — a robi się ją dwadzieścia razy szybciej. To jest główny wniosek z tego arkusza: najtańsze punkty leżą w dokumentacji i w porządku w folderach, a większość zdających zostawia je na ostatnie pięć minut i nie zdąża.

EtapCzasRezultatUwagi
Foldery, rozpakowanie pliki3.zip, oba projekty założone10 min1projekt webowy zakładaj od razu — instalacja pakietów mieli w tle
Klasa tablicy: pola, konstruktor, cztery metody45 min2najprostsza część arkusza, nie przeciągaj jej
Uruchomienie konsoli i zrzuty konsola1, konsola210 min1 + 4zrzut rób od razu, nie na końcu
Web: Bootstrap, assets, tablica z dane.txt20 min3import CSS Bootstrapa sprawdź od razu w przeglądarce
Komponent: switche, pętla po zdjęciach, przycisk Pobierz45 min3najpierw wyświetl wszystko, dopiero potem filtruj
Uruchomienie web i zrzuty web1, web2, web315 min1 + 4stan początkowy, odznaczony switch, wzrost pobrań
Nagłówek metody w komentarzu + plik egzamin15 min4najtańsze punkty w całym arkuszu
Archiwa zip, czyszczenie folderów, nagranie płyty20 min1rezerwa, nigdy jej nie tnij

Kolejność, która się broni: konsola najpierw. Jest krótsza, nie zależy od instalacji pakietów i daje ci materiał do nagłówka dokumentacji, który i tak musisz napisać. Gdyby zabrakło czasu, lepiej mieć skończoną konsolę i połowę weba niż odwrotnie — połowa weba, która się nie uruchamia, przegrywa również rezultat 1.

Zasada dwudziestej minuty: jeśli po dwudziestu minutach walki projekt webowy dalej nie startuje w przeglądarce, przestań go naprawiać i zrób zrzut ekranu ze spisem błędów i otwartym środowiskiem. Arkusz wprost przewiduje ten scenariusz w części III — taki zrzut jest punktowany, a puste miejsce nie.

3

Rezultat 1: implementacja, kompilacja, uruchomienie

Czego wymaga arkusz
  • Folder nazwany numerem zdającego z podfolderami konsolowa, webowa, dokumentacja.
  • Oba projekty mają się kompilować i uruchamiać. To osobny rezultat, niezależny od tego, czy aplikacja robi wszystko, czego wymaga jej opis.
  • Pełny kod każdej aplikacji spakowany do archiwum: konsola.zip i web.zip.
  • Po spakowaniu w podfolderze zostają wyłącznie: archiwum, modyfikowane pliki źródłowe i — jeśli istnieje — plik wykonywalny.
  • Archiwum pliki3.zip z pulpitu rozpakowane hasłem K@tegorie), zdjęcia przeniesione do folderu assets.
  • Na koniec: płyta nagrana i opisana numerem zdającego.

Zrób to jako pierwszą rzecz po przeczytaniu arkusza — zanim napiszesz choć jedną linijkę kodu. Potem porównaj kolejność z procedurą poniżej.

Pokaż rozwiązaniePierwsze 10 minut i ostatnie 20 minut egzaminu
Start: struktura i oba projekty
# 1. Struktura folderow (PESEL = Twoj numer zdajacego)
mkdir PESEL
mkdir PESEL/konsolowa PESEL/webowa PESEL/dokumentacja

# 2. Rozpakuj materialy z pulpitu, haslo: K@tegorie)
#    W srodku sa zdjecia oraz plik dane.txt

# 3. Projekt konsolowy - wybierz JEDEN wariant
dotnet new console -o PESEL/konsolowa/konsola     # C#
# albo po prostu plik main.py                      # Python
# albo projekt w NetBeans / IntelliJ               # Java
# albo projekt w Code::Blocks / Visual Studio      # C++

# 4. Projekt webowy - wybierz JEDEN wariant
npx create-react-app egzamin                       # React.js
# albo
ng new egzamin --standalone --style=css            # Angular

# 5. Bootstrap (jest juz na stanowisku egzaminacyjnym)
npm install bootstrap

# 6. Zdjecia z pliki3.zip do folderu assets
#    React:  egzamin/public/assets/  albo  egzamin/src/assets/
#    Angular: egzamin/src/assets/  albo  egzamin/public/assets/
Finisz: archiwizacja i czyszczenie
# Spakuj CALY folder projektu, nie same pliki zrodlowe
#   konsolowa -> konsola.zip
#   webowa    -> web.zip
#
# Przed spakowaniem weba usun node_modules:
#   to kilkaset megabajtow i kilkadziesiat tysiecy plikow,
#   pakowanie tego trwa dluzej niz zostalo Ci czasu.
#
# Po spakowaniu w podfolderze zostawiasz:
#   konsolowa/  konsola.zip + Program.cs + konsola.exe
#   webowa/     web.zip + App.jsx + App.css
#
# Ostatni krok: nagraj plyte, sprawdz nagranie,
# opisz plyte numerem zdajacego, zostaw w pudelku z arkuszem.
Dlaczego akurat tak
  • Projekt webowy zakładasz na początku, bo instalacja pakietów potrafi zająć kilka minut. W tym czasie piszesz klasę konsolową — procesor i tak stoi bezczynnie.
  • Nazwy podfolderów są narzucone przez arkusz: konsolowa, webowa, dokumentacja. Nie „konsola", nie „web", nie „docs".
  • Nazwy archiwów też: konsola.zip i web.zip. Zauważ, że są niesymetryczne względem nazw folderów — to celowa pułapka na automatyczne uzupełnianie.
  • Plik wykonywalny dołączasz „jeżeli istnieje". W Pythonie nie istnieje i nikt go nie oczekuje. W C# to plik z folderu bin/Debug.

Gdzie się traci punkty: spakowanie samego pliku źródłowego zamiast całego folderu projektu. Arkusz pisze „pełny kod (cały folder projektu)" — plik konsola.zip zawierający jeden plik .cs nie spełnia tego warunku. Drugi klasyk to nagranie płyty bez sprawdzenia: napęd raportuje sukces, a katalog jest pusty. Otwórz płytę po nagraniu i przejrzyj wszystkie trzy podfoldery.

4

Rezultat 2: aplikacja konsolowa

Czego wymaga arkusz
  • Język: obiektowy, zgodny z zainstalowanym na stanowisku — C++, C#, Java albo Python. Tablica i operacje na niej mają być zamknięte w klasie.
  • Pola klasy: tablica liczb całkowitych (tradycyjna tablica, w Pythonie lista) oraz liczba elementów jako liczba całkowita. Oba pola dostępne tylko w tej klasie i niedostępne dla klas potomnych.
  • Konstruktor: przyjmuje rozmiar tablicy, ustawia pole liczby elementów na wartość argumentu i wypełnia tablicę pseudolosowymi liczbami całkowitymi z zakresu od 1 do 1000.
  • Metoda 1: wyświetla wszystkie elementy w postaci „<index>: <wartość>", nic nie zwraca.
  • Metoda 2: wyszukuje pierwsze wystąpienie wartości podanej jako argument, zwraca indeks lub -1, gdy nie znaleziono.
  • Metoda 3: wyświetla wszystkie wartości nieparzyste i zwraca ich liczbę.
  • Metoda 4: liczy średnią arytmetyczną wartości i ją zwraca.
  • Wszystkie metody dostępne poza klasą.
  • Program główny: tworzy obiekt z rozmiarem tablicy większym od 20 i sprawdza działanie wszystkich metod. Przy wyszukiwaniu: gdy wartość znaleziono, wyświetla komunikat z indeksem; gdy nie — nie wyświetla nic.
  • Znaczące nazewnictwo (angielskie lub polskie), czytelne formatowanie, zrozumiała komunikacja z użytkownikiem.

To zadanie na 45 minut i na papierze wygląda banalnie — napisz je sam, zanim odsłonisz kod. Trzy rzeczy są tu punktowane osobno i łatwo je przeoczyć: modyfikator dostępu pól, zakres losowania i milczenie przy nieznalezionej wartości.

Pokaż rozwiązanieKompletny program w C# — klasa Tablica i Main

Cały plik Program.cs. Nagłówek dokumentacyjny nad metodą to jednocześnie realizacja części III arkusza.

using System;

namespace AplikacjaKonsolowa
{
  class Tablica
  {
      // Oba pola prywatne: dostepne tylko w tej klasie
      // i NIEdostepne dla klas potomnych
      private int[] tablica;
      private int liczbaElementow;

      public Tablica(int rozmiar)
      {
          liczbaElementow = rozmiar;
          tablica = new int[rozmiar];
          Random generator = new Random();
          for (int i = 0; i < liczbaElementow; i++)
          {
              // Next(1, 1001) losuje od 1 do 1000 wlacznie
              tablica[i] = generator.Next(1, 1001);
          }
      }

      public void Wyswietl()
      {
          for (int i = 0; i < liczbaElementow; i++)
          {
              Console.WriteLine(i + ": " + tablica[i]);
          }
      }

      /************************************************
      nazwa metody:         Szukaj
      opis metody:          wyszukuje pierwsze wystapienie
                            podanej wartosci w tablicy
      parametry:            szukanaWartosc - liczba calkowita,
                            ktorej szukamy w tablicy
      zwracany typ i opis:  int - indeks pierwszego wystapienia
                            szukanej wartosci lub -1, gdy
                            wartosci nie ma w tablicy
      autor:                PESEL
      ************************************************/
      public int Szukaj(int szukanaWartosc)
      {
          for (int i = 0; i < liczbaElementow; i++)
          {
              if (tablica[i] == szukanaWartosc)
              {
                  return i;
              }
          }
          return -1;
      }

      public int WyswietlNieparzyste()
      {
          int licznik = 0;
          for (int i = 0; i < liczbaElementow; i++)
          {
              if (tablica[i] % 2 != 0)
              {
                  Console.WriteLine(tablica[i]);
                  licznik++;
              }
          }
          return licznik;
      }

      public double Srednia()
      {
          double suma = 0;
          for (int i = 0; i < liczbaElementow; i++)
          {
              suma += tablica[i];
          }
          if (liczbaElementow == 0)
          {
              return 0;
          }
          return suma / liczbaElementow;
      }
  }

  class Program
  {
      static void Main(string[] args)
      {
          Tablica tab = new Tablica(25);

          Console.WriteLine("Elementy tablicy:");
          tab.Wyswietl();

          Console.WriteLine();
          Console.Write("Podaj szukana wartosc: ");
          int szukana = int.Parse(Console.ReadLine());
          int indeks = tab.Szukaj(szukana);
          if (indeks != -1)
          {
              Console.WriteLine("Znaleziono pod indeksem: " + indeks);
          }
          // gdy indeks == -1 program celowo NIC nie wyswietla

          Console.WriteLine();
          Console.WriteLine("Wartosci nieparzyste:");
          int ileNieparzystych = tab.WyswietlNieparzyste();
          Console.WriteLine("Liczba wartosci nieparzystych: " + ileNieparzystych);

          Console.WriteLine();
          Console.WriteLine("Srednia arytmetyczna: " + tab.Srednia());

          Console.ReadKey();
      }
  }
}
Rozbiór wymagań na linijki kodu
Zapis w arkuszuCo to znaczy w kodzie
„dostępne tylko w tej klasie oraz niedostępne dla klas potomnych"privateprotected jest widoczne w klasach potomnych, więc jest błędem
„pseudolosowymi z zakresu od 1 do 1000"Next(1, 1001) — górna granica w C# jest wyłączona
„w postaci <index>: <wartość>"dwukropek i spacja między indeksem a wartością, dokładnie jak na obrazie 1
„zwraca indeks lub liczbę -1"metoda zwraca int, nie bool i nie wypisuje niczego sama
„w przeciwnym wypadku nic nie jest wyświetlane"if bez gałęzi else w programie głównym
„wyświetlająca nieparzyste i zwracająca ich liczbę"jedna metoda robi obie rzeczy: WriteLine w pętli plus return licznik
„rozmiar tablicy większy od 20"dowolna liczba od 21 w górę — w przykładzie 25

Gdzie się traci punkty: wszystkie operacje muszą być ograniczone polem liczby elementów, a nie długością tablicy. W tym zadaniu obie wartości są równe, ale arkusz pisze wprost: „wszystkie operacje są ograniczone wartością tego pola". Pętla po tablica.Length jest formalnie niezgodna z opisem. Drugi klasyk: metoda średniej zwracająca int — dzielenie całkowite ucina część ułamkową i średnia z 25 losowych liczb wychodzi „równa".

Pokaż rozwiązanieTen sam program w Pythonie — z prawdziwą prywatnością pól

Plik main.py. Arkusz dopuszcza w Pythonie listę zamiast tablicy — to jedyne odstępstwo, jakie daje.

import random


class Tablica:
  def __init__(self, rozmiar):
      # Podwojny podkreslnik = name mangling.
      # Pole jest niedostepne poza klasa i w klasach potomnych.
      self.__liczba_elementow = rozmiar
      self.__tablica = []
      for i in range(rozmiar):
          self.__tablica.append(random.randint(1, 1000))

  def wyswietl(self):
      for i in range(self.__liczba_elementow):
          print(str(i) + ": " + str(self.__tablica[i]))

  # ************************************************
  # nazwa metody:         szukaj
  # opis metody:          wyszukuje pierwsze wystapienie
  #                       podanej wartosci w tablicy
  # parametry:            szukana_wartosc - liczba calkowita,
  #                       ktorej szukamy w tablicy
  # zwracany typ i opis:  int - indeks pierwszego wystapienia
  #                       lub -1, gdy wartosci nie ma
  # autor:                PESEL
  # ************************************************
  def szukaj(self, szukana_wartosc):
      for i in range(self.__liczba_elementow):
          if self.__tablica[i] == szukana_wartosc:
              return i
      return -1

  def wyswietl_nieparzyste(self):
      licznik = 0
      for i in range(self.__liczba_elementow):
          if self.__tablica[i] % 2 != 0:
              print(self.__tablica[i])
              licznik += 1
      return licznik

  def srednia(self):
      if self.__liczba_elementow == 0:
          return 0
      suma = 0
      for i in range(self.__liczba_elementow):
          suma += self.__tablica[i]
      return suma / self.__liczba_elementow


def main():
  tab = Tablica(25)

  print("Elementy tablicy:")
  tab.wyswietl()

  print()
  szukana = int(input("Podaj szukana wartosc: "))
  indeks = tab.szukaj(szukana)
  if indeks != -1:
      print("Znaleziono pod indeksem: " + str(indeks))
  # gdy indeks == -1 program celowo nic nie wyswietla

  print()
  print("Wartosci nieparzyste:")
  ile = tab.wyswietl_nieparzyste()
  print("Liczba wartosci nieparzystych: " + str(ile))

  print()
  print("Srednia arytmetyczna: " + str(tab.srednia()))


main()
Różnice, o które pytają egzaminatorzy
  • W Pythonie random.randint(1, 1000) ma obie granice włączone — inaczej niż Next w C#. To najczęstszy błąd przy przepisywaniu rozwiązania między językami.
  • Jeden podkreślnik (_pole) to tylko konwencja, dziedziczy się normalnie. Arkusz żąda niedostępności w klasach potomnych, więc idziesz w dwa podkreślniki.
  • W Javie i C++ odpowiednikiem jest private. W C++ pamiętaj o zwolnieniu pamięci, jeśli alokujesz tablicę przez new.
  • Średnia w Pythonie 3 wychodzi zmiennoprzecinkowa automatycznie — operator / nigdy nie ucina.

Uwaga o czytelności: arkusz punktuje osobno „znaczące nazewnictwo" i „czyste formatowanie kodu". Nazwy typu a, x1, fun2 kosztują punkt, mimo że program działa idealnie. Wyjątek dopuszczony wprost przez arkusz: klasa i pole tablicowe mogą się nazywać ogólnie — tab, tablica, table.

5

Rezultat 3: aplikacja WEB z Bootstrapem

Czego wymaga arkusz
  • Aplikacja front-end w Angularze albo w React.js, złożona z jednego komponentu.
  • Dane: tablica obiektów przepisana z pliku dane.txt. Każdy obiekt ma pola id, alt, filename, category (1 kwiaty, 2 zwierzęta, 3 samochody), downloads.
  • Zdjęcia z archiwum trafiają do folderu assets.
  • Nagłówek pierwszego stopnia: „Kategorie zdjęć".
  • Trzy pola switch (checkbox), domyślnie włączone, etykiety: Kwiaty, Zwierzęta, Samochody.
  • Bloki zdjęć wyświetlane warunkowo, jeden obok drugiego. Każdy zawiera zdjęcie, nagłówek czwartego stopnia z liczbą pobrań i przycisk „Pobierz".
  • Styl zdjęć: marginesy zewnętrzne 5 px, zaokrąglone rogi.
  • Kliknięcie „Pobierz" zwiększa liczbę pobrań o jeden — wartość zapisuje się w tablicy obiektów i natychmiast widać zmianę na ekranie.
  • Do wyświetlenia bloków użyte pętle i warunki; aplikacja ma działać poprawnie także dla innej liczby zdjęć.
  • Przyciski i switche stylowane zgodnie z tabelą 1 arkusza (elementy Bootstrapa).

Zbuduj to sam — najpierw wyświetl wszystkie zdjęcia bez filtrowania, potem dołóż switche. Rozwiązanie poniżej jest w dwóch wariantach.

Tabela 1 z arkusza: elementy Bootstrapa, które dostajesz za darmo

Ostatnia strona arkusza to ściąga — CKE podaje w niej gotowe fragmenty Bootstrapa. Nie musisz ich pamiętać, musisz umieć je znaleźć i wkleić. To jest część zadania, nie dodatek:

ElementCo podaje arkuszGdzie tego użyjesz
Podłączenie w Angularze@import w styles.cssplik src/styles.css, raz na projekt
Podłączenie w React.jsimport bootstrap.cssna górze pliku komponentu albo w index.js
Switchform-check form-switch, form-check-input, form-check-labeltrzy pola przełączające kategorie
Switche w jednym wierszuform-check-inlineukład poziomy jak na obrazie 2
Przyciskibtn + btn-primary, btn-success, btn-danger, ...przycisk „Pobierz" w każdym bloku zdjęcia

Uwaga na klasę etykiety: arkusz pokazuje switch, w którym id inputu i for etykiety są tą samą wartością. Jeśli je rozjedziesz, klikanie w napis „Kwiaty" przestaje działać, a egzaminator sprawdza działanie kliknięciem w etykietę, nie w kwadracik.

Pokaż rozwiązanieWariant React.js — cały komponent App.jsx

Plik src/App.jsx. Tablicę zdjecia przepisujesz jeden do jednego z dane.txt — poniżej przykładowe wartości.

import React, { useState } from 'react';
import 'bootstrap/dist/css/bootstrap.css';

// Trzy kategorie zgodnie z opisem pola category w dane.txt
const kategorie = [
{ numer: 1, etykieta: 'Kwiaty' },
{ numer: 2, etykieta: 'Zwierzeta' },
{ numer: 3, etykieta: 'Samochody' }
];

function App() {
// Tablica obiektow przepisana z pliku dane.txt
const [zdjecia, setZdjecia] = useState([
  { id: 1, alt: 'roza', filename: 'kwiat1.jpg', category: 1, downloads: 12 },
  { id: 2, alt: 'tulipan', filename: 'kwiat2.jpg', category: 1, downloads: 5 },
  { id: 3, alt: 'kot', filename: 'zwierze1.jpg', category: 2, downloads: 8 },
  { id: 4, alt: 'pies', filename: 'zwierze2.jpg', category: 2, downloads: 21 },
  { id: 5, alt: 'sedan', filename: 'auto1.jpg', category: 3, downloads: 3 },
  { id: 6, alt: 'kombi', filename: 'auto2.jpg', category: 3, downloads: 17 }
]);

// W stanie poczatkowym wszystkie switche sa wlaczone
const [wlaczoneKategorie, setWlaczoneKategorie] = useState([1, 2, 3]);

function przelaczKategorie(numer) {
  setWlaczoneKategorie(function (poprzednie) {
    if (poprzednie.includes(numer)) {
      return poprzednie.filter(function (k) { return k !== numer; });
    }
    return poprzednie.concat([numer]);
  });
}

function pobierz(idZdjecia) {
  setZdjecia(function (poprzednie) {
    return poprzednie.map(function (zdjecie) {
      if (zdjecie.id === idZdjecia) {
        return Object.assign({}, zdjecie, { downloads: zdjecie.downloads + 1 });
      }
      return zdjecie;
    });
  });
}

const styleZdjecia = { margin: '5px', borderRadius: '10px' };

return (
  <div className="container">
    <h1>Kategorie zdjec</h1>

    <div className="mb-3">
      {kategorie.map(function (kategoria) {
        return (
          <div className="form-check form-switch form-check-inline"
               key={kategoria.numer}>
            <input className="form-check-input"
                   type="checkbox"
                   id={'switch' + kategoria.numer}
                   checked={wlaczoneKategorie.includes(kategoria.numer)}
                   onChange={function () { przelaczKategorie(kategoria.numer); }} />
            <label className="form-check-label"
                   htmlFor={'switch' + kategoria.numer}>
              {kategoria.etykieta}
            </label>
          </div>
        );
      })}
    </div>

    <div className="d-flex flex-wrap">
      {zdjecia
        .filter(function (zdjecie) {
          return wlaczoneKategorie.includes(zdjecie.category);
        })
        .map(function (zdjecie) {
          return (
            <div className="text-center" key={zdjecie.id}>
              <img src={'assets/' + zdjecie.filename}
                   alt={zdjecie.alt}
                   width="220"
                   style={styleZdjecia} />
              <h4>Pobrania: {zdjecie.downloads}</h4>
              <button type="button"
                      className="btn btn-success"
                      onClick={function () { pobierz(zdjecie.id); }}>
                Pobierz
              </button>
            </div>
          );
        })}
    </div>
  </div>
);
}

export default App;
Dlaczego akurat tak
  • Stan w tablicy, nie w licznikach obok. Arkusz pisze: „liczba pobrań jest zapisywana w tablicy z obiektami zdjęć". Trzymanie osobnego licznika w drugim useState formalnie nie spełnia wymagania, choć wygląda tak samo.
  • Filtrowanie przez tablicę włączonych kategorii, a nie przez trzy osobne zmienne logiczne. Dzięki temu dodanie czwartej kategorii nie wymaga zmiany kodu — arkusz żąda uniwersalności.
  • Pętla i warunek to tutaj map plus filter. Nie wolno wypisać sześciu bloków ręcznie — to natychmiastowa strata punktu za „zastosowano pętle oraz warunki".
  • d-flex flex-wrap realizuje „bloki wyświetlone jeden obok drugiego" i zawija je, gdy zabraknie miejsca w wierszu.
  • Marginesy 5 px i zaokrąglone rogi są narzucone przez arkusz — możesz je dać stylem wbudowanym albo klasą w App.css, byle były na zdjęciu, a nie na całym bloku.

Gdzie się traci punkty: mutacja obiektu w miejscu (zdjecie.downloads++ bez setZdjecia) zwiększa wartość w pamięci, ale React nie przerysuje widoku — na ekranie nic się nie dzieje i przepada wymóg „na bieżąco powoduje wyświetlenie tej modyfikacji". Drugi klasyk: checked bez onChange — React zablokuje wtedy przełączanie switcha i wypisze ostrzeżenie w konsoli. Trzeci: ścieżka do zdjęć. Przy folderze public/assets działa assets/plik.jpg, przy src/assets trzeba plik zaimportować. Sprawdź w przeglądarce, że obrazy naprawdę się ładują — puste ramki to strata punktu za wygląd.

Pokaż rozwiązanieWariant Angular — komponent i szablon HTML

Krok zero: dopisz import Bootstrapa do src/styles.css dokładnie tak, jak podaje tabela 1 arkusza.

/* src/styles.css */
@import "~bootstrap/dist/css/bootstrap.css";

/* jesli tylda nie zadziala w nowszej wersji Angulara: */
/* @import "bootstrap/dist/css/bootstrap.css"; */
app.component.ts
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';

@Component({
selector: 'app-root',
standalone: true,
imports: [CommonModule, FormsModule],
templateUrl: './app.component.html'
})
export class AppComponent {
kategorie = [
  { numer: 1, etykieta: 'Kwiaty', wlaczona: true },
  { numer: 2, etykieta: 'Zwierzeta', wlaczona: true },
  { numer: 3, etykieta: 'Samochody', wlaczona: true }
];

// Tablica obiektow przepisana z pliku dane.txt
zdjecia = [
  { id: 1, alt: 'roza', filename: 'kwiat1.jpg', category: 1, downloads: 12 },
  { id: 2, alt: 'tulipan', filename: 'kwiat2.jpg', category: 1, downloads: 5 },
  { id: 3, alt: 'kot', filename: 'zwierze1.jpg', category: 2, downloads: 8 },
  { id: 4, alt: 'pies', filename: 'zwierze2.jpg', category: 2, downloads: 21 },
  { id: 5, alt: 'sedan', filename: 'auto1.jpg', category: 3, downloads: 3 },
  { id: 6, alt: 'kombi', filename: 'auto2.jpg', category: 3, downloads: 17 }
];

czyWidoczne(numerKategorii: number): boolean {
  const kategoria = this.kategorie.find(function (k) {
    return k.numer === numerKategorii;
  });
  return kategoria ? kategoria.wlaczona : false;
}

pobierz(zdjecie: any): void {
  zdjecie.downloads = zdjecie.downloads + 1;
}
}
app.component.html
<div class="container">
<h1>Kategorie zdjec</h1>

<div class="mb-3">
  <div class="form-check form-switch form-check-inline"
       *ngFor="let kategoria of kategorie">
    <input class="form-check-input"
           type="checkbox"
           id="switch{{kategoria.numer}}"
           [(ngModel)]="kategoria.wlaczona">
    <label class="form-check-label"
           for="switch{{kategoria.numer}}">
      {{kategoria.etykieta}}
    </label>
  </div>
</div>

<div class="d-flex flex-wrap">
  <div class="text-center"
       *ngFor="let zdjecie of zdjecia">
    <div *ngIf="czyWidoczne(zdjecie.category)">
      <img [src]="'assets/' + zdjecie.filename"
           [alt]="zdjecie.alt"
           width="220"
           style="margin: 5px; border-radius: 10px;">
      <h4>Pobrania: {{zdjecie.downloads}}</h4>
      <button type="button"
              class="btn btn-success"
              (click)="pobierz(zdjecie)">
        Pobierz
      </button>
    </div>
  </div>
</div>
</div>
Trzy pułapki wariantu angularowego
  • ngModel nie zadziała bez zaimportowanego FormsModule. Błąd „Can't bind to ngModel" to najczęstsze zatrzymanie na tym arkuszu — w projekcie modułowym import idzie do app.module.ts, w standalone do tablicy imports komponentu.
  • *ngFor i *ngIf nie mogą stać na tym samym elemencie. Dlatego warunek siedzi w zagnieżdżonym div. W Angularze 17+ możesz zamiast tego użyć składni @for i @if.
  • W Angularze mutacja zdjecie.downloads = zdjecie.downloads + 1 jest w porządku — wykrywanie zmian złapie ją samo. To odwrotność zasady z Reacta i dlatego przepisywanie rozwiązania między frameworkami bez zrozumienia kończy się źle.

Wspólne dla obu wariantów: nagłówek liczby pobrań ma być czwartego stopnia, a tytuł strony pierwszego. Arkusz podaje to wprost i egzaminator sprawdza to w kodzie, nie na oko — <h3> wygląda podobnie i nie daje punktu. Tekst alternatywny każdego zdjęcia bierzesz z pola alt w danych, nie wpisujesz własnego.

6

Rezultat 4: dokumentacja aplikacji

Czego wymaga arkusz
  • Nagłówek dowolnej metody aplikacji konsolowej opisany komentarzem według wzoru z listingu 1 — wieloliniowym albo kilkoma jednoliniowymi, nad lub pod nazwą metody.
  • We wzorze wypełniasz: nazwę metody, opis, parametry (albo słowo brak dla metody bezparametrowej), zwracany typ z opisem oraz autora, czyli numer zdającego.
  • Zrzuty ekranu dokumentujące uruchomienie obu aplikacji. Cały obszar ekranu, z widocznym paskiem zadań. Na zrzucie ma być okno z wynikiem działania programu oraz otwarte środowisko z projektem albo terminal z kompilacją.
  • Jeśli aplikacja się nie uruchamia — zrzut ze spisem błędów i otwartym środowiskiem programistycznym.
  • Nazwy zrzutów: konsola1, konsola2, ... oraz web1, web2, ... Dla weba: stan początkowy, różne kombinacje switchy, działanie przycisku „Pobierz".
  • Plik egzamin w edytorze tekstu pakietu biurowego z nazwą systemu operacyjnego, nazwami środowisk programistycznych i nazwami języków programowania.
  • Wszystko ląduje w podfolderze dokumentacja.

To jest ćwierć oceny za piętnaście minut pracy. Napisz swój nagłówek według wzoru, potem sprawdź, czy nie zgubiłeś żadnej z pięciu pozycji.

Pokaż rozwiązanieSzablon nagłówka metody i gotowa treść pliku egzamin
Wzór z listingu 1 — pusty szablon
**********************************************
nazwa metody:         <nazwa>
opis metody:          <krotki opis, co robi metoda>
parametry:            <nazwa i opis parametru1, lub "brak">
                    <nazwa i opis parametru2>
                    ...
zwracany typ i opis:  <nazwa typu i opis co jest zwracane lub "brak">
autor:                <numer zdajacego>
**********************************************
Wypełniony, w komentarzu wieloliniowym (C#, Java, C++)
/***********************************************
nazwa metody:         WyswietlNieparzyste
opis metody:          przeglada tablice i wypisuje na konsoli
                    wszystkie wartosci nieparzyste, kazda
                    w osobnym wierszu
parametry:            brak
zwracany typ i opis:  int - liczba znalezionych i wyswietlonych
                    wartosci nieparzystych
autor:                PESEL
***********************************************/
public int WyswietlNieparzyste()
{
  // ...
}
Ten sam nagłówek w komentarzach jednoliniowych (Python)
# **********************************************
# nazwa metody:         wyswietl_nieparzyste
# opis metody:          przeglada tablice i wypisuje na konsoli
#                       wszystkie wartosci nieparzyste
# parametry:            brak
# zwracany typ i opis:  int - liczba znalezionych i wyswietlonych
#                       wartosci nieparzystych
# autor:                PESEL
# **********************************************
def wyswietl_nieparzyste(self):
  pass
Plik egzamin — gotowa treść do przepisania

Zapisujesz go w edytorze tekstu pakietu biurowego pod nazwą egzamin (rozszerzenie zostawiasz domyślne, .odt albo .docx). Wpisujesz to, czego naprawdę użyłeś na stanowisku:

DOKUMENTACJA APLIKACJI
Numer zdajacego: PESEL

1. System operacyjny
 Windows 10 Education 64-bit
 (albo: Windows 11 Pro / Ubuntu 22.04 LTS)

2. Srodowiska programistyczne
 - aplikacja konsolowa: Microsoft Visual Studio 2022 Community
 - aplikacja webowa:     Visual Studio Code 1.85
 - przegladarka testowa: Mozilla Firefox 121

3. Jezyki programowania i technologie
 - aplikacja konsolowa: C# (.NET 8)
 - aplikacja webowa:     JavaScript, HTML, CSS
 - biblioteka/framework: React.js 18, Bootstrap 5

4. Wykaz zrzutow ekranu
 konsola1 - kompilacja i wyswietlenie elementow tablicy
 konsola2 - wyszukiwanie, liczby nieparzyste i srednia
 web1     - stan poczatkowy, wszystkie switche wlaczone
 web2     - odznaczona kategoria Zwierzeta
 web3     - wzrost liczby pobran po kliknieciu Pobierz
Jak robić zrzuty, żeby zostały zaliczone
  1. Ustaw okna tak, żeby na jednym ekranie było widać i wynik działania, i środowisko z projektem. Arkusz żąda obu rzeczy na jednym zrzucie.
  2. Naciśnij klawisz zrzutu całego ekranu — nie narzędzie wycinania. Kadrowany zrzut nie spełnia warunku „cały obszar ekranu monitora z widocznym paskiem zadań".
  3. Zapisz od razu do folderu dokumentacja pod właściwą nazwą. Przenoszenie i przemianowywanie na końcu to strata czasu i okazja do pomyłki.
  4. Web wymaga co najmniej trzech zrzutów, bo arkusz wymienia trzy sytuacje: stan początkowy, różne kombinacje switchy, działanie przycisku „Pobierz". Zasada ogólna z arkusza: tyle zrzutów, ile interakcji podejmuje aplikacja.

Gdzie się traci punkty: pominięcie linii autor z numerem zdającego — to jedna z pięciu pozycji wzoru i liczy się osobno. Drugi klasyk: wpisanie w parametrach myślnika albo zostawienie pustego miejsca zamiast słowa brak, którego arkusz żąda wprost. Trzeci: zrzut zrobiony na wąskim oknie terminala, na którym nie widać środowiska programistycznego. Czwarty, najboleśniejszy: aplikacja webowa nie ruszyła, więc zdający nie zrobił żadnego zrzutu — a zrzut z listą błędów kompilacji jest w arkuszu przewidziany i punktowany.

7

Checklista przed oddaniem

Aplikacja konsolowa
  • Oba pola klasy są prywatne, nie chronione i nie publiczne.
  • Konstruktor przyjmuje rozmiar i losuje wartości od 1 do 1000 — sprawdź granice w swoim języku.
  • Obiekt w programie głównym utworzony z rozmiarem większym niż 20.
  • Wyświetlanie w formacie indeks: wartość.
  • Wyszukiwanie zwraca -1, a program główny milczy, gdy nic nie znaleziono.
  • Metoda nieparzystych wyświetla wartości i zwraca ich liczbę; program główny wypisuje tę liczbę z komentarzem.
  • Średnia jest liczbą zmiennoprzecinkową.
  • Nazwy zmiennych i metod są znaczące, wcięcia równe.
Aplikacja webowa
  • Bootstrap podłączony i widoczny — switche wyglądają jak przełączniki, a nie jak zwykłe kwadraciki.
  • Nagłówek h1 o treści „Kategorie zdjęć", liczby pobrań w h4.
  • Trzy switche z etykietami Kwiaty, Zwierzęta, Samochody, wszystkie włączone po odświeżeniu strony.
  • Odznaczenie switcha ukrywa dokładnie jedną kategorię, ponowne zaznaczenie ją przywraca.
  • Zdjęcia mają margines 5 px i zaokrąglone rogi, stoją jeden obok drugiego.
  • Kliknięcie „Pobierz" podnosi licznik tego jednego zdjęcia natychmiast, wielokrotne kliknięcia sumują się.
  • Wszystkie obrazy ładują się z folderu assets — żadnej pustej ramki.
  • Bloki generowane pętlą; dopisanie siódmego zdjęcia do tablicy nie wymaga zmiany szablonu.
Dokumentacja i oddanie
  • Nagłówek metody w komentarzu ma wszystkie pięć pozycji wzoru, w tym autor: numer zdającego.
  • Zrzuty obejmują cały ekran z paskiem zadań, mają nazwy konsola1, web1 i dalej.
  • Plik egzamin wymienia system, środowiska i języki.
  • Folder główny nazwany numerem zdającego, w środku dokładnie trzy podfoldery o narzuconych nazwach.
  • W podfolderach są archiwa konsola.zip i web.zip z całymi projektami.
  • Poza archiwami zostały tylko modyfikowane pliki źródłowe i plik wykonywalny.
  • Płyta nagrana, otwarta i sprawdzona po nagraniu, opisana numerem zdającego, zapakowana w pudełku wraz z arkuszem.

Ostatnie pięć minut: nie poprawiaj już kodu. Otwórz płytę, przejdź trzy podfoldery, policz pliki i porównaj z listą z tej checklisty. Poprawka, która nie trafi na płytę, nie istnieje dla egzaminatora.

Przećwicz to na czas

Przeczytanie rozwiązania to nie to samo, co napisanie dwóch aplikacji w 180 minut. Otwórz oryginalny arkusz, ustaw stoper i zrób zadanie od zera — dopiero wynik tej próby pokaże, gdzie naprawdę stoisz.