Treća verzija programa

13.4 Treća verzija programa

Druga verzija radi prilično dobro. Nadam se da si zadovoljan....
Nisam....
Problem: Budući da ja ne volim da kucam previše, i da me mrzi da kucam cele putanje do fajlova, ja sam mislio da će program raditi i kada jednostavno „prevučem“ željeni fajl ili folder u njega... Međutim, izgleda da sam se prevario:
./beckap_v2.py 
Unesi imena fajlova koje želiš da bekapuješ 
Unesi reč "kraj" za kraj unosa. 
Veran Bekaper v2.0 >>> '/home/lenj/Desktop/Novi rad/Test Kodovi' 
Unesi reč "kraj" za kraj unosa. 
Veran Bekaper v2.0 >>> kraj 
Gde želiš da sačuvaš arhivu? 
Veran Bekaper v2.0 >>> '/home/lenj/Desktop' 
Traceback (most recent call last): 
  File "./beckap_v2.py", line 71, in <module> 
    glavni() 
  File "./beckap_v2.py", line 64, in glavni 
    ime = ime_arh(destinacija) 
  File "./beckap_v2.py", line 36, in ime_arh 
    os.mkdir(direktorijum) 
FileNotFoundError: [Errno 2] No such file or directory: "'/home/lenj/Desktop' /Bekap-30-10-2013" 
Takođe kad radim bekap velikog broja fajlova, posle određenog vremena, kada se nakupi prilično mnogo rezervnih kopija, teško mi je da razlikujem šta se nalazi u bekapovima tj arhivama, sve što imam od informacija je vreme kada je bekap rađen! Zašto mi ime zip arhive ne bi govorilo koje su to promene ili verzije fajlova u njemu? Možda kada bih imao mogućnost da ostavim neki komentar u ime ili u samu zip arhivu?
Rešenje: Kao što sam već i rekao, naš program radi savršeno. Ako je to istina, kako to da su se pojavile ove, nimalo naivne greške (bagovi)? Ovakvi bagovi su zaista ozbiljne stvari, na koje moraš da žestoko obratiš pažnju, jer ih ne možeš ni na koji način predvideti! Oni nisu nastali tvojom greškom – ti si napravio program koji radi na tačno određen način, i shodno tome se i koristi na način na koji si ti predvideo. Ali... Korisnik ga je iskoristio na neki (za tebe neočekivan) način koji se njemu činio zgodnim i.... Došao je do zaključka da je tvoj program loš, jer „ne radi“, i pri tome, razglasiće svima na internetu kako on „ništa nevalja“, te će ljudi početi i tebe da izbegavaju, jer - „nevaljaš“. To, sem što je loše za tvoj imidž, loše je i za neki budući profit tebe kao programera.
Šta to govori, i kako izbeći ovako nezgodnu situaciju? Pa, za tebe govori da si bio brzoplet, nisi dovoljno dobro osmislio test fazu, nisi dobro sagledao problem sa svih strana itd.
Šta? OPET sam JA kriv?
Aman bre, čoveče! Poznaješ li ti zakone fizike? Tačnije treći Njutnov zakon? Da te podsetim: „Sila akcije je po intenzitetu jednaka sili reakcije, njihov pravac je isti, ali im se smerovi razlikuju. Ove dve sile uvek idu u paru.“, drugim rečima – svaka akcija uvek izaziva reakciju, koja je uvek suprotna od te akcije. Ti si uradio neku akciju – izbacio si program koji radi nešto, i to je izazvalo reakciju kod korisnika – tvoj program ne radi to nešto onako kako oni žele. I? Šta sad... Ne možeš da se pravdaš da ne umeš da čitaš misli, da nisi neki parapsiholig, i slično....
Šta si trebao da uradiš? Pa.... Opet je to subjektivno pitanje, nešto si trebao da smisliš.... Ali, šta se najčešće radi?
Prvo si trebao da korisnicima dostaviš jasno i nedvosmisleno uputstvo za upotrebu proizvoda, ako je on u svojoj „konačnoj“ fazi.
Ili si, pre nego što si odlučio da program predstaviš „širokim narodnim masama“, trebao da dobro osmisliš testove, i kada ih program sve prođe, tek onda je spreman za upotrebu.
Ili si trebao da izgradiš malu zajednicu, koja bi za tebe testirala program u različitim uslovima i okruženjima, na raznorazne načine, i od koje bi primao povratne informacije gde i šta „zapinje“, pa da se, zajedničkim snagama , otklone svi nedostaci....
Ili da platiš ljude koji bi na sve negativne kritike uzvraćali istom merom... Ili, već kako je tebi najbolje....
Bilo kako da odlučiš da radiš, znaj da bagove nećeš moći da izbegneš – oni su uvek tu, manji ili veći, ozbiljni ili ne. Zato je vreme da se razmisli, i da se ti bagovi uspešno otklone!
Aman, čekaj malo... Da taj „zakon akcije i reakcije“ stvarno funkcioniše, mi sada ne bi smo imali ništa, ceo svet bi stajao i tapkao u mestu.....
Pa u životu nisu samo ove dve sile upletene, ima ih mnogo više, na primer, ako imaš jakog sponzora, on te može gurati, ma koliko reakcija želela da te drži „u mestu“. Sem toga, zašto sve shvatati lično? Ako si dovoljno mudar i učiniš nešto, jedva ćeš čekati reakciju okrenutu protiv tebe!
Brate, pa ja nisam neki sado-mazo tip, da volim da me neko napada...
Čekaj malo... Kakav sado-mazo fazon, o čemu ti to govoriš?
Ti si izazvao akciju – pružio si ljudima program, informaciju koju smatraš da im je potrebna. Naravno, sada čekaš reakciju (koja ne može biti pozitivna, jer onda nije reakcija, već nečiji interes za tvoj rad) koja je INFORMACIJA! I to veoma korisna informacija, koja ti govori na koju stranu da se okreneš kako bi ispravio/poboljšao nešto.
Uzmimo na primer, da je ovaj program komercijalan i da ga prodaješ po ceni od 0.5 eura. Nezadovoljne mušterije su ti učinile veoma veliku uslugu – rekli su ti šta im smeta! Ako si zadrt, dići ćeš ruke od svega, a ako si dovoljno mudar, razmislićeš, osmisliti rešenje i izbaciti novu verziju istog ovog programa, samo što sada, budući da je, kao fol, moćniji, umesto 0.5 košta 1 euro!
Zbog ove činjenice, ne treba da te čudi pojava da mnogi proizvođači prave poseban softver, ili plaćaju neke marketing agencije da ih naizmenično hvale, ili na nekom drugom mestu oštro napadaju – jednostavno, da bi izazvale reakciju okoline, koja će im sama reći šta joj smeta, i šta treba da se menja. Kada im „udovoljite“ biće spremni da izdvoje više za isti proizvod.
Čekaj, da kupe ono što im se ne sviđa?
Da.... Ljudska psiha je čudo. Razmisli malo o svom ponašanju – kada ti neko loše priča o nečemu što je za tebe nepoznato, ti ćeš malo „istraživati“ da vidiš da li je taj neko u pravu, jer ne želiš da budeš prevaren. I, tu je zamka. Postoji proizvod koji ne možeš da opipaš, možeš verovati nekome na reč da je to loše, ili - „vidi, pa ovo košta samo 1 euro, kupujem!“. Vidi, drugar ti je bio u pravu, sada znaš da si prevaren, želiš da iskališ svoju bes i ideš da se žališ. Krug se zatvorio – proizvođač je dobio povratnu informaciju tako da sledeća verzija programa, u kojoj postoji upravo ono što tebi treba, košta 2 eura.
Ali, sve je to samo oštar nož koji se zove marketing. Kao što vidiš, sa njim nikada ne znaš – možda uspeš da dotakneš zvezde, a može i da te baci u istoriju kao najvećeg prevaranta, iako si ti samo običan čovek koji pokušava da naplati svoj rad i trud.
Eh.... Zato se, mali moj učeniče, drži se ti Open Source-a i zajednice – makar ćeš naučiti nešto bez nekog rizika po svoju ugled i čast, koju ako izgubiš.... Ma ništa i u tom slučaju.... Možeš brati maline?
„Mali“?!? Imam metar devedesetšest i 110 kilograma!
Savete za dijetu ne dajem ovde, dovoljno sam ti tupio o marketinškim potezima, i mislim da je vreme da se vratimo na naš problem koji se tiče jedino programiranja!
Ma kakva dijeta, pa bildujem od....
Elem, pogledaj koju grešku nam je Python prijavio, evo najvažnijeg dela koji nam i najviše govori:
FileNotFoundError: [Errno 2] No such file or directory: "'/home/lenj/Desktop' /Bekap-30-10-2013" 
Kaže nam da ne postoji dotični direktorijum koji je naš program pokušao da kreira. Koji direktorijum ne postoji? Gledajući pažljivije u putanju dolazimo do zaključka da je Python pokušao da nađe ovaj direktorijum (napisaću ga između kosih crta, kako bi bilo jasnije) - /Desktop' /. Uh, Desktop imam, ali Desktop sa apostrofom i praznim mestom iza – nemam.
Šta se desilo? Očigledno je greška prilikom korisnikovog unosa, hajde da je „izazovemo“ u našim uslovima, tako što ćemo napisati petlju koja nam služi za prikazivanje onoga što je ubačeno u prompt:
>>> while True: 
...     p = input('Ubaci >>> ') 
...     p 
...     if len(p) == 0: 
...             break 
... 
Ubaci >>> '/home/lenj/Desktop/Novi rad/Test Kodovi/13.Poglavlje/__pycache__' 
"'/home/lenj/Desktop/Novi rad/Test Kodovi/13.Poglavlje/__pycache__' " 
Ubaci >>> '/home/lenj/Desktop/Novi rad/Test Kodovi/13.Poglavlje/beckap_v1.py' 
"'/home/lenj/Desktop/Novi rad/Test Kodovi/13.Poglavlje/beckap_v1.py' " 
Ubaci >>> 
'' 
>>> 
Vidimo da operativni sistem u naš prompt ubacuje i navodnike zajedno sa putanjom, a takođe i jedno prazno mesto iza ubačene stvari. Python, sa druge strane, smatra da su i navodnici, ili apostrofi, ili to prazno polje – deo stringa. A tako loše formulisan string je ubačen u listu, a zatim i na kreiranje putanje – koja će izbaciti grešku jer – ne postoji putanja ka direktorijumu, bar ne takva sa apostrofom!
Sada, kada znamo šta je problem, možemo i da ga rešimo!
Moj predlog je da, prilikom korisničkog unosa izvršimo proveru stringa, pa, ukoliko je unesen string koji počinje ili se završava sa navodnikom, apostrofom ili praznim mestom da izvršimo isecanje i odbacivanje nepotrebnog dela. Za to nam treba nova funkcija! Budući da vršimo izmene nad skriptom, opet kopiramo naš prethodni program sa drugim imenom: beckap_v3.py
def provera(putanje): 
    '''Proverava svaku putanju iz liste, vraća nam listu sa ispravnim putanjama.''' 
    povratna = [] 
    for i in putanje: 
        if os.path.exists(i): 
            povratna.append(i) 
        else: 
            while not os.path.exists(i): 
                prvo = i[0] 
                if prvo == ' ' or prvo == '"' or prvo == "'": 
                    i = i[1:] 
                zadnje = i[-1] 
                if zadnje == ' ' or zadnje == '"' or zadnje == "'": 
                    i = i[:-1] 
            povratna.append(i) 
    return povratna 
