---
# System prepended metadata

title: Flask vaja 2 - Vizualizacija API podatkov

---

# Flask vaja 2 - Vizualizacija API podatkov

V tej vaji boš ustvaril preprosto Flask aplikacijo, ki uporablja javno dostopen API in prikaže podatke na spletni strani.

## Potrebne knjižnice

```bash
pip install flask requests
```

## Osnove - ponovitev

### Flask osnove

```python
# Uvozimo potrebne knjižnice
from flask import Flask, render_template, request

# Ustvarimo instanco Flask aplikacije
# __name__ je posebna spremenljivka v Pythonu, ki vsebuje ime trenutnega modula
app = Flask(__name__)

# Definiramo pot (route) za domačo stran
# @app.route je dekorator, ki pove Flasku, kateri URL naj sproži to funkcijo
@app.route('/')
def domov():
    # render_template poišče HTML datoteko v mapi 'templates' in jo prikaže
    # podatek="vrednost" pošlje spremenljivko 'podatek' v HTML predlogo, 
    # kjer jo lahko uporabimo s {{ podatek }}
    return render_template('index.html', podatek="vrednost")

# Ta del poskrbi, da se aplikacija zažene, ko poženemo ta Python file
# debug=True omogoča samodejno osveževanje ob spremembah kode
if __name__ == '__main__':
    app.run(debug=True)
```

### Jinja osnove

```html
<!-- Primer Jinja sintakse -->
<!-- 
    Jinja je sistem predlog za Python, ki ga Flask uporablja za prikazovanje HTML.
    Omogoča nam dinamično generiranje HTML s Pythonovimi spremenljivkami in izrazi.
-->

<!-- Prikazovanje spremenljivke: uporabimo {{ ime_spremenljivke }} -->
<h1>{{ podatek }}</h1>

<!-- Zanka for: sprehajanje po seznamu elementov -->
{% for x in seznam %}
    <!-- Za vsak element v seznamu se prikaže en odstavek -->
    <p>{{ x }}</p>
{% endfor %}

<!-- Pogojni stavek if: prikažemo vsebino samo, če je pogoj izpolnjen -->
{% if pogoj %}
    <p>To se prikaže, če je pogoj True</p>
{% endif %}
```

## Dodajanje forme v Flask

### Osnovna forma v HTML

```html
<!-- templates/index.html -->
<!DOCTYPE html>
<html>
<head>
    <title>Moja API Aplikacija</title>
</head>
<body>
    <h1>Iskanje</h1>
    
    <!-- 
        Forma za vnos podatkov:
        - method="POST": podatke pošljemo na strežnik (POST zahteva)
        - action="/iskanje": URL, kamor pošljemo podatke (Flask route)
    -->
    <form method="POST" action="/iskanje">
        <!-- 
            Label (oznaka) in input (vnosno polje) za vnos iskalnega niza
            - for="iskalni_niz": poveže oznako z vnosnim poljem
            - name="iskalni_niz": ime, pod katerim bo dostopen vnešen podatek v Flasku
            - required: polje mora biti izpolnjeno
        -->
        <label for="iskalni_niz">Iskalni niz:</label>
        <input type="text" id="iskalni_niz" name="iskalni_niz" required>
        
        <!-- 
            Spustni seznam (dropdown) za izbiro kategorije
            - name="kategorija": ime, pod katerim bo dostopna izbira v Flasku
        -->
        <label for="kategorija">Kategorija:</label>
        <select id="kategorija" name="kategorija">
            <!-- Vsaka možnost ima vrednost (value), ki se pošlje na strežnik -->
            <option value="filmi">Filmi</option>
            <option value="knjige">Knjige</option>
            <option value="glasba">Glasba</option>
        </select>
        
        <!-- Gumb za pošiljanje forme -->
        <button type="submit">Išči</button>
    </form>
</body>
</html>
```

### Obdelava forme v Flask

