import csv
import sqlite3
from geslo import sifriraj_geslo

#Prebere datoteko in ustvari tabele
conn = sqlite3.connect('knjigarna.db')

with open('knjigarna.sql', encoding='utf-8') as f:
    sql_ukazi = f.read()
conn.executescript(sql_ukazi)


#Prebere dtoteko in ustavi podatke v tabele
with open('podatki.sql', encoding='utf-8') as f:
    sql_ukazi = f.read()
with conn:
    for sql_ukaz in sql_ukazi.splitlines():
        conn.execute(sql_ukaz)

class Tabela:
    """
    Razred, ki predstavlja tabelo v bazi.

    Polja razreda:
    - ime: ime tabele
    - podatki: datoteka s podatki ali None
    """
    ime = None
    podatki = None

    def __init__(self, conn):
        """
        Konstruktor razreda.
        """
        self.conn = conn

    def ustvari(self):
        """
        Metoda za ustvarjanje tabele.
        Podrazredi morajo povoziti to metodo.
        """
        raise NotImplementedError

    def izbrisi(self):
        """
        Metoda za brisanje tabele.
        """
        self.conn.execute("DROP TABLE IF EXISTS {};".format(self.ime))

    #Uvozi podatke iz csv datotek(vse podatke imava že v tabelah - ne potrebujeva te funkcije)
    def uvozi(self, encoding="UTF-8", **kwargs):
        """
        Metoda za uvoz podatkov.

        Argumenti:
        - encoding: kodiranje znakov
        - ostali poimenovani argumenti: za metodo dodaj_vrstico
        """
        if self.podatki is None:
            return
        with open(self.podatki, encoding=encoding) as datoteka:
            podatki = csv.reader(datoteka)
            stolpci = self.pretvori(next(podatki), kwargs)
            poizvedba = self.dodajanje(stolpci)
            for vrstica in podatki:
                vrstica = [None if x == "" else x for x in vrstica]
                self.dodaj_vrstico(vrstica, poizvedba, **kwargs)

    def izprazni(self):
        """
        Metodo za praznjenje tabele.
        """
        self.conn.execute("DELETE FROM {};".format(self.ime))

    @staticmethod
    def pretvori(stolpci, kwargs):
        """
        Prilagodi imena stolpcev
        in poskrbi za ustrezne argumente za dodaj_vrstico.

        Privzeto vrne podane stolpce.
        """
        return stolpci

    def dodajanje(self, stolpci=None, stevilo=None):
        """
        Metoda za gradnjo poizvedbe.

        Arugmenti uporabimo enega od njiju):
        - stolpci: seznam stolpcev
        - stevilo: število stolpcev
        """
        if stolpci is None:
            assert stevilo is not None
            st = ""
        else:
            st = " ({})".format(", ".join(stolpci))
            stevilo = len(stolpci)
        return "INSERT INTO {}{} VALUES ({})". \
            format(self.ime, st, ", ".join(["?"] * stevilo))

    def dodaj_vrstico(self, podatki, poizvedba=None, **kwargs):
        """
        Metoda za dodajanje vrstice.

        Argumenti:
        - podatki: seznam s podatki v vrstici
        - poizvedba: poizvedba, ki naj se zažene
        - poljubni poimenovani parametri: privzeto se ignorirajo
        """
        if poizvedba is None:
            poizvedba = self.dodajanje(stevilo=len(podatki))
        cur = self.conn.execute(poizvedba, podatki)
        return cur.lastrowid



class Uporabnik(Tabela):
    """
    Tabela za uporabnike.
    """
    ime = "uporabnik"
    podatki = "podatki/uporabnik.csv"

    def ustvari(self):
        """
        Ustvari tabelo uporabnik.
        """
        self.conn.execute("""
            CREATE TABLE uporabnik (
                id        INTEGER PRIMARY KEY AUTOINCREMENT,
                ime       TEXT NOT NULL UNIQUE,
                zgostitev TEXT NOT NULL,
                sol       TEXT NOT NULL
            )
        """)

    @staticmethod
    def pretvori(stolpci, kwargs):
        """
        Zapomni si indeksa stolpcev za zgostitev in sol.
        """
        kwargs["zgostitev"] = stolpci.index("zgostitev")
        kwargs["sol"] = stolpci.index("sol")
        return stolpci

    def dodaj_vrstico(self, podatki, poizvedba=None, zgostitev=None, sol=None):
        """
        Dodaj uporabnika.

        Če sol ni podana, zašifrira podano geslo.
        """
        if sol is not None and zgostitev is not None and podatki[sol] is None:
            podatki[zgostitev], podatki[sol] = sifriraj_geslo(podatki[zgostitev])
        return super().dodaj_vrstico(podatki, poizvedba)


