# 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
```