Ovo je naša nova funkcija, shodno njoj, moramo promeniti i naš glavni deo programa:
def glavni(): 
    '''Glavni deo programa.''' 
    izvor = pita('Unesi imena fajlova koje želiš da bekapuješ', 'kraj') 
    izvor = provera(izvor) 
    destinacija = pita('Gde želiš da sačuvaš arhivu?') 
    destinacija = provera(destinacija) 
    # 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!')
Kada sačuvamo, i pokrenemo naš program, vidimo da radi. Zadovoljan? Jesi? E – ja nisam. Nehotice smo upravo napravili jedan jako težak bag!
./beckap_v3.py 
Unesi imena fajlova koje želiš da bekapuješ 
Unesi reč "kraj" za kraj unosa. 
Veran Bekaper v3.0 >>> /lololo 
Unesi reč "kraj" za kraj unosa. 
Veran Bekaper v3.0 >>> kraj 

Upravo nam je program postao beskoristan! Kada smo naveli nepostojeću lokaciju, naš program se jednostavno – zaglavio. Možda se pitaš ko bi ubacivao nepostojeće stvari, i misliš da je ovaj program sasvim ok, ali nemoj da se zavaravaš! Svakakvih ljudi ima, a uvek postoji i elemenat slučaja, zapravo – prava je sreća da smo ovaj problem primetili na vreme. Ovo je teška greška, možda i primećuješ gde smo je napravili? Tačno tako – u našoj funkciji provera() kod uslova za petlju. Kako bi smo mogli da „rekonstruišemo“ funkciju da radi ono što nam treba?
Možda ovako:
def provera(string): 
    '''Proverava svaku putanju iz liste stringova. 
    Vraća nam listu sa stringovima koji nemaju ',", ' ' na početku ili kraju.''' 
    povratna = [] 
    for i in string: 
        while True: 
            prvo = i[0] 
            zadnje = i[-1] 
            if prvo == ' ' or prvo == '"' or prvo == "'": 
                i = i[1:] 
                continue    
            elif zadnje == ' ' or zadnje == '"' or zadnje == "'": 
                i = i[:-1] 
                continue 
            else: 
                povratna.append(i) 
                break 
    return povratna
