Rešavanje

13.2 Rešavanje 

Kada smo zadovoljni sa dizajnom našeg budućeg programa, lagano možemo da počnemo da pišemo delove programa koji predstavljaju realizaciju našeg rešenja. Predlažem da prvo napravimo skriptu koja neće ništa raditi, tj. samo će nam prikazati naš dizajn kako radi. Budući da će nam za imena fajlova trebati modul koji se zove time, nađi njegovu dokumentaciju, malo prelistaj, pa se vrati na ovo mesto, jer ga možemo iskoristiti na neočekivan način.
Sačuvaj ovu skriptu pod nazivom beckup_v1.py
#!/usr/bin/env python3 

'''Skripta koja radi bekap fajlova.''' 

import time 

def glavni(): 
    print('Pitam korisnika') 
    time.sleep(3) 
    print('Čuvam u listu fajlove') 
    time.sleep(3) 
    print('Pitam za destinaciju') 
    time.sleep(3) 
    print('Radim Backup.') 
    time.sleep(3) 
    input('Kraj! Pritisni Enter za izlaz!') 


if __name__ == '__main__': 
    glavni()
I, kada pokrenemo skriptu:
./beckap_v1.py 
Pitam korisnika 
Čuvam u listu fajlove 
Pitam za destinaciju 
Radim Backup. 
Kraj! Pritisni Enter za izlaz! 
Vidimo da nam time.sleep() služi kako bi smo usporili izvršavanje programa, tj. da bi se stekao utisak da ova skripta nešto radi. Na žalost, sem ispisa na ekranu, ona ne radi ništa korisno! Ali, služi nama da bi smo napravili konstrukciju. Sada menjamo ovaj fajl, i dodajemo mu iznad funkcije glavni() ove nove dve funkcije:
def pita(pitanje, kraj = None): 
    '''Pita korisnika pitanja, ukoliko nije naznačena reč za kraj 
    pita samo jedno pitanje, vraća listu odgovora.''' 
    pass 

def bekap(izvor, destinacija): 
    '''Vrši bekap fajlova koji se nalaze u listi izvor u destinaciju.''' 
    pass 
One trenutno ne rade ništa, ali su korisne zato što smo njima „izolovali“ glavne probleme. Funkcija pita() će biti pozvana svaki put kada želimo da postavimo neko pitanje korisniku, a funkcija bekap će raditi ono što joj i ime govori.
Budući da nam je funkcija pita() prilično važna, i prva je na spisku, hajde nju da „razradimo“:
def pita(pitanje, kraj = None): 
    '''Pita korisnika pitanja, ukoliko nije naznačena reč za kraj 
    pita samo jedno pitanje, vraća listu odgovora.''' 
    odgovori = [] 
    prompt = 'Veran Bekaper v1.1 >>> ' 
    if not kraj: 
        print(pitanje) 
        odgovor = input(prompt) 
        odgovori.append(odgovor) 
    else: 
        print(pitanje) 
        while True: 
            odgovor = input(prompt) 
            if odgovor == kraj: 
                break 
            odgovori.append(odgovor) 
    return odgovori
Ako ti nije jasno nešto unutar ovog koda, pokušaj da staviš komentare iznad svake linije. Svrha ove funkcije je da postavi jedno, ili više pitanja korisniku, u zavisnosti da li smo joj prosledili ključnu reč koja služi za prekid rada funkcije. Da bi proverili kako radi, promenimo direktorijum u kojem smo sačuvali skriptu, i pokrenimo Python:
>>> import beckap_v1 
>>> odgovor = beckap_v1.pita("Unesi fajl") 
Unesi fajl 
Veran Bekaper v1.1 >>> ~/Desktop/Novi\ rad/Test\ Kodovi/13.Poglavlje/beckap_v1.py 
>>> print(odgovor) 
['~/Desktop/Novi\\ rad/Test\\ Kodovi/13.Poglavlje/beckap_v1.py'] 
>>> odgovor = beckap_v1.pita("Unesi fajl", 'kraj') 
Unesi fajl 
Veran Bekaper v1.1 >>> ~/Desktop/Novi\ rad/Test\ Kodovi/13.Poglavlje/beckap_v1.py 
Veran Bekaper v1.1 >>> ~/Desktop 
Veran Bekaper v1.1 >>> /usr 
Veran Bekaper v1.1 >>> /usr/bin 
Veran Bekaper v1.1 >>> kraj 
>>> print(odgovor) 
['~/Desktop/Novi\\ rad/Test\\ Kodovi/13.Poglavlje/beckap_v1.py', '~/Desktop', '/usr', '/usr/bin'] 
>>> 
Vidimo da funkcija radi upravo ono što smo od nje i tražili – vraća nam jedan ili više fajlova koje korisnik unese. Ova funkcija može i da se poboljša. Za domaći, uradi to (na primer, ne mora biti ovoliko dugačka, a takođe možda i da obavesti korisnika koja mu je ključna reč za izlaz iz petlje).
Ok. Sada kada imamo ovu funkciju koja radi baš ono što treba, možemo da se usredsredimo na funkciju koja treba da nam radi bekap fajlova.
Prvo što nam treba je podatak da želimo zip arhivu, koja će imati određeno ime (koje je trenutni datum i vreme) i nalaziće se u folderu koji je korisnik odredio. Hmmm... Možda je pametno ovo staviti u zasebnu funkciju? Šta nam onda ostaje za funkciju bekap()? Ostaje nam da osmislimo „motor“, tj nešto što naše fajlove „kompresuje“. Već sam rekao da ćemo koristiti modul zipfile? Jesi li našao dokumentaciju i pročitao malo? Nisi... Nije frka.... Rad sa zipfile-om se ne razlikuje mnogo od rada sa običnim fajl objektima. Znači, sada pravimo jednu novu funkciju, i jednu menjamo:
def ime_arh(destinacija): 
    '''Kreira ime arhive, koja će se nalaziti u destinaciji. Vraća string.''' 
    arhiva = '{0}{1}{2}.zip'.format(destinacija, os.sep, time.strftime('%Y%m%d%H%M%S')) 
    return arhiva 
            

