13 Commits

9 changed files with 112 additions and 28 deletions

View File

@@ -1,5 +1,5 @@
Package: brovski-adressetiketten
Version: 0.6a
Version: 0.1.0b
Maintainer: Ovski
Architecture: all
Description: manage csv files for glables address labels

View File

@@ -5,11 +5,10 @@ import tkinter as tk
from configparser import NoSectionError, NoOptionError
from tkinter import messagebox
from tkinter import ttk
import json
from config import Config
from connector import JSONConnector
from windows import SettingsWindow, EditRecord, Window, show_error
from model import Model
from windows import SettingsWindow, EditRecord, show_error
class Application:
@@ -19,7 +18,7 @@ class Application:
y_offset = 200
width = 1050
height = 700
VERSION = '0.6a'
VERSION = '0.1.0b'
title = f"Brovski Adress-Etiketten Verwaltung {VERSION}"
self.root = tk.Tk(className="BrovskiAdressEtiketten")
@@ -37,7 +36,7 @@ class Application:
self.filter_active = tk.BooleanVar(value=False)
# model connector
self.model = JSONConnector()
self.model = Model(self.config)
# init paths to json and csv file
self.json_file_name = "brovski-adress-etiketten-verwaltung.json"
@@ -93,8 +92,10 @@ class Application:
scrollbar.pack(side=tk.LEFT, fill=tk.Y)
self.table.bind("<ButtonRelease-1>", self.mouse_click)
self.table.bind("<Return>", self.mouse_click_double)
self.table.bind("<Return>", self.enter_button)
self.table.bind("<Double-1>", self.mouse_click_double)
self.root.bind("<Up>", self.focus_table)
self.root.bind("<Down>", self.focus_table)
# bottom status bar
tk.Label(bottom_frame, textvariable=self.statusbar).pack(side=tk.LEFT)
@@ -202,7 +203,10 @@ class Application:
region = self.table.identify("region", event.x, event.y)
match region:
case "cell":
self.click_on_cell()
self.edit_selected_record()
def enter_button(self, event):
self.edit_selected_record()
def click_on_header(self, event):
column = self.table.identify_column(event.x)
@@ -229,7 +233,9 @@ class Application:
self.address_list = self.model.get_all_sorted_by(field, self.sort_order)
self.populate_table(reload=False)
def click_on_cell(self):
def edit_selected_record(self):
if self.table.focus() is None or self.table.focus() == "":
return
self.current_record = int(self.table.focus())
self.open_window_edit_records(self.current_record)
@@ -311,6 +317,16 @@ class Application:
self.address_list.sort(key=lambda x: (x["firma"], x["name"]))
self.populate_table()
def focus_table(self, event):
first = self.table.get_children()[0]
last = self.table.get_children()[-1]
goto = last if event.keysym == "Up" else first
if self.table.focus() == "":
self.table.selection_set(goto)
self.table.focus(goto)
self.table.see(goto)
self.table.focus_force()
if __name__ == '__main__':
Application()

View File

@@ -1,11 +1,12 @@
import configparser
import os
from configparser import ConfigParser
from configparser import ConfigParser, DuplicateSectionError
class Config:
parser: ConfigParser
def __init__(self):
def __init__(self, path: str = None, filename: str = "config.ini"):
"""
Config parser reading config.ini
@@ -14,12 +15,18 @@ class Config:
self.__filename: Path and name to the config file
"""
self.parser = ConfigParser()
self.path = path
self.filename = filename
if self.path is None:
home_path = os.environ["HOME"]
full_path = os.path.join(home_path, ".config", "brovski-adress-etiketten" )
else:
full_path = self.path
if not os.path.exists(full_path):
os.makedirs(full_path)
self.config_file = os.path.join(full_path, "config.ini")
self.config_file = os.path.join(full_path, self.filename)
self._load()
@@ -32,11 +39,15 @@ class Config:
def add_section(self, section):
self._load()
try:
self.parser.add_section(section)
except DuplicateSectionError:
pass
self._save()
def set(self, section: str, option: str, value: str):
self._load()
self.add_section(section)
self.parser.set(section, option, value)
self._save()