Ali, sada nam treba i funkcija koja će proveravati da li putanja postoji.... Takva funkcija se inače naziva helper funkcija, i mi bi smo je koristili da naizmenično pita korisnika, proverava da li je unos dobar, a zatim proverava da li putanja postoji.... Ili nam ipak ne treba takva funkcija? Možda je pametnije da izmenimo funkciju koja nam služi za proveru stringa, jer koristimo Python, pa je moguće da uradimo i jedan mali „trik“ - da funkcija pozove samu sebe! Jednostavno treba pogledati sadašnju konstrukciju funkcije provera(). Ona sasvim lepo radi, i neće nam ometati program u njegovom funkcionisanju, ali nam neće ni izbaciti grešku ukoliko korisnik unese nepostojeću putanju. Mi ne želimo nepostojeće putanje, zar ne? Jer u koliko one postoje, postojaće velika verovatnoća da program na nekom drugom mestu ne radi ono što se od njega očekuje. Mislim da je pametnije dodati još jednu elif klauzulu koja bi nam rešila mnogo muke. U njoj ćemo jednostavno proveriti da li putanja postoji. Ako postoji – ništa, funkcija nastavlja dalje sa svojim radom, ali ako putanja ne postoji, neka izbaci upozorenje korisniku, i ponovo ga pita da unese novu putanju (ili da ne unese ništa, jer, možda se čovek prevario u kucanju), a potom, zarad sigurnosti, opet pozove samu sebe kako bi proverila unetu vrednost! Ako uneta vrednost nije dobra, biće „opet Jovo nanovo“, ali mi ne moramo da vršimo nikakve korekcije u programu! Drugim rečima, ukoliko korisnik konstantno daje pogrešnu lokaciju, naš program će ga gnjaviti i primoravati da se uozbilji!
Sada imamo konstrukciju rešenja, hajde da pogledamo realizaciju (nikada ne treba žuriti sa rešenjima – 5 sekundi razmišljanja više može uštedeti mnoge godine rada koji bi bio uložen da bi se ispravila nehotična greška).
Obrati pažnju šta mi dobijamo u funkcije i šta vraćamo iz funkcija – to su sve liste. Ako bi neki deo ove funkcije pozvao sam sebe sa listom, i mi, nepažnjom „vraćen“ podatak jednostavno ubacili u listu, ne bi se dobro završilo:
>>> a = [1, 2, 3] 
>>> b = [1, 2, 3] 
>>> a.append(b) 
>>> a 
[1, 2, 3, [1, 2, 3]] 
>>> 
Imali bi smo listu unutar liste – Python bi očekivao string i – kraj za rad našeg programa. Umesto toga, liste možemo i koncentrisati sa znakom + (kao i stringove), ili možemo koristiti daleko zastupljeniji metod extend():
>>> a = [1, 2, 3] 
>>> a + b 
[1, 2, 3, 1, 2, 3] 
>>> a.extend(b) 
>>> a 
[1, 2, 3, 1, 2, 3] 
>>> 
Koristićemo extend() metodu, a takođe ću malo i unaprediti if i elif provere, kao i uslov za while petlju:
def provera(putanja): 
    '''Proverava svaku prosleđenu putanju. 
    Vraća nam listu stringova, u kojoj je svaki string ispravna putanja.''' 
    povratna = [] 
    nesme = (' ', '"', "'") 
    for i in putanja: 
        while i: 
            prvo = i[0] 
            zadnje = i[-1] 
            if prvo in nesme: 
                i = i[1:] 
                continue 
            elif zadnje in nesme: 
                i = i[:-1] 
                continue 
            elif  not os.path.exists(i): 
                upozorenje = 'OPREZ!\nPutanja\n{}\nNE POSTOJI!\nŽelite li da unesete novu putanju, pritisni Samo Enter za ne? \n '.format(i) 
                pomocna = pita(upozorenje) 
                if len(pomocna) != 0: 
                    pomocna = provera(pomocna) 
                    povratna.extend(pomocna) 
                break 
            else: 
                povratna.append(i) 
                break 
    return povratna