```python
# Definiramo pot '/iskanje' in dovolimo samo POST zahteve (poglej form method v index.html)
# methods=['POST'] pomeni, da ta funkcija sprejema samo POST zahteve (iz obrazca)
@app.route('/iskanje', methods=['POST'])
def iskanje():
    # Pridobimo podatke iz forme
    # request.form.get('ime_polja') vrne vrednost, ki jo je uporabnik vnesel v formo
    iskalni_niz = request.form.get('iskalni_niz')  # Vrednost iz polja z name="iskalni_niz"
    kategorija = request.form.get('kategorija')    # Vrednost iz polja z name="kategorija"
    
    # TUKAJ BI NORMALNO POSLALI ZAHTEVO NA API
    # Na tem mestu bi dodali kodo za pridobivanje podatkov iz API-ja
    # (primer je v naslednjem odseku kode)
    
    # Prikažemo predlogo 'rezultati.html' in ji pošljemo pridobljene podatke
    # Ti podatki bodo dostopni v predlogi kot {{ iskalni_niz }} in {{ kategorija }}
    return render_template('rezultati.html', iskalni_niz=iskalni_niz, kategorija=kategorija)
```

## Povezava z API-jem

```python
# Uvozimo knjižnico requests, ki nam omogoča pošiljanje HTTP zahtev
import requests

def pridobi_podatke_iz_api(iskalni_niz):
    """Funkcija za pridobivanje podatkov iz API-ja
    
    Args:
        iskalni_niz (str): Besedilo, po katerem iščemo
        
    Returns:
        list: Seznam rezultatov iz API-ja ali prazen seznam, če ni rezultatov
    """
    
    # Sestavimo URL za API zahtevo
    # f-string (f"...") omogoča vključevanje spremenljivk v niz z uporabo {}
    # Primer za Open Movie Database API (OMDb)
    url = f"http://www.omdbapi.com/?s={iskalni_niz}&apikey=TVOJ_API_KLJUC"
    
    # Pošljemo GET zahtevo na API
    # requests.get() pošlje HTTP GET zahtevo na podan URL
    odgovor = requests.get(url)
    
    # Preverimo, ali je bila zahteva uspešna
    # Koda 200 pomeni "OK" (uspešna zahteva)
    if odgovor.status_code == 200:
        # Pretvorimo odgovor iz JSON formata v Python slovar ali seznam
        # .json() metoda pretvori JSON v Python podatkovne strukture
        # .get('Search', []) vrne vrednost ključa 'Search' ali prazen seznam, če ključa ni
        return odgovor.json().get('Search', [])
    
    # Če zahteva ni bila uspešna, vrnemo prazen seznam
    return []
```

### Prikaz rezultatov v HTML

```html
<!-- templates/rezultati.html -->
<!DOCTYPE html>
<html>
<head>
    <title>Rezultati iskanja</title>
</head>
<body>
    <!-- Prikažemo naslov z vrednostmi, ki smo jih dobili iz Flask funkcije -->
    <h1>Rezultati iskanja za "{{ iskalni_niz }}" v kategoriji {{ kategorija }}</h1>
    
    <!-- Povezava nazaj na domačo stran -->
    <a href="/">Novo iskanje</a>
    
    <div class="rezultati">
        <!-- Preverimo, ali imamo kaj rezultatov -->
        {% if rezultati %}
            <!-- Če imamo rezultate, se sprehodimo po seznamu -->
            {% for rezultat in rezultati %}
                <!-- Za vsak rezultat ustvarimo div z ustreznimi podatki -->
                <div class="element">
                    <!-- Prikažemo naslov filma (Title je ključ v JSON odgovoru) -->
                    <h2>{{ rezultat.Title }}</h2>
                    
                    <!-- Če obstaja poster, ga prikažemo -->
                    {% if rezultat.Poster %}
                        <!-- src je URL slike, alt je nadomestno besedilo (za dostopnost) -->
                        <img src="{{ rezultat.Poster }}" alt="{{ rezultat.Title }}">
                    {% endif %}
                    
                    <!-- Prikažemo leto izida filma -->
                    <p>Leto: {{ rezultat.Year }}</p>
                </div>
            {% endfor %}
        {% else %}
            <!-- Če ni rezultatov, prikažemo sporočilo -->
            <p>Ni rezultatov.</p>
        {% endif %}
    </div>
</body>
</html>
```

