Car manager - wersja konsolowa cz.1

🕓 · ☕7
🏷️
  • #Python
  • Wprowadzenie

    gantt
    section Car Manager
    Skrypt + baza danych :active,    des1, 2020-01-14,2020-01-16
    Django        :  des2, after des1, 2d
    Desktop app   :         des3, after des2, 2d
    Mobile app   :         des4, after des3, 2d
    

    Czas na rozpoczęcie pracy nad pierwszą wersją aplikacji, wpierw konsolowej napisanej w Pythonie.
    Baza danych w prostym SQlite.
    Repozytorium projektu: Car Manager

    Praca z bazą danych

    Przy uruchamianiu programu chcemy, aby pokazał się aktualny stan pojazdu.
    Czyli w pierwszej kolejności musimy odczytać z bazy danych interesujące nas tabele, a jeśli jej nie ma, to utworzyć nową bazę.

    Sprawdzamy, czy w folderze z programem jest plik bazy danych carmng.db i czy można go odczytać.
    Jeżeli go nie ma, wywołujemy funkcję, która będzie tworzyła bazę danych:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    
    import os
    import sqlite3
    from sqlite3 import Error
    
    def main():
        database = "carmng.db"
        if not os.access(database, os.R_OK):
            #jeżeli nie ma pliku, tworzy bazę danych z dwiema tabelami
            connection = sqlite3.connect(database)
            create_table(connection)
    

    Tworzymy tabelę w bazie przeznaczoną dla informacji o autach i odrębną o statystykach (tankowania, koszty, etc.):

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    
    def create_table(connection):
        sql_create_car_table = """ CREATE TABLE IF NOT EXISTS car (
                                            id integer PRIMARY KEY AUTOINCREMENT UNIQUE,
                                            nazwa text NOT NULL,
                                            marka text,
                                            rocznik text
                                        ); """
     
        sql_create_stats_table = """CREATE TABLE IF NOT EXISTS stats (
                                        id integer PRIMARY KEY AUTOINCREMENT UNIQUE,
                                        data text,
                                        godzina text,
                                        licznik_km integer,
                                        cena numeric, 
                                        ilosc_litrow integer,
                                        koszt integer,
                                        do_pelna integer,
                                        car_id integer NOT NULL,
                                        FOREIGN KEY (car_id) REFERENCES car (id)
                                    );"""
        try:
            cursor = connection.cursor()
            cursor.execute(sql_create_car_table)
            cursor.execute(sql_create_stats_table)
            cursor.close()
        except Error as e:
            print(e)
        finally:
            if (connection):
                connection.close()
    

    Gdy baza już jest, wywołamy funkcję do odczytu danych z bazy, dla konkretnego auta:

    1
    
    showAuto(database)
    

    Teraz trzeba wyświetlić rekordy:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    
    def showAuto(database):
        try:
            connection = sqlite3.connect(database)
            cursor = connection.cursor()
            select_query = """SELECT * from car"""
            cursor.execute(select_query)
            records = cursor.fetchall()
            print("Ilość rekordów:  ", len(records))
            for row in records:
                print("Id: ", row[0])
                print("Nazwa: ", row[1]) 
                print("Marka: ", row[2])
                print("Rocznik: ", row[3])
                print("\n")
    
            cursor.close()
    
        except sqlite3.Error as error:
            print("Błąd odczytu danych z bazy", error)
        finally:
            if (connection):
                connection.close()
    

    Pokażemy sobie na tę chwilę wszystkie rekordy z tabeli ze statystykami dla podanego auta:

    1
    
    showStats(database, car_id)
    

    Printujemy rekordy:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    
    def showStats(database, car_id):
        try:
            connection = sqlite3.connect(database)
            cursor = connection.cursor()
            print(car_id)
            select_query = """SELECT * from stats WHERE car_id = {}""".format(car_id)
            cursor.execute(select_query)
            records = cursor.fetchall()
            print("Total rows are:  ", len(records))
            print("Printing each row")
            for row in records:
                print("Id: ", row[0])
                print("Data: ", row[1]) 
                print("Godzina: ", row[2])
                print("Licznik KM: ", row[3])
                print("Cena: ", row[4])
                print("Ilość litrów: ", row[5])
                print("Koszt: ", row[6])
                print("Do pełna: ", row[7])
                print("\n")
            cursor.close()
    
        except sqlite3.Error as error:
            print("Błąd odczytu danych z bazy", error)
        finally:
            if (connection):
                connection.close()
    

    Czyszczenie ekranu

    Dobrze byłoby czyścić ekran, dlatego dodam wpis (dla windowsa czyszczenie to cls pod linuxem clear):

    1
    
    clear = lambda: os.system('cls')
    

    Teraz za każdym razem, gdy będę chciał wyczyścić ekran, wystarczy, że wpiszę:

    1
    
    clear()
    

    Wybieralne menu

    Skoro mamy już rozpisane wyświetlanie, czas na dodawanie wpisów.
    Na początek wprowadzimy możliwość dodawania aut:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    
    def addAutoToDatabase(database):
        nazwa = input("Podaj nazwę auta: ")
        marka = input("Podaj markę auta: ")
        rocznik = input("Podaj rocznik auta: ")
        
        try:
            sqliteConnection = sqlite3.connect(database)
            cursor = sqliteConnection.cursor()
    
            sqlite_insert_query = """INSERT INTO `car`
                                ('nazwa', 'marka', 'rocznik') 
                                VALUES 
                                ('{}','{}',{})""".format(nazwa, marka, rocznik)
    
            count = cursor.execute(sqlite_insert_query)
            sqliteConnection.commit()
            print("Dodano wpis do bazy danych ", cursor.rowcount)
            cursor.close()
    
        except sqlite3.Error as error:
            print("Błąd przy dodawaniu wpisu do bazy danych", error)
        finally:
            if (sqliteConnection):
                sqliteConnection.close()
    

    Skoro mamy już opcję dodania aut, to czas na opcję dodania wpisów:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    
    def addStatDatabase(database,auto = 1):
        wybor_daty = False
        wybor_do_pelna = False
        while wybor_daty == False:
            data_dzis = input("Wstawić dzisiejszą datę? T/N")
            if data_dzis == "t" or data_dzis == "T":
                data = date.today()
                wybor_daty = True
            elif data_dzis == "n" or data_dzis == "N":
                data = input("Podaj datę tankowania: ")
                wybor_daty = True
            else:
                wybor_daty = False
        godzina = input("Podaj godzinę: ")
        licznik_km = input("Podaj stan licznika kilometrów: ")
        cena = float(input("Podaj cenę: "))
        ilosc_litrow = float(input("Podaj ilość zatankowanego paliwa: "))
        while wybor_do_pelna == False:
            do_pelna = input("Czy zatankowałeś do pełna? T/N")
            if do_pelna == "t" or do_pelna == "T":
                do_pelna = 1
                wybor_do_pelna = True
            elif do_pelna == "n" or do_pelna == "N":
                do_pelna = 0
                wybor_do_pelna = True
            else:
                wybor_do_pelna = False
        koszt = cena * ilosc_litrow
        try:
            sqliteConnection = sqlite3.connect(database)
            cursor = sqliteConnection.cursor()
    
            sqlite_insert_query = """INSERT INTO `stats`
                                ('data', 'godzina', 'licznik_km', 'cena', 'ilosc_litrow', 'koszt', 'do_pelna', 'car_id') 
                                VALUES 
                                ('{}','{}',{},{},{},{},{},{})""".format(data, godzina, licznik_km, cena, ilosc_litrow, koszt,do_pelna, auto)
    
            count = cursor.execute(sqlite_insert_query)
            sqliteConnection.commit()
            print("Dodano wpis do bazy danych ", cursor.rowcount)
            cursor.close()
    
        except sqlite3.Error as error:
            print("Błąd przy dodawaniu wpisu do bazy danych", error)
        finally:
            if (sqliteConnection):
                sqliteConnection.close()
    

    Konfiguracja w pliku ini

    Dobrym rozwiązaniem będzie dodanie domyślnych ustawień i wpływu na nie.
    Do pracy z plikami konfiguracyjnymi, użyję modułu configparser
    Na początku chcę, bym mógł ustawić jakieś auto domyślne, by od razu pokazywały dla niego statystyki.
    Do funkcji dodawania auta, dam możliwość wyboru, czy wprowadzone auto ma być domyślne:

    1
    2
    3
    4
    5
    6
    7
    
    domyslne_auto = input("\nDodać jako auto domyślne?: ")
    if domyslne_auto == "t" or domyslne_auto == "T":
        config = configparser.ConfigParser()
        config['DEFAULT'] = {}
        config['DEFAULT']['CAR_ID'] = str(cursor.lastrowid)
        with open('config.ini', 'w') as configfile:
            config.write(configfile)
    

    Wpierw standardowo w main sprawdzę, czy jest plik .ini tak jak to było w przypadku bazy danych i wczytam ID dla domyślnego auta:

    1
    2
    3
    4
    5
    6
    7
    8
    
    conf = "config.ini"
    car_id = 1
    if os.access(conf, os.R_OK):
        config.read(conf)
        config.sections()
        if 'DEFAULT' in config:
            car_id = int(config['DEFAULT']['CAR_ID'])
    clear()
    

    Wybieralne menu

    By całość lepiej się prezentowała, dodamy menu wyboru poszczególnych opcji.
    Wykorzystamy w tym celu bibliotekę pick

    1
    2
    
    from pick import pick
    import sys
    

    Do fukcji main dopiszemu sobie kod dla menu:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    
    wybor = input("\nWciśnij 'Q', by wyjść, lub inny dowolny klawisz by przejść dalej...")
        clear()
        if wybor == "q" or wybor == "Q":
            sys.exit()
        wyjscie = False
        while wyjscie == False:
    
            title = 'Wybierz rodzaj: '
            options = ['Ekran główny','Dodaj wpis','Auta','Wyjście']
            option1, index = pick(options, title)
            print(index)
            if index == 0:
                showStats(database, car_id)
                wybor = input("\nWciśnij 'Q', by wyjść, lub inny dowolny klawisz by przejść dalej...")
                clear()
                if wybor == "q" or wybor == "Q":
                    wyjscie = True
            elif index == 1:
                addStatDatabase(database,car_id)
                wybor = input("\nWciśnij 'Q', by wyjść, lub inny dowolny klawisz by przejść dalej...")
                clear()
                if wybor == "q" or wybor == "Q":
                    wyjscie = True
            elif index == 2:
                connection = sqlite3.connect(database)
                cursor = connection.cursor()
                select_query = """SELECT * from car"""
                cursor.execute(select_query)
                records = cursor.fetchall()
                if len(records) <= 0:
                    addAutoToDatabase(database)
                else:
                    showAuto(database)
                    dodaj_auto = input("\nDodać auto?: ")
                    if dodaj_auto == "t" or dodaj_auto == "T":
                        addAutoToDatabase(database)
                wybor = input("\nWciśnij 'Q', by wyjść, lub inny dowolny klawisz by przejść dalej...")
                clear()
                if wybor == "q" or wybor == "Q":
                    wyjscie = True
            elif index == 3:
                wyjscie = True
    
        ## po wszystkim
        try:
            if (connection):
                connection.close()
            clear()
        except:
            clear()
    

    Koniec pierwszej części

    Na tę chwilę tylko dodajemy wpisy i wyświetlamy je. Kolejnym krokiem będą obliczenia, które będą wykonywane na tych danych, a na koniec refaktoryzacja kodu.



    Robert Lewandowski
    Robert Lewandowski
    Problem Solver Developer