View File

@@ -1,13 +1,12 @@
import json
import os
from abc import ABC, abstractmethod
import config
from config import Config
class Connector(ABC):
def __init__(self):
pass
def __init__(self, config: Config):
self.config = config
@abstractmethod
def get_all(self) -> list:
@@ -29,13 +28,16 @@ class Connector(ABC):
def create_new(self, values: dict) -> int:
pass
@abstractmethod
def get_all_sorted_by(self, field: str, reverse: bool = False) -> list:
pass
class JSONConnector(Connector):
def __init__(self):
super().__init__()
self.config = config.Config()
def __init__(self, config: Config, ):
super().__init__(config)
self.json_path = self.config.get("json", "path")
self.json_file = os.path.join(self.json_path, "brovski-adress-etiketten-verwaltung.json")
self.json_file = os.path.join(self.json_path, "brovski-adress-etiketten-verwaltung-v7.json")
def get_all(self) -> list:
with open(self.json_file, "r") as f:
@@ -78,5 +80,5 @@ class JSONConnector(Connector):
return next_id
def _write_to_file(self, data):
with open(self.json_file, "w") as f:
json.dump(data, f, indent=4)
with open(self.json_file, "w", encoding="UTF-8") as f:
json.dump(data, f, indent=4, ensure_ascii=False)

24
src/model.py Normal file
View File

@@ -0,0 +1,24 @@
from connector import Connector, JSONConnector
from config import Config
class Model:
def __init__(self, config: Config):
self.connector = JSONConnector(config)
def get_all(self):
return self.connector.get_all()
def get_all_sorted_by(self, field: str, reverse: bool = False):
return self.connector.get_all_sorted_by(field=field, reverse=reverse)
def get_by_id(self, record_id: int):
return self.connector.get_by_id(record_id)
def delete_by_id(self, record_id: int):
self.connector.delete_by_id(record_id)
def update_record(self, new_record: dict):
self.connector.update_record(new_record)
def create_new(self, record: dict):
self.connector.create_new(record)

View File

@@ -3,7 +3,7 @@ from configparser import NoSectionError, NoOptionError
from tkinter import font, filedialog, messagebox
from config import Config
from connector import JSONConnector
from model import Model
def show_error(message_title: str, message: str, parent: tk.Tk | tk.Toplevel):
@@ -18,6 +18,7 @@ class Window(tk.Toplevel):
def __init__(self, parent, root: tk.Tk):
super().__init__(root)
self.parent = parent
self.config = parent.config
self.root = root
self.protocol("WM_DELETE_WINDOW", self.close_window)
self.bind("<Escape>", self.close_window)
@@ -36,7 +37,7 @@ class EditRecord(Window):
super().__init__(parent, root)
self.bind("<Return>", self._update)
self.model = JSONConnector()
self.model = Model(self.config)
record = self.model.get_by_id(record_id)

0
tests/__init__.py Normal file
View File

30
tests/test_config.py Normal file
View File

@@ -0,0 +1,30 @@
import os
from typing import assert_type
import pytest
from src.config import Config
@pytest.fixture
def config() -> Config:
config = Config(path="testfiles", filename="configtest.ini")
return config
def teardown_config():
print("tearing down config test")
def test_construction(config):
assert_type(config, Config)
def test_file_creation(config):
config._save()
assert os.path.isfile(os.path.join(config.path, config.filename))
def test_add_section(config):
config.add_section("test_section")
assert "test_section" in config.parser.sections()
def test_set_and_get(config):
config.set(section="section", option="option", value="value")
assert config.get(section="section", option="option") == "value"

View File

@@ -1 +1 @@
0.6a
0.1.0b