Takođe, u programu nam je važno da imamo krajnju destinaciju – gde će arhiva biti sačuvana. Ovakva konstrukcija funkcije provera() nam može vratiti i praznu listu, zato je bolje da u slučaju kada korisnik ne unese ispravnu lokaciju mi ga pitamo ponovo za nju:
def glavni(): 
    '''Glavni deo programa.''' 
    izvor = [] 
    while not izvor: 
        izvor = pita('Unesi imena fajlova koje želiš da bekapuješ', 'kraj') 
        if izvor: 
            izvor = provera(izvor) 
    destinacija = [] 
    while not destinacija: 
        destinacija = pita('Gde želiš da sačuvaš arhivu?') 
        if  destinacija: 
            destinacija = provera(destinacija) 
    # 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!')
Na ovom mestu smo uradili i zanimljivost – da nam program ne bi izbacivao grešku kada korisnik ne unese ništa (lista nam se vrati sa praznim stringom) mi ne želimo da pokrećemo funkciju koja vrši proveru, jer bi nam ona izbacila grešku. Da li možeš da ispratiš tok programa, i vidiš zbog čega sam to uradio i zašto sam izmenio uslov za while petlju u prethodnoj funkciji?
Nakon ovih izmena, naš program radi ovako:
./beckap_v3.py 
Unesi imena fajlova koje želiš da bekapuješ 
Unesi reč "kraj" za kraj unosa. 
Veran Bekaper v3.0 >>> 
Unesi reč "kraj" za kraj unosa. 
Veran Bekaper v3.0 >>> kraj 
Unesi imena fajlova koje želiš da bekapuješ 
Unesi reč "kraj" za kraj unosa. 
Veran Bekaper v3.0 >>> /mok 
Unesi reč "kraj" za kraj unosa. 
Veran Bekaper v3.0 >>> kraj 
OPREZ! 
Putanja 
/mok 
NE POSTOJI! 
Želite li da unesete novu putanju, pritisni Samo Enter za ne? 
 