def ustvari_tabele(tabele):
    """
    Ustvari podane tabele.
    """
    for t in tabele:
        t.ustvari()


def izbrisi_tabele(tabele):
    """
    Izbriši podane tabele.
    """
    for t in tabele:
        t.izbrisi()


def uvozi_podatke(tabele):
    """
    Uvozi podatke v podane tabele.
    """
    for t in tabele:
        t.uvozi()


def izprazni_tabele(tabele):
    """
    Izprazni podane tabele.
    """
    for t in tabele:
        t.izprazni()


def ustvari_bazo(conn):
    """
    Izvede ustvarjanje baze.
    """
    tabele = pripravi_tabele(conn)
    izbrisi_tabele(tabele)
    ustvari_tabele(tabele)
    uvozi_podatke(tabele)


def pripravi_tabele(conn):
    """
    Pripravi objekte za tabele.
    """
    uporabnik = Uporabnik(conn)
    
    return uporabnik


def ustvari_bazo_ce_ne_obstaja(conn):
    """
    Ustvari bazo, če ta še ne obstaja.
    """
    with conn:
        cur = conn.execute("SELECT COUNT(*) FROM sqlite_master")
        if cur.fetchone() == (0, ):
            ustvari_bazo(conn)

import csv
import sqlite3
from geslo import sifriraj_geslo

#Prebere datoteko in ustvari tabele
conn = sqlite3.connect('knjigarna.db')

with open('knjigarna.sql', encoding='utf-8') as f:
    sql_ukazi = f.read()
conn.executescript(sql_ukazi)


#Prebere dtoteko in ustavi podatke v tabele
with open('podatki.sql', encoding='utf-8') as f:
    sql_ukazi = f.read()
with conn:
    for sql_ukaz in sql_ukazi.splitlines():
        conn.execute(sql_ukaz)

class Tabela:
    """
    Razred, ki predstavlja tabelo v bazi.

    Polja razreda:
    - ime: ime tabele
    - podatki: datoteka s podatki ali None
    """
    ime = None
    podatki = None

    def __init__(self, conn):
        """
        Konstruktor razreda.
        """
        self.conn = conn

    def ustvari(self):
        """
        Metoda za ustvarjanje tabele.
        Podrazredi morajo povoziti to metodo.
        """
        raise NotImplementedError

    def izbrisi(self):
        """
        Metoda za brisanje tabele.
        """
        self.conn.execute("DROP TABLE IF EXISTS {};".format(self.ime))

    #Uvozi podatke iz csv datotek(vse podatke imava že v tabelah - ne potrebujeva te funkcije)
    def uvozi(self, encoding="UTF-8", **kwargs):
        """
        Metoda za uvoz podatkov.

        Argumenti:
        - encoding: kodiranje znakov
        - ostali poimenovani argumenti: za metodo dodaj_vrstico
        """
        if self.podatki is None:
            return
        with open(self.podatki, encoding=encoding) as datoteka:
            podatki = csv.reader(datoteka)
            stolpci = self.pretvori(next(podatki), kwargs)
            poizvedba = self.dodajanje(stolpci)
            for vrstica in podatki:
                vrstica = [None if x == "" else x for x in vrstica]
                self.dodaj_vrstico(vrstica, poizvedba, **kwargs)

    def izprazni(self):
        """
        Metodo za praznjenje tabele.
        """
        self.conn.execute("DELETE FROM {};".format(self.ime))

    @staticmethod
    def pretvori(stolpci, kwargs):
        """
        Prilagodi imena stolpcev
        in poskrbi za ustrezne argumente za dodaj_vrstico.

        Privzeto vrne podane stolpce.
        """
        return stolpci

    def dodajanje(self, stolpci=None, stevilo=None):
        """
        Metoda za gradnjo poizvedbe.

        Arugmenti uporabimo enega od njiju):
        - stolpci: seznam stolpcev
        - stevilo: število stolpcev
        """
        if stolpci is None:
            assert stevilo is not None
            st = ""
        else:
            st = " ({})".format(", ".join(stolpci))
            stevilo = len(stolpci)
        return "INSERT INTO {}{} VALUES ({})". \
            format(self.ime, st, ", ".join(["?"] * stevilo))

    def dodaj_vrstico(self, podatki, poizvedba=None, **kwargs):
        """
        Metoda za dodajanje vrstice.

        Argumenti:
        - podatki: seznam s podatki v vrstici
        - poizvedba: poizvedba, ki naj se zažene
        - poljubni poimenovani parametri: privzeto se ignorirajo
        """
        if poizvedba is None:
            poizvedba = self.dodajanje(stevilo=len(podatki))
        cur = self.conn.execute(poizvedba, podatki)
        return cur.lastrowid



