eurekiusz 0.4.0__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.
- eurekiusz-0.4.0/PKG-INFO +49 -0
- eurekiusz-0.4.0/README.md +31 -0
- eurekiusz-0.4.0/pyproject.toml +39 -0
- eurekiusz-0.4.0/setup.cfg +4 -0
- eurekiusz-0.4.0/src/eurekiusz/__init__.py +3 -0
- eurekiusz-0.4.0/src/eurekiusz/cli.py +167 -0
- eurekiusz-0.4.0/src/eurekiusz/data/chunks.db +0 -0
- eurekiusz-0.4.0/src/eurekiusz/eureka.py +97 -0
- eurekiusz-0.4.0/src/eurekiusz/gui.py +44 -0
- eurekiusz-0.4.0/src/eurekiusz/law.py +308 -0
- eurekiusz-0.4.0/src/eurekiusz/paths.py +39 -0
- eurekiusz-0.4.0/src/eurekiusz/present.py +131 -0
- eurekiusz-0.4.0/src/eurekiusz/server.py +99 -0
- eurekiusz-0.4.0/src/eurekiusz/static/index.html +374 -0
- eurekiusz-0.4.0/src/eurekiusz.egg-info/PKG-INFO +49 -0
- eurekiusz-0.4.0/src/eurekiusz.egg-info/SOURCES.txt +18 -0
- eurekiusz-0.4.0/src/eurekiusz.egg-info/dependency_links.txt +1 -0
- eurekiusz-0.4.0/src/eurekiusz.egg-info/entry_points.txt +5 -0
- eurekiusz-0.4.0/src/eurekiusz.egg-info/requires.txt +3 -0
- eurekiusz-0.4.0/src/eurekiusz.egg-info/top_level.txt +1 -0
eurekiusz-0.4.0/PKG-INFO
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: eurekiusz
|
|
3
|
+
Version: 0.4.0
|
|
4
|
+
Summary: Lokalny asystent ustaw podatkowych: ustawa → nowela → KIS. Bez zgadywania kwot.
|
|
5
|
+
Author-email: QA10 <hello@qa10.io>
|
|
6
|
+
License: Proprietary
|
|
7
|
+
Project-URL: Homepage, https://qa10.io
|
|
8
|
+
Keywords: vat,podatki,eli,kis,eureka
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Environment :: Console
|
|
11
|
+
Classifier: Environment :: Win32 (MS Windows)
|
|
12
|
+
Classifier: Intended Audience :: Legal Industry
|
|
13
|
+
Classifier: Topic :: Office/Business :: Financial
|
|
14
|
+
Requires-Python: >=3.10
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
Provides-Extra: gui
|
|
17
|
+
Requires-Dist: pywebview>=5; extra == "gui"
|
|
18
|
+
|
|
19
|
+
# Eurekiusz
|
|
20
|
+
|
|
21
|
+
Lokalny asystent ustaw podatkowych. Trzy warstwy: ustawa → nowela → KIS.
|
|
22
|
+
Model nie zgaduje kwot. Pusty kontekst = `Nie znalazlem w bazie.`
|
|
23
|
+
|
|
24
|
+
## Instalacja
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
python -m pip install -U eurekiusz
|
|
28
|
+
eurekiusz gui
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Windows: `winget install Python.Python.3.12`, nowy PowerShell, potem powyższe.
|
|
32
|
+
Okno natywne: `python -m pip install pywebview` (WebView2). Bez tego workbench wstaje na 127.0.0.1.
|
|
33
|
+
|
|
34
|
+
## Użycie
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
eurekiusz ask "jaki jest limit zwolnienia VAT 2026"
|
|
38
|
+
eurekiusz lookup DU/2025/775 113
|
|
39
|
+
eurekiusz health
|
|
40
|
+
eurekiusz gui
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Bramka: art. 113 TJ DU/2025/775 = 200 000 zł. Limit 240 000 zł to nowela DU/2025/896 od 1.01.2026.
|
|
44
|
+
|
|
45
|
+
## Czego tu nie ma
|
|
46
|
+
|
|
47
|
+
- modelu LLM / GGUF
|
|
48
|
+
- `eurekiusz.exe` z tego builda (Wails tylko na Windows)
|
|
49
|
+
- Qdrant, 8 botów, treści Optima `prawo_rachunkowe`
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Eurekiusz
|
|
2
|
+
|
|
3
|
+
Lokalny asystent ustaw podatkowych. Trzy warstwy: ustawa → nowela → KIS.
|
|
4
|
+
Model nie zgaduje kwot. Pusty kontekst = `Nie znalazlem w bazie.`
|
|
5
|
+
|
|
6
|
+
## Instalacja
|
|
7
|
+
|
|
8
|
+
```
|
|
9
|
+
python -m pip install -U eurekiusz
|
|
10
|
+
eurekiusz gui
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Windows: `winget install Python.Python.3.12`, nowy PowerShell, potem powyższe.
|
|
14
|
+
Okno natywne: `python -m pip install pywebview` (WebView2). Bez tego workbench wstaje na 127.0.0.1.
|
|
15
|
+
|
|
16
|
+
## Użycie
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
eurekiusz ask "jaki jest limit zwolnienia VAT 2026"
|
|
20
|
+
eurekiusz lookup DU/2025/775 113
|
|
21
|
+
eurekiusz health
|
|
22
|
+
eurekiusz gui
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Bramka: art. 113 TJ DU/2025/775 = 200 000 zł. Limit 240 000 zł to nowela DU/2025/896 od 1.01.2026.
|
|
26
|
+
|
|
27
|
+
## Czego tu nie ma
|
|
28
|
+
|
|
29
|
+
- modelu LLM / GGUF
|
|
30
|
+
- `eurekiusz.exe` z tego builda (Wails tylko na Windows)
|
|
31
|
+
- Qdrant, 8 botów, treści Optima `prawo_rachunkowe`
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "eurekiusz"
|
|
7
|
+
version = "0.4.0"
|
|
8
|
+
description = "Lokalny asystent ustaw podatkowych: ustawa → nowela → KIS. Bez zgadywania kwot."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
authors = [{ name = "QA10", email = "hello@qa10.io" }]
|
|
12
|
+
license = { text = "Proprietary" }
|
|
13
|
+
keywords = ["vat", "podatki", "eli", "kis", "eureka"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Programming Language :: Python :: 3",
|
|
16
|
+
"Environment :: Console",
|
|
17
|
+
"Environment :: Win32 (MS Windows)",
|
|
18
|
+
"Intended Audience :: Legal Industry",
|
|
19
|
+
"Topic :: Office/Business :: Financial",
|
|
20
|
+
]
|
|
21
|
+
dependencies = []
|
|
22
|
+
|
|
23
|
+
[project.optional-dependencies]
|
|
24
|
+
gui = ["pywebview>=5"]
|
|
25
|
+
|
|
26
|
+
[project.urls]
|
|
27
|
+
Homepage = "https://qa10.io"
|
|
28
|
+
|
|
29
|
+
[project.scripts]
|
|
30
|
+
eurekiusz = "eurekiusz.cli:main"
|
|
31
|
+
|
|
32
|
+
[project.gui-scripts]
|
|
33
|
+
eurekiusz-gui = "eurekiusz.gui:main"
|
|
34
|
+
|
|
35
|
+
[tool.setuptools.packages.find]
|
|
36
|
+
where = ["src"]
|
|
37
|
+
|
|
38
|
+
[tool.setuptools.package-data]
|
|
39
|
+
eurekiusz = ["data/chunks.db", "static/index.html"]
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
"""CLI: eurekiusz ask|lookup|search|health|gui"""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import argparse
|
|
5
|
+
import json
|
|
6
|
+
import sys
|
|
7
|
+
|
|
8
|
+
from eurekiusz import __version__
|
|
9
|
+
from eurekiusz.law import ask, health, lookup, search
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _clip(text: str, n: int = 900) -> str:
|
|
13
|
+
text = (text or "").strip()
|
|
14
|
+
if len(text) > n:
|
|
15
|
+
return text[:n] + "…"
|
|
16
|
+
return text
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _hit_lines(hit: dict) -> list[str]:
|
|
20
|
+
if not hit:
|
|
21
|
+
return []
|
|
22
|
+
inner = hit.get("article") or hit
|
|
23
|
+
eli = inner.get("eli") or hit.get("eli") or ""
|
|
24
|
+
art = inner.get("art") or hit.get("art") or ""
|
|
25
|
+
text = inner.get("text") or hit.get("text") or hit.get("head") or ""
|
|
26
|
+
lines = [f"{eli} art. {art}".strip()]
|
|
27
|
+
if text:
|
|
28
|
+
lines.append(_clip(text))
|
|
29
|
+
return lines
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def format_ask(out: dict) -> str:
|
|
33
|
+
if not out.get("ok"):
|
|
34
|
+
return (out.get("reason") or "Nie znalazlem w bazie.") + "\n"
|
|
35
|
+
lines: list[str] = []
|
|
36
|
+
mode = out.get("mode")
|
|
37
|
+
if mode == "dual_cite":
|
|
38
|
+
if out.get("ruling"):
|
|
39
|
+
lines.append(out["ruling"])
|
|
40
|
+
lines.append("")
|
|
41
|
+
lines.extend(_hit_lines(out.get("tj") or {}))
|
|
42
|
+
lines.append("")
|
|
43
|
+
nowela = out.get("nowela") or {}
|
|
44
|
+
if nowela.get("ok"):
|
|
45
|
+
lines.append("Nowela DU/2025/896 (wejście 1.01.2026):")
|
|
46
|
+
lines.extend(_hit_lines(nowela))
|
|
47
|
+
lines.append("")
|
|
48
|
+
elif mode == "lookup":
|
|
49
|
+
lines.extend(_hit_lines(out.get("hit") or {}))
|
|
50
|
+
lines.append("")
|
|
51
|
+
elif mode == "disambiguate":
|
|
52
|
+
lines.append(out.get("note") or "Kilka ustaw. Sprecyzuj.")
|
|
53
|
+
for c in out.get("candidates") or []:
|
|
54
|
+
lines.append(f"- {c.get('eli')} art. {c.get('art')} ({c.get('domain')})")
|
|
55
|
+
lines.append("")
|
|
56
|
+
else:
|
|
57
|
+
hits = out.get("hits") or []
|
|
58
|
+
if not hits:
|
|
59
|
+
return "Nie znalazlem w bazie.\n"
|
|
60
|
+
for h in hits[:3]:
|
|
61
|
+
lines.extend(_hit_lines(h))
|
|
62
|
+
lines.append("")
|
|
63
|
+
kis = out.get("kis")
|
|
64
|
+
if kis:
|
|
65
|
+
lines.append(f"KIS {kis.get('sygnatura') or kis.get('id')}: {kis.get('teza') or ''}")
|
|
66
|
+
if kis.get("url"):
|
|
67
|
+
lines.append(kis["url"])
|
|
68
|
+
if kis.get("uwaga"):
|
|
69
|
+
lines.append(kis["uwaga"])
|
|
70
|
+
elif mode != "disambiguate":
|
|
71
|
+
lines.append("KIS: nie znalazłem w bazie interpretacji dla tego artykułu.")
|
|
72
|
+
return "\n".join(lines).strip() + "\n"
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def format_ask_md(out: dict) -> str:
|
|
76
|
+
"""Markdown for the desktop chat. Never invents amounts."""
|
|
77
|
+
if not out.get("ok"):
|
|
78
|
+
return out.get("reason") or "Nie znalazlem w bazie."
|
|
79
|
+
parts: list[str] = []
|
|
80
|
+
mode = out.get("mode")
|
|
81
|
+
if mode == "dual_cite":
|
|
82
|
+
parts.append("## Rozstrzygnięcie")
|
|
83
|
+
parts.append(out.get("ruling") or "")
|
|
84
|
+
parts.append("## Ustawa")
|
|
85
|
+
parts.extend(_hit_lines(out.get("tj") or {}))
|
|
86
|
+
nowela = out.get("nowela") or {}
|
|
87
|
+
if nowela.get("ok"):
|
|
88
|
+
parts.append("## Nowela")
|
|
89
|
+
parts.append("DU/2025/896, wejście **1.01.2026**.")
|
|
90
|
+
parts.extend(_hit_lines(nowela))
|
|
91
|
+
elif mode == "lookup":
|
|
92
|
+
parts.append("## Ustawa")
|
|
93
|
+
parts.extend(_hit_lines(out.get("hit") or {}))
|
|
94
|
+
elif mode == "disambiguate":
|
|
95
|
+
parts.append("## Sprecyzuj")
|
|
96
|
+
parts.append(out.get("note") or "Ten numer artykułu jest w kilku ustawach.")
|
|
97
|
+
for c in out.get("candidates") or []:
|
|
98
|
+
parts.append(f"- {c.get('eli')} art. {c.get('art')} ({c.get('domain')})")
|
|
99
|
+
return "\n\n".join(p for p in parts if p).strip()
|
|
100
|
+
else:
|
|
101
|
+
hits = out.get("hits") or []
|
|
102
|
+
if not hits:
|
|
103
|
+
return "Nie znalazlem w bazie."
|
|
104
|
+
parts.append("## Ustawa")
|
|
105
|
+
for h in hits[:3]:
|
|
106
|
+
parts.extend(_hit_lines(h))
|
|
107
|
+
parts.append("")
|
|
108
|
+
kis = out.get("kis")
|
|
109
|
+
parts.append("## KIS")
|
|
110
|
+
if kis:
|
|
111
|
+
syg = kis.get("sygnatura") or kis.get("id") or ""
|
|
112
|
+
parts.append(f"{syg}: {kis.get('teza') or ''}".strip())
|
|
113
|
+
if kis.get("url"):
|
|
114
|
+
parts.append(f"[Źródło EUREKA]({kis['url']})")
|
|
115
|
+
if kis.get("uwaga"):
|
|
116
|
+
parts.append(kis["uwaga"])
|
|
117
|
+
else:
|
|
118
|
+
parts.append("Nie znalazlem w bazie interpretacji dla tego artykułu.")
|
|
119
|
+
return "\n\n".join(p for p in parts if p is not None).strip()
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def main() -> int:
|
|
123
|
+
p = argparse.ArgumentParser(prog="eurekiusz")
|
|
124
|
+
p.add_argument("--version", action="version", version=f"eurekiusz {__version__}")
|
|
125
|
+
sub = p.add_subparsers(dest="cmd", required=True)
|
|
126
|
+
|
|
127
|
+
pa = sub.add_parser("ask", help="pytanie → ustawa + nowela + KIS")
|
|
128
|
+
pa.add_argument("question")
|
|
129
|
+
pa.add_argument("--json", action="store_true")
|
|
130
|
+
|
|
131
|
+
pl = sub.add_parser("lookup")
|
|
132
|
+
pl.add_argument("eli")
|
|
133
|
+
pl.add_argument("art")
|
|
134
|
+
|
|
135
|
+
ps = sub.add_parser("search")
|
|
136
|
+
ps.add_argument("query")
|
|
137
|
+
ps.add_argument("--domain")
|
|
138
|
+
ps.add_argument("--eli")
|
|
139
|
+
ps.add_argument("-k", type=int, default=5)
|
|
140
|
+
|
|
141
|
+
sub.add_parser("health")
|
|
142
|
+
sub.add_parser("gui", help="okno desktopowe")
|
|
143
|
+
|
|
144
|
+
args = p.parse_args()
|
|
145
|
+
if args.cmd == "gui":
|
|
146
|
+
from eurekiusz.gui import run_gui
|
|
147
|
+
|
|
148
|
+
run_gui()
|
|
149
|
+
return 0
|
|
150
|
+
if args.cmd == "lookup":
|
|
151
|
+
out = lookup(args.eli, args.art)
|
|
152
|
+
elif args.cmd == "search":
|
|
153
|
+
out = search(args.query, domain=args.domain, eli=args.eli, k=args.k)
|
|
154
|
+
elif args.cmd == "health":
|
|
155
|
+
out = health()
|
|
156
|
+
else:
|
|
157
|
+
out = ask(args.question)
|
|
158
|
+
if not getattr(args, "json", False) and args.cmd == "ask":
|
|
159
|
+
sys.stdout.write(format_ask(out))
|
|
160
|
+
return 0 if out.get("ok") else 1
|
|
161
|
+
json.dump(out, sys.stdout, ensure_ascii=False, indent=2)
|
|
162
|
+
sys.stdout.write("\n")
|
|
163
|
+
return 0 if out.get("ok") else 1
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
if __name__ == "__main__":
|
|
167
|
+
raise SystemExit(main())
|
|
Binary file
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""EUREKA (KIS) layer 3. Never overrides statute. Empty is success."""
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import re
|
|
7
|
+
import urllib.error
|
|
8
|
+
import urllib.request
|
|
9
|
+
|
|
10
|
+
EUREKA_SEARCH = (
|
|
11
|
+
"https://eureka.mf.gov.pl/api/public/v1/wyszukiwarka/informacje/"
|
|
12
|
+
"?size=12&page=0&sort=parametryPozycjonowania%2Casc"
|
|
13
|
+
)
|
|
14
|
+
UA = "Mozilla/5.0 (compatible; Eurekiusz/1.0; +https://qa10.io)"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _przepisy_blob(pr) -> str:
|
|
18
|
+
if isinstance(pr, list):
|
|
19
|
+
return " ".join(str(x) for x in pr)
|
|
20
|
+
return str(pr or "")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _status_ok(st) -> bool:
|
|
24
|
+
s = st[0] if isinstance(st, list) else st
|
|
25
|
+
return bool(s) and "aktualna" in str(s).lower()
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def art_in_przepisy(pr, art: str) -> bool:
|
|
29
|
+
"""EUREKA encodes 'art. 113-ust. 1'. Substring '113' is a false friend."""
|
|
30
|
+
blob = _przepisy_blob(pr)
|
|
31
|
+
art = art.strip()
|
|
32
|
+
return bool(re.search(rf"art\.\s*{re.escape(art)}-", blob, re.I))
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def search_kis(nouns: str, art: str | None = None, timeout: float = 8.0) -> dict | None:
|
|
36
|
+
"""Return at most one aktualna interpretacja, or None.
|
|
37
|
+
|
|
38
|
+
Requires art in PRZEPISY when art is given. Requires at least one
|
|
39
|
+
query token (len>=4) in TEZA. Otherwise skip — ranking is garbage.
|
|
40
|
+
"""
|
|
41
|
+
q = " ".join((nouns or "").split()[:8]).strip()
|
|
42
|
+
if len(q) < 4:
|
|
43
|
+
return None
|
|
44
|
+
body = json.dumps(
|
|
45
|
+
{
|
|
46
|
+
"searchQuery": q,
|
|
47
|
+
"searchInContent": True,
|
|
48
|
+
"searchInSynonyms": True,
|
|
49
|
+
"searchInFullPhrase": False,
|
|
50
|
+
"filter": {},
|
|
51
|
+
"columns": [
|
|
52
|
+
"ID_INFORMACJI",
|
|
53
|
+
"SYG",
|
|
54
|
+
"DT_WYD",
|
|
55
|
+
"TEZA",
|
|
56
|
+
"STATUS_INFORMACJI",
|
|
57
|
+
"PRZEPISY",
|
|
58
|
+
"KATEGORIA_INFORMACJI",
|
|
59
|
+
],
|
|
60
|
+
"warunkiDodatkowe": [],
|
|
61
|
+
}
|
|
62
|
+
).encode()
|
|
63
|
+
req = urllib.request.Request(
|
|
64
|
+
EUREKA_SEARCH,
|
|
65
|
+
data=body,
|
|
66
|
+
headers={
|
|
67
|
+
"Content-Type": "application/json",
|
|
68
|
+
"Accept": "application/json",
|
|
69
|
+
"User-Agent": UA,
|
|
70
|
+
},
|
|
71
|
+
method="POST",
|
|
72
|
+
)
|
|
73
|
+
try:
|
|
74
|
+
with urllib.request.urlopen(req, timeout=timeout) as r:
|
|
75
|
+
data = json.loads(r.read().decode())
|
|
76
|
+
except (urllib.error.URLError, TimeoutError, json.JSONDecodeError, OSError):
|
|
77
|
+
return None
|
|
78
|
+
tokens = [t.lower() for t in re.findall(r"[a-ząćęłńóśźż]{4,}", q, re.I)]
|
|
79
|
+
for item in data.get("results") or []:
|
|
80
|
+
if not _status_ok(item.get("STATUS_INFORMACJI")):
|
|
81
|
+
continue
|
|
82
|
+
if art and not art_in_przepisy(item.get("PRZEPISY"), art):
|
|
83
|
+
continue
|
|
84
|
+
teza = (item.get("TEZA") or "").lower()
|
|
85
|
+
if tokens and not any(t in teza for t in tokens):
|
|
86
|
+
continue
|
|
87
|
+
iid = str(item.get("ID_INFORMACJI") or "")
|
|
88
|
+
return {
|
|
89
|
+
"id": iid,
|
|
90
|
+
"sygnatura": item.get("SYG"),
|
|
91
|
+
"data": item.get("DT_WYD"),
|
|
92
|
+
"teza": (item.get("TEZA") or "")[:400],
|
|
93
|
+
"przepisy": item.get("PRZEPISY") or [],
|
|
94
|
+
"url": f"https://eureka.mf.gov.pl/informacje/podglad/{iid}" if iid else None,
|
|
95
|
+
"uwaga": "Interpretacja KIS nie zmienia ustawy. To wykładnia organu.",
|
|
96
|
+
}
|
|
97
|
+
return None
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"""Desktop chat window: local HTML UI + 127.0.0.1 API."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import threading
|
|
5
|
+
import time
|
|
6
|
+
import webbrowser
|
|
7
|
+
|
|
8
|
+
from eurekiusz.server import make_server
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def run_gui() -> None:
|
|
12
|
+
httpd = make_server("127.0.0.1", 0)
|
|
13
|
+
host, port = httpd.server_address[:2]
|
|
14
|
+
url = f"http://{host}:{port}/"
|
|
15
|
+
worker = threading.Thread(target=httpd.serve_forever, daemon=True)
|
|
16
|
+
worker.start()
|
|
17
|
+
|
|
18
|
+
opened = False
|
|
19
|
+
try:
|
|
20
|
+
import webview # type: ignore
|
|
21
|
+
|
|
22
|
+
webview.create_window("Eurekiusz", url, width=1120, height=760)
|
|
23
|
+
webview.start()
|
|
24
|
+
opened = True
|
|
25
|
+
except Exception:
|
|
26
|
+
opened = False
|
|
27
|
+
|
|
28
|
+
if not opened:
|
|
29
|
+
webbrowser.open(url)
|
|
30
|
+
print(f"Eurekiusz okno nie wstało (brak pywebview). Workbench: {url}", flush=True)
|
|
31
|
+
try:
|
|
32
|
+
while worker.is_alive():
|
|
33
|
+
time.sleep(0.4)
|
|
34
|
+
except KeyboardInterrupt:
|
|
35
|
+
pass
|
|
36
|
+
httpd.shutdown()
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def main() -> None:
|
|
40
|
+
run_gui()
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
if __name__ == "__main__":
|
|
44
|
+
main()
|