Veran Bekaper v3.0 >>> 
Unesi imena fajlova koje želiš da bekapuješ 
Unesi reč "kraj" za kraj unosa. 
Veran Bekaper v3.0 >>> '/home/lenj/Desktop/Novi rad/Test Kodovi/13.Poglavlje/__pycache__' 
Gde želiš da sačuvaš arhivu? 
Veran Bekaper v3.0 >>> /nigde 
OPREZ! 
Putanja 
/nigde 
NE POSTOJI! 
Želite li da unesete novu putanju, pritisni Samo Enter za ne? 
 
Veran Bekaper v3.0 >>> 
Gde želiš da sačuvaš arhivu? 
Veran Bekaper v3.0 >>> '/home/lenj/Desktop' 
Kreirao sam novi direktorijum --> /home/boxer/lenj/Bekap-31-10-2013 
Radim.... 
Kraj! Pritisni Enter za izlaz! 
Vau... Teškom mukom smo postigli ono što smo i želeli. Sada korisnik može da prevlači fajlove ili foldere u naš program, i da ih program prepozna, a takođe, program neće dopustiti da ništa ne bude u arhivi, ili da putanja do arhive ne postoji.
Ali, mi imamo još jedan problem, zar ne? Nisi zaboravio da korisnik želi da priloži komentare bekapa, koje ćemo lepiti za naziv arhive?
Kako se ispostavilo, ovo nije ni približno težak zadatak, naspram ovog koji smo malo pre rešavali....
Sve što treba je da izmenimo funkciju ime_arh() i posle toga da odahnemo malo....
def ime_arh(destinacija): 
    '''Kreira direktorijum, koji će se nalaziti u destinaciji a nosi ime koje je 
    trenutni datum. Vraća string koji predstavlja naziv arhive.''' 
    komentar = input('Komentar arhive --> ') 
    if len(komentar) == 0: 
        arh = 'regular' 
    else: 
        arh = 'koment' 
    datum = time.strftime('%d-%m-%Y') 
    vreme = time.strftime('%H%M%S') 
    direktorijum = '{}{}{}-Bekap-{}'.format(destinacija, os.sep, arh, datum) 
    if not os.path.exists(direktorijum): 
        os.mkdir(direktorijum) 
        print('Kreirao sam novi direktorijum -->', direktorijum) 
    arhiva = '{0}{1}{2}{3}.zip'.format(direktorijum, os.sep, vreme, komentar) 
    return arhiva