class Uporabnik(Tabela):
    """
    Tabela za uporabnike.
    """
    ime = "uporabnik"
    podatki = "podatki/uporabnik.csv"

    def ustvari(self):
        """
        Ustvari tabelo uporabnik.
        """
        self.conn.execute("""
            CREATE TABLE uporabnik (
                id        INTEGER PRIMARY KEY AUTOINCREMENT,
                ime       TEXT NOT NULL UNIQUE,
                zgostitev TEXT NOT NULL,
                sol       TEXT NOT NULL
            )
        """)

    @staticmethod
    def pretvori(stolpci, kwargs):
        """
        Zapomni si indeksa stolpcev za zgostitev in sol.
        """
        kwargs["zgostitev"] = stolpci.index("zgostitev")
        kwargs["sol"] = stolpci.index("sol")
        return stolpci

    def dodaj_vrstico(self, podatki, poizvedba=None, zgostitev=None, sol=None):
        """
        Dodaj uporabnika.

        Če sol ni podana, zašifrira podano geslo.
        """
        if sol is not None and zgostitev is not None and podatki[sol] is None:
            podatki[zgostitev], podatki[sol] = sifriraj_geslo(podatki[zgostitev])
        return super().dodaj_vrstico(podatki, poizvedba)


def ustvari_tabele(tabele):
    """
    Ustvari podane tabele.
    """
    for t in tabele:
        t.ustvari()


def izbrisi_tabele(tabele):
    """
    Izbriši podane tabele.
    """
    for t in tabele:
        t.izbrisi()


def uvozi_podatke(tabele):
    """
    Uvozi podatke v podane tabele.
    """
    for t in tabele:
        t.uvozi()


def izprazni_tabele(tabele):
    """
    Izprazni podane tabele.
    """
    for t in tabele:
        t.izprazni()


def ustvari_bazo(conn):
    """
    Izvede ustvarjanje baze.
    """
    tabele = pripravi_tabele(conn)
    izbrisi_tabele(tabele)
    ustvari_tabele(tabele)
    uvozi_podatke(tabele)


def pripravi_tabele(conn):
    """
    Pripravi objekte za tabele.
    """
    uporabnik = Uporabnik(conn)
    
    return uporabnik


def ustvari_bazo_ce_ne_obstaja(conn):
    """
    Ustvari bazo, če ta še ne obstaja.
    """
    with conn:
        cur = conn.execute("SELECT COUNT(*) FROM sqlite_master")
        if cur.fetchone() == (0, ):
            ustvari_bazo(conn)

#Prebere datoteko in ustvari tabele #Prebere dtoteko in ustavi podatke v tabele Razred, ki predstavlja tabelo v bazi.      Polja razreda:     - ime: ime tabele     - podatki: datoteka s podatki ali None Konstruktor razreda. Metoda za ustvarjanje tabele.         Podrazredi morajo povoziti to metodo. Metoda za brisanje tabele. #Uvozi podatke iz csv datotek(vse podatke imava že v tabelah - ne potrebujeva te funkcije) Metoda za uvoz podatkov.          Argumenti:         - encoding: kodiranje znakov         - ostali poimenovani argumenti: za metodo dodaj_vrstico Metodo za praznjenje tabele. Prilagodi imena stolpcev         in poskrbi za ustrezne argumente za dodaj_vrstico.          Privzeto vrne podane stolpce. Metoda za gradnjo poizvedbe.          Arugmenti uporabimo enega od njiju):         - stolpci: seznam stolpcev         - stevilo: število stolpcev Metoda za dodajanje vrstice.          Argumenti:         - podatki: seznam s podatki v vrstici         - poizvedba: poizvedba, ki naj se zažene         - poljubni poimenovani parametri: privzeto se ignorirajo Tabela za uporabnike. Ustvari tabelo uporabnik. CREATE TABLE uporabnik (                 id        INTEGER PRIMARY KEY AUTOINCREMENT,                 ime       TEXT NOT NULL UNIQUE,                 zgostitev TEXT NOT NULL,                 sol       TEXT NOT NULL             ) Zapomni si indeksa stolpcev za zgostitev in sol. Dodaj uporabnika.          Če sol ni podana, zašifrira podano geslo. Ustvari podane tabele. Izbriši podane tabele. Uvozi podatke v podane tabele. Izprazni podane tabele. Izvede ustvarjanje baze. Pripravi objekte za tabele. Ustvari bazo, če ta še ne obstaja.