## Celoten primer: Vremenska aplikacija

### app.py

```python
# Uvozimo potrebne knjižnice
from flask import Flask, render_template, request  # Flask za spletno aplikacijo
import requests  # Za pošiljanje zahtev na API

# Ustvarimo instanco Flask aplikacije
app = Flask(__name__)

# Če potrebuješ API ključ (za OpenWeatherMap ga potrebuješ)
# Za pridobitev ključa se moraš registrirati na https://openweathermap.org/
API_KLJUC = 'tvoj-api-kljuc'  # Tukaj vstavi svoj API ključ!

# Definiramo pot '/' (domača stran) in dovolimo obe vrsti zahtev: GET in POST
# GET zahteve so za prikaz strani, POST zahteve so za obdelavo podatkov iz forme
@app.route('/', methods=['GET', 'POST'])
def index():
    # Najprej preverimo, ali je prišla POST zahteva (iz forme)
    if request.method == 'POST':
        # Pridobimo ime mesta iz forme
        mesto = request.form.get('mesto')
        
        # Sestavimo URL za API zahtevo
        # Dodamo parametre:
        # - q: ime mesta
        # - units=metric: za temperaturo v Celzijih (namesto Fahrenheit)
        # - appid: naš API ključ
        url = f"https://api.openweathermap.org/data/2.5/weather?q={mesto}&units=metric&appid={API_KLJUC}"
        
        # Pošljemo GET zahtevo na API
        odgovor = requests.get(url)
        
        # Preverimo, ali je bila zahteva uspešna (koda 200)
        if odgovor.status_code == 200:
            # Pretvorimo JSON odgovor v Python slovar
            podatki = odgovor.json()
            
            # Ustvarimo nov slovar z le tistimi podatki, ki jih potrebujemo
            # JSON struktura iz OpenWeatherMap API-ja je precej kompleksna,
            # zato izluščimo samo potrebne podatke
            vreme = {
                'mesto': podatki['name'],  # Ime mesta
                'temperatura': podatki['main']['temp'],  # Temperatura v °C
                'opis': podatki['weather'][0]['description'],  # Opis vremena (npr. "oblačno")
                'ikona': podatki['weather'][0]['icon'],  # ID ikone za prikaz
                'vlaga': podatki['main']['humidity']  # Vlažnost v %
            }
            
            # Prikažemo predlogo 'vreme.html' s pridobljenimi podatki
            return render_template('vreme.html', vreme=vreme)
        else:
            # Če je prišlo do napake, prikažemo indeks z napako
            return render_template('index.html', napaka="Mesta ni mogoče najti!")
    
    # Če je GET zahteva (prvi obisk strani), samo prikažemo indeks
    return render_template('index.html')

# Zažene aplikacijo, če je ta datoteka glavna (se izvaja neposredno)
if __name__ == '__main__':
    app.run(debug=True)  # debug=True omogoča samodejno osveževanje ob spremembah
```

### templates/index.html