Mi smo napravili samo minimalnu izmenu funkcije, u kojoj sada tražimo od korisnika da unese neki komentar koristeći input funkciju, a zatim proverimo da li je unesen neki string, na taj način što proverimo dužinu promenljive komentar pomoću funkcije len(). Ukoliko je korisnik pritisnuo samo taster enter (smatramo da je to samo uobičajeni bekap), onda će ova funkcija imenovati direktorijum kao regular. 
Međutim, ako je korisnik uneo neki komentar, onda taj komentar dodajemo u ime zip arhive pre .zip ekstenzije, a naziv direktorijuma će u sebi imati reč koment.
Proveri, i vidi – program sada radi – ma, fantastično je slaba reč, kada smo mi jaki igrači :P
Sve ove korekcije koje smo radili nisu ništa drugo nego obično fiksiranje bagova. Mi smo u fazi održavanja našeg softvera. Izgleda da je to mnogo teži zadatak nego pravljenje softvera? Zapravo jeste – iz prostog razloga: kada kreiraš sam svoj program, ti znaš šta želiš od mašine. Kada održavaš program – ti moraš da znaš mogućnosti i zahteve kako mašine, tako i čoveka koji koristi program. Sa mašinom je lako..... Sa ovim drugim baš i ne...

13.3 Druga verzija Indeks 13.5 Četvrta verzija

Primjedbe

Popularno ovog meseca

Gde pronaći novosti sa facebook stranica nakon velike promene koja ga je zadesila?

Dva načina da vratite svoj Windows 10 na fabričke vrednosti

Kako preuzeti video koji je neko postavio na facebook-u bez upotrebe dodatnih programa?

Više neće biti moguć besplatan prelazak na Windows 10

Prijateljski meč ŠK "Titel" - ŠK "Bukovac"

Tu je novi Qt creator 4.4.0!

Kako manipulisati Windows licencom pomoću slmgr komande?

Fake poet

Da li je poželjno biti anoniman na internetu (ili se predstavljati punim imenom i prezimenom)?

Organizacija foldera i fajlova u Linux fajl sistemu