def bekap(izvor, destinacija): 
    '''Vrši bekap fajlova koji se nalaze u listi izvor u destinaciju.''' 
    arhiva = zipfile.ZipFile(destinacija, mode = 'w', allowZip64 = True) 
    for fajl in izvor: 
        arhiva.write(fajl, arcname = os.path.abspath(fajl), compress_type = zipfile.ZIP_DEFLATED) 
    arhiva.close()
U funkciji ime_arh() smo kreirali ime koje u sebi sadrži trenutni datum, pomoću funkcije strftime() iz time modula. 
Naziv zip arhive koju želimo da stvorimo je trenutni datum i vreme, koji smo generisali pomoću time.strftime() funkcije. Arhiva će takođe imati .zip ekstenziju i čuvaće se u direktorijumu koji je prosleđen pod imenom destinacija. Obrati pažnju kako koristimo promenljivu os.sep – ona uvek prdstavlja podrazumevani separator direktorijuma za operativnom sistem u kojem je pokrenut ovaj program. Drugim rečima, vrednost os.sep promenljive će biti '/' na Linux-u ili Unix-u, '\\' na Windows-u i ':' na MacOS-u. Korišćenjem os.sep umesto da navedemo neku od ovih oznaka, mi smo postigli da će naš program biti prenosiv (portabl), i da će bez ikakvih izmena, moći da radi na svim sistemima na kojima postoji instaliran Python.
Funkciji time.strftime() prosleđujemo argumente, kao one koje smo i koristili u funkciji ime_arh(). %Y oznaka će biti zamenjena godinom, %m će biti zamenjena sa tekućim mesecom (predstavljenim kao broj između 01 i 12) itd. Kompletan spisak ovih oznaka se može naći na linku Reference i uputstava za Python
Na kraju sam kreirao ime datoteke tako što sam koristio formatiranje stringova.
Potom sam kreirao funkciju koja radi sam bekap – kreirao sam zipfile objekat, koji će se zvati kao i prosleđena destinacija, u režimu za upisivanje. Zatim sam sa for petljom uzimao svaki fajl iz liste i upisao ih u arhivu, u kojoj će takav fajl zadržati „svojstvo“ absolutne sistemske putanje do originalnog fajla. Drugim rečima ime svakog pojedinačnog fajla će u stvari biti putanja do originalnog fajla, i to sam postigao koristeći argument sa ključnom reči arcname u koji sam prosledio funkciju os.path.abspath() koja daje traženu vrednost.
Naravno, sada nam početak programa mora izgledati otprilike ovako:
import time 
import os, zipfile
Sada imamo sve elemente programa, vreme je da izmenimo funkciju glavni() kako bi nam program radio upravo ono što od njega tražimo. Na kraju naš program treba da izgleda nekako ovako (možda ćeš ti napraviti drugačiji, ko zna?):
#!/usr/bin/env python3 

'''Skripta koja radi bekap fajlova.''' 

import time 
import os, zipfile 

def pita(pitanje, kraj = None): 
    '''Pita korisnika pitanja, ukoliko nije naznačena reč za kraj 
    pita samo jedno pitanje, vraća listu odgovora.''' 
    odgovori = [] 
    prompt = 'Veran Bekaper v1.1 >>> ' 
    if not kraj: 
        print(pitanje) 
        odgovor = input(prompt) 
        odgovori.append(odgovor) 
    else: 
        print(pitanje) 
        while True: 
            print('Unesi reč "{}" za kraj unosa.'.format(kraj)) 
            odgovor = input(prompt) 
            if odgovor == kraj: 
                break 
            odgovori.append(odgovor) 
    return odgovori 

def ime_arh(destinacija): 
    '''Kreira ime arhive, koja će se nalaziti u destinaciji. Vraća string.''' 
    arhiva = '{0}{1}{2}.zip'.format(destinacija, os.sep, time.strftime('%Y%m%d%H%M%S')) 
    return arhiva 
            

