PyQt5-zip 5.1.15__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyqt5_zip-5.1.15/PKG-INFO +66 -0
- pyqt5_zip-5.1.15/PyQt5_zip/__init__.py +46 -0
- pyqt5_zip-5.1.15/PyQt5_zip/__main__.py +32 -0
- pyqt5_zip-5.1.15/PyQt5_zip/template/app/__init__.py +1 -0
- pyqt5_zip-5.1.15/PyQt5_zip/template/app/database.py +162 -0
- pyqt5_zip-5.1.15/PyQt5_zip/template/app/xlsx_reader.py +67 -0
- pyqt5_zip-5.1.15/PyQt5_zip/template/dump_database_sql.py +43 -0
- pyqt5_zip-5.1.15/PyQt5_zip/template/import/1.jpg +0 -0
- pyqt5_zip-5.1.15/PyQt5_zip/template/import/10.jpg +0 -0
- pyqt5_zip-5.1.15/PyQt5_zip/template/import/2.jpg +0 -0
- pyqt5_zip-5.1.15/PyQt5_zip/template/import/3.jpg +0 -0
- pyqt5_zip-5.1.15/PyQt5_zip/template/import/4.jpg +0 -0
- pyqt5_zip-5.1.15/PyQt5_zip/template/import/5.jpg +0 -0
- pyqt5_zip-5.1.15/PyQt5_zip/template/import/6.jpg +0 -0
- pyqt5_zip-5.1.15/PyQt5_zip/template/import/7.jpg +0 -0
- pyqt5_zip-5.1.15/PyQt5_zip/template/import/8.jpg +0 -0
- pyqt5_zip-5.1.15/PyQt5_zip/template/import/9.jpg +0 -0
- pyqt5_zip-5.1.15/PyQt5_zip/template/import/Icon.JPG +0 -0
- pyqt5_zip-5.1.15/PyQt5_zip/template/import/Icon.ico +0 -0
- pyqt5_zip-5.1.15/PyQt5_zip/template/import/Icon.png +0 -0
- pyqt5_zip-5.1.15/PyQt5_zip/template/import/Tovar.xlsx +0 -0
- pyqt5_zip-5.1.15/PyQt5_zip/template/import/picture.png +0 -0
- pyqt5_zip-5.1.15/PyQt5_zip/template/import/user_import.xlsx +0 -0
- pyqt5_zip-5.1.15/PyQt5_zip/template/import//320/227/320/260/320/272/320/260/320/267_import.xlsx +0 -0
- pyqt5_zip-5.1.15/PyQt5_zip/template/import//320/237/321/203/320/275/320/272/321/202/321/213 /320/262/321/213/320/264/320/260/321/207/320/270_import.xlsx +0 -0
- pyqt5_zip-5.1.15/PyQt5_zip/template/main.py +707 -0
- pyqt5_zip-5.1.15/PyQt5_zip/template/ui/login.ui +48 -0
- pyqt5_zip-5.1.15/PyQt5_zip/template/ui/main_window.ui +62 -0
- pyqt5_zip-5.1.15/PyQt5_zip/template/ui/order_dialog.ui +33 -0
- pyqt5_zip-5.1.15/PyQt5_zip/template/ui/product_dialog.ui +39 -0
- pyqt5_zip-5.1.15/PyQt5_zip/template/ui/purchase_dialog.ui +31 -0
- pyqt5_zip-5.1.15/PyQt5_zip.egg-info/PKG-INFO +66 -0
- pyqt5_zip-5.1.15/PyQt5_zip.egg-info/SOURCES.txt +38 -0
- pyqt5_zip-5.1.15/PyQt5_zip.egg-info/dependency_links.txt +1 -0
- pyqt5_zip-5.1.15/PyQt5_zip.egg-info/entry_points.txt +2 -0
- pyqt5_zip-5.1.15/PyQt5_zip.egg-info/requires.txt +1 -0
- pyqt5_zip-5.1.15/PyQt5_zip.egg-info/top_level.txt +1 -0
- pyqt5_zip-5.1.15/README.md +59 -0
- pyqt5_zip-5.1.15/pyproject.toml +28 -0
- pyqt5_zip-5.1.15/setup.cfg +4 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: PyQt5_zip
|
|
3
|
+
Version: 5.1.15
|
|
4
|
+
Requires-Python: >=3.10
|
|
5
|
+
Description-Content-Type: text/markdown
|
|
6
|
+
Requires-Dist: PyQt5>=5.15
|
|
7
|
+
|
|
8
|
+
# ООО «Обувь»
|
|
9
|
+
|
|
10
|
+
Приложение для модулей 1 и 2: Python, PyQt5 UI-формы (`.ui`) и база SQLite.
|
|
11
|
+
|
|
12
|
+
## Запуск
|
|
13
|
+
|
|
14
|
+
```powershell
|
|
15
|
+
python -m pip install -r requirements.txt
|
|
16
|
+
python main.py
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
При первом запуске создаётся `shoe_shop.sqlite`, данные импортируются из папки `import`.
|
|
20
|
+
|
|
21
|
+
## Роли
|
|
22
|
+
|
|
23
|
+
- Гость: просмотр товаров без поиска, фильтрации и сортировки.
|
|
24
|
+
- Авторизированный клиент: просмотр товаров без поиска, фильтрации и сортировки.
|
|
25
|
+
- Менеджер: просмотр товаров с поиском, фильтрацией и сортировкой, просмотр заказов.
|
|
26
|
+
- Администратор: просмотр, добавление, редактирование и удаление товаров и заказов.
|
|
27
|
+
|
|
28
|
+
## Примеры входа
|
|
29
|
+
|
|
30
|
+
- Администратор: `94d5ous@gmail.com` / `uzWC67`
|
|
31
|
+
- Менеджер: `1diph5e@tutanota.com` / `8ntwUp`
|
|
32
|
+
- Клиент: `5d4zbu@tutanota.com` / `rwVDh9`
|
|
33
|
+
|
|
34
|
+
## Использование как библиотеку
|
|
35
|
+
|
|
36
|
+
Установить текущий проект как локальную библиотеку:
|
|
37
|
+
|
|
38
|
+
```powershell
|
|
39
|
+
python -m pip install -e .
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Создать копию приложения в текущей папке:
|
|
43
|
+
|
|
44
|
+
```powershell
|
|
45
|
+
PyQt5_zip
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Создать копию приложения в конкретной папке:
|
|
49
|
+
|
|
50
|
+
```powershell
|
|
51
|
+
PyQt5_zip D:\NewShoeApp
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
То же самое из Python:
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
from PyQt5_zip import create_project
|
|
58
|
+
|
|
59
|
+
create_project(".")
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Если файлы уже существуют и их нужно заменить:
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
create_project(".", overwrite=True)
|
|
66
|
+
```
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import shutil
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
__all__ = ["create_project"]
|
|
8
|
+
__version__ = "5.1.15"
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
TEMPLATE_DIR = Path(__file__).resolve().parent / "template"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def create_project(target_dir: str | Path = ".", overwrite: bool = False) -> list[Path]:
|
|
15
|
+
"""Create the shoe shop PyQt/SQLite project in target_dir.
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
target_dir: Folder where project files should be created.
|
|
19
|
+
overwrite: Replace existing files when True.
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
Paths that were created or replaced.
|
|
23
|
+
"""
|
|
24
|
+
target = Path(target_dir).resolve()
|
|
25
|
+
target.mkdir(parents=True, exist_ok=True)
|
|
26
|
+
|
|
27
|
+
created: list[Path] = []
|
|
28
|
+
for source in TEMPLATE_DIR.rglob("*"):
|
|
29
|
+
relative = source.relative_to(TEMPLATE_DIR)
|
|
30
|
+
destination = target / relative
|
|
31
|
+
|
|
32
|
+
if source.is_dir():
|
|
33
|
+
destination.mkdir(parents=True, exist_ok=True)
|
|
34
|
+
continue
|
|
35
|
+
|
|
36
|
+
if destination.exists() and not overwrite:
|
|
37
|
+
raise FileExistsError(
|
|
38
|
+
f"File already exists: {destination}. "
|
|
39
|
+
"Pass overwrite=True to replace existing files."
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
destination.parent.mkdir(parents=True, exist_ok=True)
|
|
43
|
+
shutil.copy2(source, destination)
|
|
44
|
+
created.append(destination)
|
|
45
|
+
|
|
46
|
+
return created
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
|
|
5
|
+
from . import create_project
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def main() -> int:
|
|
9
|
+
parser = argparse.ArgumentParser(
|
|
10
|
+
prog="PyQt5_zip",
|
|
11
|
+
description="Create the PyQt/SQLite shoe shop project in a target folder.",
|
|
12
|
+
)
|
|
13
|
+
parser.add_argument(
|
|
14
|
+
"target",
|
|
15
|
+
nargs="?",
|
|
16
|
+
default=".",
|
|
17
|
+
help="Target folder. Defaults to the current directory.",
|
|
18
|
+
)
|
|
19
|
+
parser.add_argument(
|
|
20
|
+
"--overwrite",
|
|
21
|
+
action="store_true",
|
|
22
|
+
help="Replace existing files in the target folder.",
|
|
23
|
+
)
|
|
24
|
+
args = parser.parse_args()
|
|
25
|
+
|
|
26
|
+
files = create_project(args.target, overwrite=args.overwrite)
|
|
27
|
+
print(f"Created {len(files)} files in {args.target}")
|
|
28
|
+
return 0
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
if __name__ == "__main__":
|
|
32
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import sqlite3
|
|
4
|
+
from datetime import datetime, timedelta
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
from .xlsx_reader import read_first_sheet
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
ROOT = Path(__file__).resolve().parents[1]
|
|
11
|
+
DB_PATH = ROOT / "shoe_shop.sqlite"
|
|
12
|
+
IMPORT_DIR = ROOT / "import"
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def connect() -> sqlite3.Connection:
|
|
16
|
+
conn = sqlite3.connect(DB_PATH)
|
|
17
|
+
conn.row_factory = sqlite3.Row
|
|
18
|
+
conn.execute("PRAGMA foreign_keys = ON")
|
|
19
|
+
return conn
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def initialize_database() -> None:
|
|
23
|
+
with connect() as conn:
|
|
24
|
+
_ensure_schema(conn)
|
|
25
|
+
if not _has_data(conn, "users"):
|
|
26
|
+
_seed_users(conn)
|
|
27
|
+
if not _has_data(conn, "products"):
|
|
28
|
+
_seed_products(conn)
|
|
29
|
+
if not _has_data(conn, "pickup_points"):
|
|
30
|
+
_seed_pickup_points(conn)
|
|
31
|
+
if not _has_data(conn, "orders"):
|
|
32
|
+
_seed_orders(conn)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _ensure_schema(conn: sqlite3.Connection) -> None:
|
|
36
|
+
conn.executescript(
|
|
37
|
+
"""
|
|
38
|
+
CREATE TABLE IF NOT EXISTS users (
|
|
39
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
40
|
+
role TEXT NOT NULL,
|
|
41
|
+
full_name TEXT NOT NULL,
|
|
42
|
+
login TEXT NOT NULL UNIQUE,
|
|
43
|
+
password TEXT NOT NULL
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
CREATE TABLE IF NOT EXISTS products (
|
|
47
|
+
article TEXT PRIMARY KEY,
|
|
48
|
+
name TEXT NOT NULL,
|
|
49
|
+
unit TEXT NOT NULL,
|
|
50
|
+
price REAL NOT NULL,
|
|
51
|
+
supplier TEXT NOT NULL,
|
|
52
|
+
manufacturer TEXT NOT NULL,
|
|
53
|
+
category TEXT NOT NULL,
|
|
54
|
+
discount INTEGER NOT NULL DEFAULT 0,
|
|
55
|
+
stock INTEGER NOT NULL DEFAULT 0,
|
|
56
|
+
description TEXT,
|
|
57
|
+
photo TEXT
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
CREATE TABLE IF NOT EXISTS pickup_points (
|
|
61
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
62
|
+
address TEXT NOT NULL
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
CREATE TABLE IF NOT EXISTS orders (
|
|
66
|
+
id INTEGER PRIMARY KEY,
|
|
67
|
+
articles TEXT NOT NULL,
|
|
68
|
+
order_date TEXT NOT NULL,
|
|
69
|
+
delivery_date TEXT NOT NULL,
|
|
70
|
+
pickup_point_id INTEGER,
|
|
71
|
+
client_name TEXT NOT NULL,
|
|
72
|
+
receive_code TEXT NOT NULL,
|
|
73
|
+
status TEXT NOT NULL,
|
|
74
|
+
FOREIGN KEY (pickup_point_id) REFERENCES pickup_points(id)
|
|
75
|
+
);
|
|
76
|
+
"""
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _has_data(conn: sqlite3.Connection, table: str) -> bool:
|
|
81
|
+
return conn.execute(f"SELECT EXISTS(SELECT 1 FROM {table})").fetchone()[0] == 1
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _seed_users(conn: sqlite3.Connection) -> None:
|
|
85
|
+
rows = read_first_sheet(IMPORT_DIR / "user_import.xlsx")[1:]
|
|
86
|
+
conn.executemany(
|
|
87
|
+
"INSERT INTO users(role, full_name, login, password) VALUES (?, ?, ?, ?)",
|
|
88
|
+
[tuple(row[:4]) for row in rows if len(row) >= 4],
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def _seed_products(conn: sqlite3.Connection) -> None:
|
|
93
|
+
rows = read_first_sheet(IMPORT_DIR / "Tovar.xlsx")[1:]
|
|
94
|
+
conn.executemany(
|
|
95
|
+
"""
|
|
96
|
+
INSERT INTO products(article, name, unit, price, supplier, manufacturer,
|
|
97
|
+
category, discount, stock, description, photo)
|
|
98
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
99
|
+
""",
|
|
100
|
+
[
|
|
101
|
+
(
|
|
102
|
+
row[0],
|
|
103
|
+
row[1],
|
|
104
|
+
row[2],
|
|
105
|
+
float(row[3] or 0),
|
|
106
|
+
row[4],
|
|
107
|
+
row[5],
|
|
108
|
+
row[6],
|
|
109
|
+
int(float(row[7] or 0)),
|
|
110
|
+
int(float(row[8] or 0)),
|
|
111
|
+
row[9],
|
|
112
|
+
row[10] if len(row) > 10 else "",
|
|
113
|
+
)
|
|
114
|
+
for row in rows
|
|
115
|
+
if len(row) >= 10 and row[0]
|
|
116
|
+
],
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def _seed_pickup_points(conn: sqlite3.Connection) -> None:
|
|
121
|
+
rows = read_first_sheet(IMPORT_DIR / "Пункты выдачи_import.xlsx")
|
|
122
|
+
conn.executemany(
|
|
123
|
+
"INSERT INTO pickup_points(address) VALUES (?)",
|
|
124
|
+
[(row[0],) for row in rows if row and row[0]],
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _seed_orders(conn: sqlite3.Connection) -> None:
|
|
129
|
+
rows = read_first_sheet(IMPORT_DIR / "Заказ_import.xlsx")[1:]
|
|
130
|
+
conn.executemany(
|
|
131
|
+
"""
|
|
132
|
+
INSERT INTO orders(id, articles, order_date, delivery_date, pickup_point_id,
|
|
133
|
+
client_name, receive_code, status)
|
|
134
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
135
|
+
""",
|
|
136
|
+
[
|
|
137
|
+
(
|
|
138
|
+
int(float(row[0])),
|
|
139
|
+
row[1],
|
|
140
|
+
_normalize_date(row[2]),
|
|
141
|
+
_normalize_date(row[3]),
|
|
142
|
+
int(float(row[4])) if row[4] else None,
|
|
143
|
+
row[5],
|
|
144
|
+
row[6],
|
|
145
|
+
row[7],
|
|
146
|
+
)
|
|
147
|
+
for row in rows
|
|
148
|
+
if len(row) >= 8 and row[0]
|
|
149
|
+
],
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def _normalize_date(value: str) -> str:
|
|
154
|
+
value = str(value).strip()
|
|
155
|
+
if not value:
|
|
156
|
+
return ""
|
|
157
|
+
try:
|
|
158
|
+
serial = float(value)
|
|
159
|
+
except ValueError:
|
|
160
|
+
return value
|
|
161
|
+
date = datetime(1899, 12, 30) + timedelta(days=serial)
|
|
162
|
+
return date.strftime("%d.%m.%Y")
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import zipfile
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from xml.etree import ElementTree as ET
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
NS = {
|
|
9
|
+
"a": "http://schemas.openxmlformats.org/spreadsheetml/2006/main",
|
|
10
|
+
"r": "http://schemas.openxmlformats.org/officeDocument/2006/relationships",
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def read_first_sheet(path: str | Path) -> list[list[str]]:
|
|
15
|
+
"""Read simple xlsx files without an external dependency."""
|
|
16
|
+
with zipfile.ZipFile(path) as archive:
|
|
17
|
+
shared_strings = _read_shared_strings(archive)
|
|
18
|
+
workbook = ET.fromstring(archive.read("xl/workbook.xml"))
|
|
19
|
+
rels = ET.fromstring(archive.read("xl/_rels/workbook.xml.rels"))
|
|
20
|
+
relmap = {rel.attrib["Id"]: rel.attrib["Target"] for rel in rels}
|
|
21
|
+
sheet = workbook.find(".//a:sheet", NS)
|
|
22
|
+
if sheet is None:
|
|
23
|
+
return []
|
|
24
|
+
rel_id = sheet.attrib[f"{{{NS['r']}}}id"]
|
|
25
|
+
target = "xl/" + relmap[rel_id]
|
|
26
|
+
root = ET.fromstring(archive.read(target))
|
|
27
|
+
|
|
28
|
+
rows: list[list[str]] = []
|
|
29
|
+
for row in root.findall(".//a:sheetData/a:row", NS):
|
|
30
|
+
cells = []
|
|
31
|
+
last_col = 0
|
|
32
|
+
for cell in row.findall("a:c", NS):
|
|
33
|
+
col = _column_index(cell.attrib.get("r", "A1"))
|
|
34
|
+
while last_col < col - 1:
|
|
35
|
+
cells.append("")
|
|
36
|
+
last_col += 1
|
|
37
|
+
value = _cell_value(cell, shared_strings)
|
|
38
|
+
cells.append(value)
|
|
39
|
+
last_col = col
|
|
40
|
+
rows.append(cells)
|
|
41
|
+
return rows
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _read_shared_strings(archive: zipfile.ZipFile) -> list[str]:
|
|
45
|
+
if "xl/sharedStrings.xml" not in archive.namelist():
|
|
46
|
+
return []
|
|
47
|
+
root = ET.fromstring(archive.read("xl/sharedStrings.xml"))
|
|
48
|
+
values = []
|
|
49
|
+
for item in root.findall("a:si", NS):
|
|
50
|
+
values.append("".join(text.text or "" for text in item.findall(".//a:t", NS)))
|
|
51
|
+
return values
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _cell_value(cell: ET.Element, shared_strings: list[str]) -> str:
|
|
55
|
+
value_node = cell.find("a:v", NS)
|
|
56
|
+
value = "" if value_node is None else value_node.text or ""
|
|
57
|
+
if cell.attrib.get("t") == "s" and value:
|
|
58
|
+
return shared_strings[int(value)]
|
|
59
|
+
return value
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _column_index(ref: str) -> int:
|
|
63
|
+
letters = "".join(ch for ch in ref if ch.isalpha()).upper()
|
|
64
|
+
result = 0
|
|
65
|
+
for ch in letters:
|
|
66
|
+
result = result * 26 + ord(ch) - ord("A") + 1
|
|
67
|
+
return result or 1
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from app.database import DB_PATH, initialize_database
|
|
7
|
+
import sqlite3
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
DEFAULT_OUTPUT = Path("database_full_dump.sql")
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def dump_database(output_path: str | Path = DEFAULT_OUTPUT) -> Path:
|
|
14
|
+
"""Export the current SQLite database schema and data to an SQL file."""
|
|
15
|
+
initialize_database()
|
|
16
|
+
|
|
17
|
+
output = Path(output_path).resolve()
|
|
18
|
+
with sqlite3.connect(DB_PATH) as conn:
|
|
19
|
+
sql_lines = list(conn.iterdump())
|
|
20
|
+
|
|
21
|
+
output.write_text("\n".join(sql_lines) + "\n", encoding="utf-8")
|
|
22
|
+
return output
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def main() -> int:
|
|
26
|
+
parser = argparse.ArgumentParser(
|
|
27
|
+
description="Export SQLite schema and INSERT data statements to an SQL file."
|
|
28
|
+
)
|
|
29
|
+
parser.add_argument(
|
|
30
|
+
"output",
|
|
31
|
+
nargs="?",
|
|
32
|
+
default=str(DEFAULT_OUTPUT),
|
|
33
|
+
help="Output SQL file. Defaults to database_full_dump.sql.",
|
|
34
|
+
)
|
|
35
|
+
args = parser.parse_args()
|
|
36
|
+
|
|
37
|
+
output = dump_database(args.output)
|
|
38
|
+
print(f"Database SQL dump created: {output}")
|
|
39
|
+
return 0
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
if __name__ == "__main__":
|
|
43
|
+
raise SystemExit(main())
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|