```html
<!DOCTYPE html>
<html>
<head>
    <title>Vremenska aplikacija</title>
</head>
<body>
    <div>
        <h1>Vremenska aplikacija</h1>
        
        <!-- 
            Prikaz sporočila o napaki, če obstaja:
            - Če spremenljivka 'napaka' obstaja (jo je Flask poslal),
              se prikaže sporočilo o napaki
        -->
        {% if napaka %}
            <p>{{ napaka }}</p>
        {% endif %}
        
        <!-- 
            Obrazec za vnos mesta:
            - method="POST": podatke pošljemo na strežnik s POST metodo
            - Ker ni atributa action, se form pošlje na trenutni URL (/)
        -->
        <form method="POST">
            <div>
                <!-- Label in input za vnos imena mesta -->
                <label for="mesto">Vnesite ime mesta:</label>
                <input type="text" id="mesto" name="mesto" required>
            </div>
            
            <!-- Gumb za pošiljanje forme -->
            <button type="submit">Prikaži vreme</button>
        </form>
    </div>
</body>
</html>
```

### templates/vreme.html

```html
<!DOCTYPE html>
<html>
<head>
    <!-- Naslov strani z imenom mesta (dinamično iz Flask-a) -->
    <title>Vreme za {{ vreme.mesto }}</title>
</head>
<body>
    <div>
        <!-- Povezava za vrnitev na domačo stran -->
        <a href="/">← Nazaj</a>
        
        <!-- Glavni podatki o vremenu -->
        <div>
            <!-- Ime mesta -->
            <h1>{{ vreme.mesto }}</h1>
            
            <!-- Glavni vremenski podatki -->
            <div>
                <!-- 
                    Vremenska ikona:
                    - OpenWeatherMap ponuja ikone na tem URL-ju
                    - vreme.ikona je ID ikone, ki smo ga dobili iz API-ja
                    - @2x.png pomeni večjo različico ikone
                -->
                <img src="https://openweathermap.org/img/wn/{{ vreme.ikona }}@2x.png" alt="{{ vreme.opis }}">
                
                <!-- 
                    Temperatura:
                    - vreme.temperatura|round(1) zaokroži vrednost na 1 decimalno mesto
                    - Pipe (|) v Jinji uvaja filter, ki preoblikuje vrednost
                -->
                <div>{{ vreme.temperatura|round(1) }}°C</div>
                
                <!-- Opis vremena (npr. "oblačno", "jasno"...) -->
                <div>{{ vreme.opis }}</div>
            </div>
            
            <!-- Dodatni vremenski podatki -->
            <div>
                <!-- Vlažnost v odstotkih -->
                <p>Vlažnost: {{ vreme.vlaga }}%</p>
            </div>
        </div>
    </div>
</body>
</html>
```

### Opomba glede CSS

Pri tej vaji se ne bomo osredotočali na CSS. Za delujočo aplikacijo je dovolj, da pripravite samo Python kodo in HTML predloge.


## Ideje za API-je

Nekaj javno dostopnih API-jev za tvojo nalogo:

~~1. **Vremenska napoved** - OpenWeatherMap: `https://openweathermap.org/api`~~
2. **Države** - REST Countries: `https://restcountries.com/`
3. **Slike psov** - Dog API: `https://dog.ceo/dog-api/`
4. **Informacije o filmih** - OMDb API: `https://www.omdbapi.com/`
5. **Vesoljske slike** - NASA API: `https://api.nasa.gov/`
6. **Šale** - JokeAPI: `https://jokeapi.dev/`
7. **Pokemoni** - PokéAPI: `https://pokeapi.co/`
8. https://github.com/public-apis/public-apis

## Naloga

Tvoja naloga je:

1. Izberi poljuben javno dostopen API iz zgornjega seznama (ali najdi drug API)
2. Ustvari preprosto Flask aplikacijo z dvema HTML predlogama:
   - Prva za prikaz forme za vnos parametrov
   - Druga za prikaz rezultatov iz API-ja
3. Dodaj preprosto CSS stiliranje (lahko AI)
4. Pošlji zahtevo na API in prikaži podatke na spletni strani

## Struktura projekta

```
projekt/
├── app.py          # Glavna Flask aplikacija
└── templates/      # Mapa za HTML predloge
    ├── index.html  # Začetna stran s formo
    └── vreme.html  # Stran za prikaz vremena
```