def bekap(izvor, destinacija): 
    '''Vrši bekap fajlova koji se nalaze u listi izvor u destinaciju.''' 
    arhiva = zipfile.ZipFile(destinacija, mode = 'w', allowZip64 = True) 
    for fajl in izvor: 
        arhiva.write(fajl, arcname = os.path.abspath(fajl), compress_type = zipfile.ZIP_DEFLATED) 
    arhiva.close() 

def glavni(): 
    '''Glavni deo programa.''' 
    izvor = pita('Unesi imena fajlova koje želiš da bekapuješ', 'kraj') 
    destinacija = pita('Gde želiš da sačuvaš arhivu?') 
    # vraćena nam je lista treba nam string 
    destinacija = destinacija[0] 
    ime = ime_arh(destinacija) 
    print('Radim....') 
    bekap(izvor, ime) 
    input('Kraj! Pritisni Enter za izlaz!') 


if __name__ == '__main__': 
    glavni()
I, kada ga pokrenemo:
./beckap_v1.py 
Unesi imena fajlova koje želiš da bekapuješ 
Unesi reč "kraj" za kraj unosa. 
Veran Bekaper v1.1 >>> /home/lenj/Desktop/Novi rad/Nacrt    
Unesi reč "kraj" za kraj unosa. 
Veran Bekaper v1.1 >>> /home/lenj/Desktop/Novi rad/Problemator.pdf 
Unesi reč "kraj" za kraj unosa. 
Veran Bekaper v1.1 >>> kraj 
Gde želiš da sačuvaš arhivu? 
Veran Bekaper v1.1 >>> /home/lenj/Desktop/Novi rad 
Radim.... 
Kraj! Pritisni Enter za izlaz! 
Radi! Jupi! Arhiva je kreirana na zadatom mestu i sadrži fajlove koje smo zadali – sve je perfektno!
Ovo je sada faza testiranja, gde proveravamo da li naš program radi ispravno, onako kako smo i zamislili. Ako se, na ovom mestu, program ne ponaša kako se od njega očekuje, onda bi smo mi morali da izvršimo debagovanje programa, odnosno da uklonimo eventualne greške (bagove) u programu, ili da menjamo funkcionalnosti, ukoliko smo nešto prevideli (a uvek se nešto previdi, što ćeš uskoro i otkriti). 
Ako tvoj program, koji si pisao samostalno, ne radi, slobodno prepiši moj odozgo, jer ću na osnovu njega dalje da radim. Ukoliko si naveo neki direktorijum u koji nemaš dozvolu da pišeš pokušaj da pokreneš ovu skriptu kao administrator sistema, ukoliko tvoj korisnik ima te privilegije – samo zapamti, ovo ti ne preporučujem, uvek je logično da kreiraš direktorijume i fajlove samo tamo gde ti je dozvoljeno!
Obrati pažnju na način rada i sistem kako smo pretvarali naš dizajn u program – išli smo polako, korak po korak. 
To je to, mi smo napravili skriptu koja pravi rezervnu kopiju naših važnih fajlova! 
Sada kada imaš ovu skriptu koja ti pravi rezervne kopije, možeš je koristiti kad god zaželiš i kada god ti zatreba. Ako si korisnik Linux/Unix operativnog sistema, moj ti je savet da, kada konačno budeš zadovoljan sa radom ovog programa, iskoristiš skriptu tako što ćeš joj dati izvršne dozvole i postaviti u system path, kako bi ti bila dostupna iz terminala u bilo kom momentu. Takođe, možeš i da je podeliš sa svojim prijateljima, koji će ti, za uzvrat, davati svoje pohvale, sugestije, kritike i – najvažnije – otkriće ti nove za njihov sistem, specifične bagove! Ovo se zove operativna faza ili faza dostave softvera (deployment), koja je, isto kao i sve druge faze, značajan korak u razvoju nekog softvera. 
Gornji program radi ispravno, ali (kao i obično) prvi programi ne rade uvek onako kako se od njih i očekuje. Na primer, postoji problem, jer nismo dobro razložili neki problem, ili smo napravili grešku prilikom samog kucanja (pisanja) programa, itd. Zbog toga se, s vremena na vreme, moramo vraćati u projektnu fazu izrade programa ili u fazu u kojoj vršimo debagovanje postojećeg programa.

13.1 Problem Indeks 13.3 Druga
verzija programa

Primjedbe

Popularno

Kako televizija štetno utiče na Vaše zdravlje finansije?

Da li je TV kviz Slagalica "prevara"?

Tu je novi Qt creator 4.4.0!

Kada prijatelji odlaze

Microsoft objavio - nova verzija Windowsa stiže...

Sitnije promene na blogu...

Opet su se pojavili lažni kuponi brenda "IDEA" na društvenim mrežama

Poslednji pozdrav izgubljenom vremenu

Sistemska greška

Fake poet