not1mm 23.7.29__py3-none-any.whl → 23.8.6__py3-none-any.whl
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.
- not1mm/__main__.py +23 -15
- not1mm/bandmap.py +11 -3
- not1mm/checkwindow.py +206 -0
- not1mm/data/checkwindow.ui +103 -0
- not1mm/data/main.ui +6 -0
- not1mm/lib/database.py +37 -0
- not1mm/lib/super_check_partial.py +56 -0
- not1mm/lib/version.py +1 -1
- not1mm/logwindow.py +6 -3
- {not1mm-23.7.29.dist-info → not1mm-23.8.6.dist-info}/METADATA +13 -9
- {not1mm-23.7.29.dist-info → not1mm-23.8.6.dist-info}/RECORD +15 -12
- {not1mm-23.7.29.dist-info → not1mm-23.8.6.dist-info}/WHEEL +1 -1
- {not1mm-23.7.29.dist-info → not1mm-23.8.6.dist-info}/LICENSE +0 -0
- {not1mm-23.7.29.dist-info → not1mm-23.8.6.dist-info}/entry_points.txt +0 -0
- {not1mm-23.7.29.dist-info → not1mm-23.8.6.dist-info}/top_level.txt +0 -0
not1mm/__main__.py
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
NOT1MM Logger
|
4
4
|
"""
|
5
5
|
# pylint: disable=unused-import, c-extension-no-member, no-member, invalid-name, too-many-lines, no-name-in-module
|
6
|
+
# pylint: disable=logging-fstring-interpolation
|
6
7
|
|
7
8
|
import datetime as dt
|
8
9
|
import importlib
|
@@ -16,7 +17,6 @@ import socket
|
|
16
17
|
import subprocess
|
17
18
|
import sys
|
18
19
|
import threading
|
19
|
-
import time
|
20
20
|
import uuid
|
21
21
|
from datetime import datetime
|
22
22
|
from json import JSONDecodeError, dumps, loads
|
@@ -25,7 +25,6 @@ from shutil import copyfile
|
|
25
25
|
|
26
26
|
import notctyparser
|
27
27
|
import psutil
|
28
|
-
import requests
|
29
28
|
import sounddevice as sd
|
30
29
|
import soundfile as sf
|
31
30
|
from PyQt5 import QtCore, QtGui, QtWidgets, uic
|
@@ -54,6 +53,7 @@ from not1mm.lib.lookup import HamDBlookup, HamQTH, QRZlookup
|
|
54
53
|
from not1mm.lib.multicast import Multicast
|
55
54
|
from not1mm.lib.n1mm import N1MM
|
56
55
|
from not1mm.lib.new_contest import NewContest
|
56
|
+
from not1mm.lib.super_check_partial import SCP
|
57
57
|
from not1mm.lib.select_contest import SelectContest
|
58
58
|
from not1mm.lib.settings import Settings
|
59
59
|
from not1mm.lib.version import __version__
|
@@ -77,8 +77,6 @@ from not1mm.lib.versiontest import VersionTest
|
|
77
77
|
loader = pkgutil.get_loader("not1mm")
|
78
78
|
WORKING_PATH = os.path.dirname(loader.get_filename())
|
79
79
|
|
80
|
-
MASTER_SCP_URL = "https://www.supercheckpartial.com/MASTER.SCP"
|
81
|
-
|
82
80
|
DATA_PATH = os.environ.get("XDG_DATA_HOME", str(Path.home() / ".local" / "share"))
|
83
81
|
DATA_PATH += "/not1mm"
|
84
82
|
|
@@ -232,6 +230,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
232
230
|
self.leftdot.hide()
|
233
231
|
self.rightdot.hide()
|
234
232
|
self.n1mm = N1MM()
|
233
|
+
self.mscp = SCP(WORKING_PATH)
|
235
234
|
self.next_field = self.other_2
|
236
235
|
self.dupe_indicator.hide()
|
237
236
|
self.cw_speed.valueChanged.connect(self.cwspeed_spinbox_changed)
|
@@ -240,6 +239,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
240
239
|
self.actionCommand_Buttons.triggered.connect(self.command_buttons_state_change)
|
241
240
|
self.actionLog_Window.triggered.connect(self.launch_log_window)
|
242
241
|
self.actionBandmap.triggered.connect(self.launch_bandmap_window)
|
242
|
+
self.actionCheck_Window.triggered.connect(self.launch_check_window)
|
243
243
|
self.actionRecalculate_Mults.triggered.connect(self.recalculate_mults)
|
244
244
|
|
245
245
|
self.actionGenerate_Cabrillo.triggered.connect(self.generate_cabrillo)
|
@@ -383,6 +383,10 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
383
383
|
|
384
384
|
def quit_app(self):
|
385
385
|
"""doc"""
|
386
|
+
cmd = {}
|
387
|
+
cmd["cmd"] = "HALT"
|
388
|
+
cmd["station"] = platform.node()
|
389
|
+
self.multicast_interface.send_as_json(cmd)
|
386
390
|
app.quit()
|
387
391
|
|
388
392
|
@staticmethod
|
@@ -421,17 +425,10 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
421
425
|
self.about_dialog.open()
|
422
426
|
|
423
427
|
def update_masterscp(self) -> None:
|
424
|
-
"""Update the MASTER.SCP file.
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
with requests.Session() as session:
|
429
|
-
the_request = session.get(MASTER_SCP_URL)
|
430
|
-
if the_request.status_code == 200:
|
431
|
-
with open(WORKING_PATH + "/data/MASTER.SCP", "wb+") as file:
|
432
|
-
file.write(the_request.content)
|
433
|
-
self.show_message_box("MASTER.SCP file updated.")
|
434
|
-
return
|
428
|
+
"""Update the MASTER.SCP file."""
|
429
|
+
if self.mscp.update_masterscp():
|
430
|
+
self.show_message_box("MASTER.SCP file updated.")
|
431
|
+
return
|
435
432
|
self.show_message_box("MASTER.SCP could not be updated.")
|
436
433
|
|
437
434
|
def edit_configuration_settings(self):
|
@@ -812,6 +809,11 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
812
809
|
if not check_process("bandmap.py"):
|
813
810
|
_ = subprocess.Popen([sys.executable, WORKING_PATH + "/bandmap.py"])
|
814
811
|
|
812
|
+
def launch_check_window(self):
|
813
|
+
"""launch the Log Window"""
|
814
|
+
if not check_process("checkwindow.py"):
|
815
|
+
_ = subprocess.Popen([sys.executable, WORKING_PATH + "/checkwindow.py"])
|
816
|
+
|
815
817
|
def clear_band_indicators(self):
|
816
818
|
"""Clear the indicators"""
|
817
819
|
for _, indicators in self.all_mode_indicators.items():
|
@@ -833,6 +835,10 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
833
835
|
"""
|
834
836
|
Write window size and position to config file
|
835
837
|
"""
|
838
|
+
cmd = {}
|
839
|
+
cmd["cmd"] = "HALT"
|
840
|
+
cmd["station"] = platform.node()
|
841
|
+
self.multicast_interface.send_as_json(cmd)
|
836
842
|
self.pref["window_width"] = self.size().width()
|
837
843
|
self.pref["window_height"] = self.size().height()
|
838
844
|
self.pref["window_x"] = self.pos().x()
|
@@ -1901,6 +1907,8 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
1901
1907
|
stripped_text = text.strip().replace(" ", "")
|
1902
1908
|
self.callsign.setText(stripped_text)
|
1903
1909
|
self.callsign.setCursorPosition(position)
|
1910
|
+
results = self.mscp.super_check(stripped_text)
|
1911
|
+
logger.debug(f"{results}")
|
1904
1912
|
|
1905
1913
|
if " " in text:
|
1906
1914
|
if stripped_text == "CW":
|
not1mm/bandmap.py
CHANGED
@@ -19,12 +19,10 @@ import platform
|
|
19
19
|
import sys
|
20
20
|
import sqlite3
|
21
21
|
|
22
|
-
from PyQt5 import QtCore, QtGui
|
22
|
+
from PyQt5 import QtCore, QtGui
|
23
23
|
from PyQt5 import QtNetwork
|
24
24
|
from PyQt5 import QtWidgets, uic
|
25
25
|
|
26
|
-
from not1mm.lib.multicast import Multicast
|
27
|
-
|
28
26
|
os.environ["QT_QPA_PLATFORMTHEME"] = "gnome"
|
29
27
|
|
30
28
|
PIXELSPERSTEP = 10
|
@@ -259,6 +257,10 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
259
257
|
self.udpsocket.readyRead.connect(self.watch_udp)
|
260
258
|
self.request_workedlist()
|
261
259
|
|
260
|
+
def quit_app(self):
|
261
|
+
"""doc"""
|
262
|
+
app.quit()
|
263
|
+
|
262
264
|
def connect(self):
|
263
265
|
"""doc"""
|
264
266
|
if self.connected is True:
|
@@ -386,6 +388,12 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
386
388
|
):
|
387
389
|
self.worked_list = packet.get("worked", {})
|
388
390
|
logger.debug("%s", f"{self.worked_list}")
|
391
|
+
continue
|
392
|
+
if (
|
393
|
+
packet.get("cmd", "") == "HALT"
|
394
|
+
and packet.get("station", "") == platform.node()
|
395
|
+
):
|
396
|
+
self.quit_app()
|
389
397
|
|
390
398
|
def spot_clicked(self):
|
391
399
|
"""dunno"""
|
not1mm/checkwindow.py
ADDED
@@ -0,0 +1,206 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
Check Window
|
4
|
+
"""
|
5
|
+
# pylint: disable=no-name-in-module, unused-import, no-member, invalid-name
|
6
|
+
|
7
|
+
import logging
|
8
|
+
import pkgutil
|
9
|
+
import platform
|
10
|
+
import queue
|
11
|
+
import os
|
12
|
+
import sys
|
13
|
+
|
14
|
+
from json import JSONDecodeError, loads, dumps
|
15
|
+
from pathlib import Path
|
16
|
+
|
17
|
+
from PyQt5 import uic
|
18
|
+
from PyQt5.QtCore import QDir, Qt
|
19
|
+
from PyQt5.QtWidgets import QApplication, QListWidget, QListWidgetItem, QMainWindow
|
20
|
+
from PyQt5 import QtNetwork
|
21
|
+
from PyQt5.QtGui import QFontDatabase
|
22
|
+
|
23
|
+
from not1mm.lib.database import DataBase
|
24
|
+
from not1mm.lib.multicast import Multicast
|
25
|
+
from not1mm.lib.super_check_partial import SCP
|
26
|
+
|
27
|
+
os.environ["QT_QPA_PLATFORMTHEME"] = "gnome"
|
28
|
+
|
29
|
+
loader = pkgutil.get_loader("not1mm")
|
30
|
+
WORKING_PATH = os.path.dirname(loader.get_filename())
|
31
|
+
|
32
|
+
if "XDG_DATA_HOME" in os.environ:
|
33
|
+
DATA_PATH = os.environ.get("XDG_DATA_HOME")
|
34
|
+
else:
|
35
|
+
DATA_PATH = str(Path.home() / ".local" / "share")
|
36
|
+
DATA_PATH += "/not1mm"
|
37
|
+
|
38
|
+
if "XDG_CONFIG_HOME" in os.environ:
|
39
|
+
CONFIG_PATH = os.environ.get("XDG_CONFIG_HOME")
|
40
|
+
else:
|
41
|
+
CONFIG_PATH = str(Path.home() / ".config")
|
42
|
+
CONFIG_PATH += "/not1mm"
|
43
|
+
|
44
|
+
MULTICAST_PORT = 2239
|
45
|
+
MULTICAST_GROUP = "239.1.1.1"
|
46
|
+
INTERFACE_IP = "0.0.0.0"
|
47
|
+
|
48
|
+
|
49
|
+
class MainWindow(QMainWindow):
|
50
|
+
"""
|
51
|
+
The main window
|
52
|
+
"""
|
53
|
+
|
54
|
+
dbname = None
|
55
|
+
pref = {}
|
56
|
+
|
57
|
+
def __init__(self, *args, **kwargs):
|
58
|
+
super().__init__(*args, **kwargs)
|
59
|
+
self.load_pref()
|
60
|
+
self.dbname = DATA_PATH + "/" + self.pref.get("current_database", "ham.db")
|
61
|
+
self.database = DataBase(self.dbname, WORKING_PATH)
|
62
|
+
self.database.current_contest = self.pref.get("contest", 0)
|
63
|
+
data_path = WORKING_PATH + "/data/checkwindow.ui"
|
64
|
+
uic.loadUi(data_path, self)
|
65
|
+
self.setWindowTitle("CheckWindow")
|
66
|
+
self.logList.clear()
|
67
|
+
self.masterList.clear()
|
68
|
+
self.telnetList.clear()
|
69
|
+
self.callhistoryList.clear()
|
70
|
+
# self.logList.hide()
|
71
|
+
self.telnetList.hide()
|
72
|
+
self.telnetListLabel.hide()
|
73
|
+
self.callhistoryList.hide()
|
74
|
+
self.callhistoryListLabel.hide()
|
75
|
+
self.mscp = SCP(WORKING_PATH)
|
76
|
+
self._udpwatch = None
|
77
|
+
self.udp_fifo = queue.Queue()
|
78
|
+
self.udpsocket = QtNetwork.QUdpSocket(self)
|
79
|
+
self.udpsocket.bind(
|
80
|
+
QtNetwork.QHostAddress.AnyIPv4,
|
81
|
+
MULTICAST_PORT,
|
82
|
+
QtNetwork.QUdpSocket.ShareAddress,
|
83
|
+
)
|
84
|
+
self.udpsocket.joinMulticastGroup(QtNetwork.QHostAddress(MULTICAST_GROUP))
|
85
|
+
self.udpsocket.readyRead.connect(self.watch_udp)
|
86
|
+
|
87
|
+
def quit_app(self):
|
88
|
+
"""doc"""
|
89
|
+
app.quit()
|
90
|
+
|
91
|
+
def load_pref(self):
|
92
|
+
"""Load preference file to get current db filename."""
|
93
|
+
try:
|
94
|
+
if os.path.exists(CONFIG_PATH + "/not1mm.json"):
|
95
|
+
with open(
|
96
|
+
CONFIG_PATH + "/not1mm.json", "rt", encoding="utf-8"
|
97
|
+
) as file_descriptor:
|
98
|
+
self.pref = loads(file_descriptor.read())
|
99
|
+
logger.info("%s", self.pref)
|
100
|
+
else:
|
101
|
+
self.pref["current_database"] = "ham.db"
|
102
|
+
|
103
|
+
except IOError as exception:
|
104
|
+
logger.critical("Error: %s", exception)
|
105
|
+
|
106
|
+
def watch_udp(self):
|
107
|
+
"""Puts UDP datagrams in a FIFO queue"""
|
108
|
+
while self.udpsocket.hasPendingDatagrams():
|
109
|
+
datagram, _, _ = self.udpsocket.readDatagram(
|
110
|
+
self.udpsocket.pendingDatagramSize()
|
111
|
+
)
|
112
|
+
|
113
|
+
try:
|
114
|
+
debug_info = f"{datagram.decode()}"
|
115
|
+
logger.debug(debug_info)
|
116
|
+
json_data = loads(datagram.decode())
|
117
|
+
except UnicodeDecodeError as err:
|
118
|
+
the_error = f"Not Unicode: {err}\n{datagram}"
|
119
|
+
logger.debug(the_error)
|
120
|
+
continue
|
121
|
+
except JSONDecodeError as err:
|
122
|
+
the_error = f"Not JSON: {err}\n{datagram}"
|
123
|
+
logger.debug(the_error)
|
124
|
+
continue
|
125
|
+
if json_data.get("station", "") != platform.node():
|
126
|
+
continue
|
127
|
+
if json_data.get("cmd", "") == "UPDATELOG":
|
128
|
+
self.clear_lists()
|
129
|
+
if json_data.get("cmd", "") == "CALLCHANGED":
|
130
|
+
call = json_data.get("call", "")
|
131
|
+
self.master_list(call)
|
132
|
+
self.log_list(call)
|
133
|
+
if json_data.get("cmd", "") == "NEWDB":
|
134
|
+
...
|
135
|
+
# self.load_new_db()
|
136
|
+
if json_data.get("cmd", "") == "HALT":
|
137
|
+
self.quit_app()
|
138
|
+
|
139
|
+
def clear_lists(self) -> None:
|
140
|
+
"""Clear match lists"""
|
141
|
+
self.logList.clear()
|
142
|
+
self.masterList.clear()
|
143
|
+
self.telnetList.clear()
|
144
|
+
self.callhistoryList.clear()
|
145
|
+
|
146
|
+
def master_list(self, call: str) -> None:
|
147
|
+
"""Get MASTER.SCP matches to call"""
|
148
|
+
results = self.mscp.super_check(call)
|
149
|
+
self.masterList.clear()
|
150
|
+
for item in results:
|
151
|
+
listItem = QListWidgetItem(item)
|
152
|
+
self.masterList.addItem(listItem)
|
153
|
+
self.masterList.show()
|
154
|
+
|
155
|
+
def log_list(self, call: str) -> None:
|
156
|
+
"""Parse calls in log for matches"""
|
157
|
+
self.logList.clear()
|
158
|
+
if call:
|
159
|
+
result = self.database.get_like_calls_and_bands(call)
|
160
|
+
for calls in result:
|
161
|
+
listItem = QListWidgetItem(calls)
|
162
|
+
self.logList.addItem(listItem)
|
163
|
+
self.logList.show()
|
164
|
+
|
165
|
+
|
166
|
+
def load_fonts_from_dir(directory: str) -> set:
|
167
|
+
"""
|
168
|
+
Well it loads fonts from a directory...
|
169
|
+
"""
|
170
|
+
font_families = set()
|
171
|
+
for _fi in QDir(directory).entryInfoList(["*.ttf", "*.woff", "*.woff2"]):
|
172
|
+
_id = QFontDatabase.addApplicationFont(_fi.absoluteFilePath())
|
173
|
+
font_families |= set(QFontDatabase.applicationFontFamilies(_id))
|
174
|
+
return font_families
|
175
|
+
|
176
|
+
|
177
|
+
def main():
|
178
|
+
"""main entry"""
|
179
|
+
sys.exit(app.exec())
|
180
|
+
|
181
|
+
|
182
|
+
logger = logging.getLogger("__main__")
|
183
|
+
handler = logging.StreamHandler()
|
184
|
+
formatter = logging.Formatter(
|
185
|
+
datefmt="%H:%M:%S",
|
186
|
+
fmt="[%(asctime)s] %(levelname)s %(module)s - %(funcName)s Line %(lineno)d:\n%(message)s",
|
187
|
+
)
|
188
|
+
handler.setFormatter(formatter)
|
189
|
+
logger.addHandler(handler)
|
190
|
+
|
191
|
+
if Path("./debug").exists():
|
192
|
+
logger.setLevel(logging.DEBUG)
|
193
|
+
logger.debug("debugging on")
|
194
|
+
else:
|
195
|
+
logger.setLevel(logging.WARNING)
|
196
|
+
logger.warning("debugging off")
|
197
|
+
|
198
|
+
app = QApplication(sys.argv)
|
199
|
+
app.setStyle("Adwaita-Dark")
|
200
|
+
font_path = WORKING_PATH + "/data"
|
201
|
+
_families = load_fonts_from_dir(os.fspath(font_path))
|
202
|
+
window = MainWindow()
|
203
|
+
window.show()
|
204
|
+
|
205
|
+
if __name__ == "__main__":
|
206
|
+
main()
|
@@ -0,0 +1,103 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<ui version="4.0">
|
3
|
+
<class>MainWindow</class>
|
4
|
+
<widget class="QMainWindow" name="MainWindow">
|
5
|
+
<property name="geometry">
|
6
|
+
<rect>
|
7
|
+
<x>0</x>
|
8
|
+
<y>0</y>
|
9
|
+
<width>310</width>
|
10
|
+
<height>600</height>
|
11
|
+
</rect>
|
12
|
+
</property>
|
13
|
+
<property name="font">
|
14
|
+
<font>
|
15
|
+
<family>JetBrains Mono</family>
|
16
|
+
<pointsize>14</pointsize>
|
17
|
+
</font>
|
18
|
+
</property>
|
19
|
+
<property name="windowTitle">
|
20
|
+
<string>CheckWindow</string>
|
21
|
+
</property>
|
22
|
+
<widget class="QWidget" name="centralwidget">
|
23
|
+
<layout class="QGridLayout" name="gridLayout">
|
24
|
+
<item row="1" column="1">
|
25
|
+
<widget class="QListWidget" name="masterList"/>
|
26
|
+
</item>
|
27
|
+
<item row="1" column="3">
|
28
|
+
<widget class="QListWidget" name="callhistoryList"/>
|
29
|
+
</item>
|
30
|
+
<item row="1" column="2">
|
31
|
+
<widget class="QListWidget" name="telnetList"/>
|
32
|
+
</item>
|
33
|
+
<item row="1" column="0">
|
34
|
+
<widget class="QListWidget" name="logList"/>
|
35
|
+
</item>
|
36
|
+
<item row="0" column="0">
|
37
|
+
<widget class="QLabel" name="logListLabel">
|
38
|
+
<property name="font">
|
39
|
+
<font>
|
40
|
+
<family>JetBrains Mono</family>
|
41
|
+
<pointsize>10</pointsize>
|
42
|
+
</font>
|
43
|
+
</property>
|
44
|
+
<property name="text">
|
45
|
+
<string>Log</string>
|
46
|
+
</property>
|
47
|
+
</widget>
|
48
|
+
</item>
|
49
|
+
<item row="0" column="1">
|
50
|
+
<widget class="QLabel" name="masterListLabel">
|
51
|
+
<property name="font">
|
52
|
+
<font>
|
53
|
+
<family>JetBrains Mono</family>
|
54
|
+
<pointsize>10</pointsize>
|
55
|
+
</font>
|
56
|
+
</property>
|
57
|
+
<property name="text">
|
58
|
+
<string>Master</string>
|
59
|
+
</property>
|
60
|
+
</widget>
|
61
|
+
</item>
|
62
|
+
<item row="0" column="2">
|
63
|
+
<widget class="QLabel" name="telnetListLabel">
|
64
|
+
<property name="font">
|
65
|
+
<font>
|
66
|
+
<family>JetBrains Mono</family>
|
67
|
+
<pointsize>10</pointsize>
|
68
|
+
</font>
|
69
|
+
</property>
|
70
|
+
<property name="text">
|
71
|
+
<string>Telnet</string>
|
72
|
+
</property>
|
73
|
+
</widget>
|
74
|
+
</item>
|
75
|
+
<item row="0" column="3">
|
76
|
+
<widget class="QLabel" name="callhistoryListLabel">
|
77
|
+
<property name="font">
|
78
|
+
<font>
|
79
|
+
<family>JetBrains Mono</family>
|
80
|
+
<pointsize>10</pointsize>
|
81
|
+
</font>
|
82
|
+
</property>
|
83
|
+
<property name="text">
|
84
|
+
<string>Call</string>
|
85
|
+
</property>
|
86
|
+
</widget>
|
87
|
+
</item>
|
88
|
+
</layout>
|
89
|
+
</widget>
|
90
|
+
<widget class="QMenuBar" name="menubar">
|
91
|
+
<property name="geometry">
|
92
|
+
<rect>
|
93
|
+
<x>0</x>
|
94
|
+
<y>0</y>
|
95
|
+
<width>310</width>
|
96
|
+
<height>36</height>
|
97
|
+
</rect>
|
98
|
+
</property>
|
99
|
+
</widget>
|
100
|
+
</widget>
|
101
|
+
<resources/>
|
102
|
+
<connections/>
|
103
|
+
</ui>
|
not1mm/data/main.ui
CHANGED
@@ -1082,6 +1082,7 @@
|
|
1082
1082
|
</property>
|
1083
1083
|
<addaction name="actionLog_Window"/>
|
1084
1084
|
<addaction name="actionBandmap"/>
|
1085
|
+
<addaction name="actionCheck_Window"/>
|
1085
1086
|
</widget>
|
1086
1087
|
<widget class="QMenu" name="menuOther">
|
1087
1088
|
<property name="title">
|
@@ -1481,6 +1482,11 @@
|
|
1481
1482
|
<string>Update CTY</string>
|
1482
1483
|
</property>
|
1483
1484
|
</action>
|
1485
|
+
<action name="actionCheck_Window">
|
1486
|
+
<property name="text">
|
1487
|
+
<string>Check Window</string>
|
1488
|
+
</property>
|
1489
|
+
</action>
|
1484
1490
|
</widget>
|
1485
1491
|
<resources/>
|
1486
1492
|
<connections/>
|
not1mm/lib/database.py
CHANGED
@@ -920,6 +920,43 @@ class DataBase:
|
|
920
920
|
logger.debug("%s", exception)
|
921
921
|
return {}
|
922
922
|
|
923
|
+
def get_like_calls_and_bands(self, call: str) -> dict:
|
924
|
+
"""
|
925
|
+
Returns a dict like:
|
926
|
+
{'K5TUX': [14.0, 21.0], 'N2CQR': [14.0], 'NE4RD': [14.0]}
|
927
|
+
"""
|
928
|
+
try:
|
929
|
+
with sqlite3.connect(self.database) as conn:
|
930
|
+
conn.row_factory = self.row_factory
|
931
|
+
cursor = conn.cursor()
|
932
|
+
cursor.execute(
|
933
|
+
f"select call, band from DXLOG where call like '%{call}%' and ContestNR = {self.current_contest};"
|
934
|
+
)
|
935
|
+
result = cursor.fetchall()
|
936
|
+
worked_list = {}
|
937
|
+
# This converts a list of dicts like:
|
938
|
+
# [
|
939
|
+
# {"Call": "K5TUX", "Band": 14.0},
|
940
|
+
# {"Call": "K5TUX", "Band": 21.0},
|
941
|
+
# {"Call": "N2CQR", "Band": 14.0},
|
942
|
+
# {"Call": "NE4RD", "Band": 14.0},
|
943
|
+
# ]
|
944
|
+
#
|
945
|
+
# To:
|
946
|
+
# {'K5TUX': [14.0, 21.0], 'N2CQR': [14.0], 'NE4RD': [14.0]}
|
947
|
+
for worked_dict in result:
|
948
|
+
call = worked_dict.get("Call")
|
949
|
+
if call in worked_list:
|
950
|
+
bandlist = worked_list[call]
|
951
|
+
bandlist.append(worked_dict["Band"])
|
952
|
+
worked_list[call] = bandlist
|
953
|
+
continue
|
954
|
+
worked_list[call] = [worked_dict["Band"]]
|
955
|
+
return worked_list
|
956
|
+
except sqlite3.OperationalError as exception:
|
957
|
+
logger.debug("%s", exception)
|
958
|
+
return {}
|
959
|
+
|
923
960
|
def get_unique_band_and_mode(self) -> dict:
|
924
961
|
"""get count of unique band and mode as {mult: x}"""
|
925
962
|
try:
|
@@ -0,0 +1,56 @@
|
|
1
|
+
"""Super Check Partial"""
|
2
|
+
|
3
|
+
import logging
|
4
|
+
|
5
|
+
import requests
|
6
|
+
|
7
|
+
MASTER_SCP_URL = "https://www.supercheckpartial.com/MASTER.SCP"
|
8
|
+
|
9
|
+
if __name__ == "__main__":
|
10
|
+
print("I'm not the program you are looking for.")
|
11
|
+
|
12
|
+
logger = logging.getLogger("__main__")
|
13
|
+
|
14
|
+
|
15
|
+
class SCP:
|
16
|
+
"""Super check partial"""
|
17
|
+
|
18
|
+
def __init__(self, working_path):
|
19
|
+
"""initialize dialog"""
|
20
|
+
self.scp = []
|
21
|
+
self.working_path = working_path
|
22
|
+
self.read_scp()
|
23
|
+
|
24
|
+
def update_masterscp(self) -> None:
|
25
|
+
"""Update the MASTER.SCP file.
|
26
|
+
- Returns True if successful
|
27
|
+
- Otherwise False
|
28
|
+
"""
|
29
|
+
with requests.Session() as session:
|
30
|
+
the_request = session.get(MASTER_SCP_URL)
|
31
|
+
if the_request.status_code == 200:
|
32
|
+
with open(self.working_path + "/data/MASTER.SCP", "wb+") as file:
|
33
|
+
file.write(the_request.content)
|
34
|
+
return True
|
35
|
+
return False
|
36
|
+
|
37
|
+
def read_scp(self):
|
38
|
+
"""
|
39
|
+
Reads in a list of known contesters into an internal dictionary
|
40
|
+
"""
|
41
|
+
try:
|
42
|
+
with open(
|
43
|
+
self.working_path + "/data/MASTER.SCP", "r", encoding="utf-8"
|
44
|
+
) as file_descriptor:
|
45
|
+
self.scp = file_descriptor.readlines()
|
46
|
+
self.scp = list(map(lambda x: x.strip(), self.scp))
|
47
|
+
except IOError as exception:
|
48
|
+
logger.critical("read_scp: read error: %s", exception)
|
49
|
+
|
50
|
+
def super_check(self, acall: str) -> list:
|
51
|
+
"""
|
52
|
+
Performs a supercheck partial on the callsign entered in the field.
|
53
|
+
"""
|
54
|
+
if len(acall) > 2:
|
55
|
+
return list(filter(lambda x: x.startswith(acall), self.scp))
|
56
|
+
return []
|
not1mm/lib/version.py
CHANGED
@@ -1,2 +1,2 @@
|
|
1
1
|
"""It's the version"""
|
2
|
-
__version__ = "23.
|
2
|
+
__version__ = "23.8.6"
|
not1mm/logwindow.py
CHANGED
@@ -11,10 +11,7 @@ import os
|
|
11
11
|
import pkgutil
|
12
12
|
import platform
|
13
13
|
import queue
|
14
|
-
import socket
|
15
14
|
import sys
|
16
|
-
import time
|
17
|
-
import threading
|
18
15
|
|
19
16
|
from json import JSONDecodeError, loads, dumps
|
20
17
|
from pathlib import Path
|
@@ -164,6 +161,10 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
164
161
|
packet, QtNetwork.QHostAddress(MULTICAST_GROUP), MULTICAST_PORT
|
165
162
|
)
|
166
163
|
|
164
|
+
def quit_app(self):
|
165
|
+
"""doc"""
|
166
|
+
app.quit()
|
167
|
+
|
167
168
|
def get_column(self, name: str) -> int:
|
168
169
|
"""returns the column number of the given column name."""
|
169
170
|
for key, value in self.columns.items():
|
@@ -625,6 +626,8 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
625
626
|
for column in columns_to_show:
|
626
627
|
self.generalLog.setColumnHidden(self.get_column(column), False)
|
627
628
|
self.focusedLog.setColumnHidden(self.get_column(column), False)
|
629
|
+
if json_data.get("cmd", "") == "HALT":
|
630
|
+
self.quit_app()
|
628
631
|
|
629
632
|
def show_like_calls(self, call):
|
630
633
|
"""Show like calls"""
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: not1mm
|
3
|
-
Version: 23.
|
3
|
+
Version: 23.8.6
|
4
4
|
Summary: NOT1MM Logger
|
5
5
|
Author-email: Michael Bridak <michael.bridak@gmail.com>
|
6
6
|
Project-URL: Homepage, https://github.com/mbridak/not1mm
|
@@ -24,7 +24,7 @@ Requires-Dist: psutil
|
|
24
24
|
Requires-Dist: sounddevice
|
25
25
|
Requires-Dist: soundfile
|
26
26
|
Requires-Dist: numpy
|
27
|
-
Requires-Dist: notctyparser
|
27
|
+
Requires-Dist: notctyparser >=23.6.21
|
28
28
|
|
29
29
|
# Not1MM
|
30
30
|
|
@@ -89,6 +89,7 @@ Requires-Dist: notctyparser (>=23.6.21)
|
|
89
89
|
- [Editing a contact](#editing-a-contact)
|
90
90
|
- [Recalulate Mults](#recalulate-mults)
|
91
91
|
- [Bandmap](#bandmap)
|
92
|
+
- [Check Window](#check-window)
|
92
93
|
- [Cabrillo](#cabrillo)
|
93
94
|
- [ADIF](#adif)
|
94
95
|
- [Dupe checking](#dupe-checking)
|
@@ -150,13 +151,8 @@ I wish to thank those who've contributed to the project.
|
|
150
151
|
|
151
152
|
## Recent Changes
|
152
153
|
|
153
|
-
- [23-
|
154
|
-
- [23-
|
155
|
-
- [23-7-27] Check if bandwidth returned is not a number.
|
156
|
-
- [23-7-13] Add IARU HF contest.
|
157
|
-
- [23-7-11] Add mode to logwindow. Highlight already worked calls in bandmap. Add FM and AM to Field Day, since I guess it's still a thing.
|
158
|
-
- [23-7-5] Fix coredump in bandmap after CTRL-G.
|
159
|
-
- [23-7-2] bandmap now requests worked list at startup. Completed ARRL Field Day plugin.
|
154
|
+
- [23-8-6] Add parsing of local log to check window.
|
155
|
+
- [23-8-5] Add Check Window. Moved MASTER.SCP stuff to it's own class. Close sub windows when main app closes.
|
160
156
|
|
161
157
|
See [CHANGELOG.md](CHANGELOG.md) for prior changes.
|
162
158
|
|
@@ -483,6 +479,14 @@ VFO indicator now displays as small triangle in the frequency tickmarks. A small
|
|
483
479
|
|
484
480
|
Clicked on spots now tune the radio and set the callsign field. Previously worked calls are displayed in red.
|
485
481
|
|
482
|
+
## Check Window
|
483
|
+
|
484
|
+
`Window`>`Check Window`
|
485
|
+
|
486
|
+
As you enter a callsign, the Check Window will show probable matches to calls either in the MASTER.SCP file, or your local log. The MASTER.SCP column will show results for strings of 3 or more matching from the start of the call string. The local log column will show matches of any length appearing anywhere in the string.
|
487
|
+
|
488
|
+

|
489
|
+
|
486
490
|
## Cabrillo
|
487
491
|
|
488
492
|
Click on `File` > `Generate Cabrillo`
|
@@ -1,13 +1,15 @@
|
|
1
1
|
not1mm/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
-
not1mm/__main__.py,sha256=
|
3
|
-
not1mm/bandmap.py,sha256=
|
4
|
-
not1mm/
|
2
|
+
not1mm/__main__.py,sha256=epgycpDTK2ELV0NNwQcoukeC7sJVSdSkoD4pSurn5d0,96702
|
3
|
+
not1mm/bandmap.py,sha256=5mANS6F9c11CrEzHD5EL2sTYB_d-ncvRofrLg8bHMP0,26884
|
4
|
+
not1mm/checkwindow.py,sha256=gJYc67pMKbxNQMPGJ1PI41nKWGXvG1_H1n9gQRvXsJA,6451
|
5
|
+
not1mm/logwindow.py,sha256=DOv2L3wEWzxrIFb5mAg6yaIoW9ejSbcuLN6D5HiLgf8,34331
|
5
6
|
not1mm/data/JetBrainsMono-Regular.ttf,sha256=UOHctAKY_PzCGh7zy-6f6egnCcSK0wzmF0csBqO9lDY,203952
|
6
7
|
not1mm/data/MASTER.SCP,sha256=J7Xi62-SF0vO8X1IvQE5O-dESikT6OVmrT_x80F7f4Y,657382
|
7
8
|
not1mm/data/about.ui,sha256=7TqvtXFFm0Rmcu0bmLupwpO1CsK8MekfZ09_xn6kZrQ,2067
|
8
9
|
not1mm/data/alpha bravo charlie delta.txt,sha256=d5QMmSWEUAe4Rj1XbNjTPLa_5Be4Se6u5LUIqAYidOQ,224
|
9
10
|
not1mm/data/bandmap.ui,sha256=FB2NaxSK-ZZvfYHU9Mu8FkQxTeVHcFGV40zIqgBXAFo,5246
|
10
11
|
not1mm/data/check.png,sha256=UvFOLr8V-79qnjW8wUaGItXk_OSP8m8hqPevs8NDlFY,387
|
12
|
+
not1mm/data/checkwindow.ui,sha256=nc4FRc4D3ozC1jhm96Q2y6rolayKF6OwMrbCSaQ-vbg,2572
|
11
13
|
not1mm/data/configuration.ui,sha256=-6LKpkTWFuWy9gVBeQ5k3fg5Mw01rwAxp6fQag0E38c,41522
|
12
14
|
not1mm/data/contests.sql,sha256=4hmJCDvrbxnA_Y5S4T5o52TZieeFk6QUwFerwlFePNA,89307
|
13
15
|
not1mm/data/cty.json,sha256=0l3wUE5vsc9yknd59ffRLFtAPcnXNl2IaKAmQojGBGI,4667346
|
@@ -21,7 +23,7 @@ not1mm/data/k6gte.not1mm-128.png,sha256=vWqt3Cgsaguj-BBiIoSJApzzhisPxldM8HZQbZ05
|
|
21
23
|
not1mm/data/k6gte.not1mm-32.png,sha256=yucSwzlmqv3NegdWUvPvZzSgP7G22Ky3se8TWRXvzfI,1108
|
22
24
|
not1mm/data/k6gte.not1mm-64.png,sha256=1KQvk0WBckUds79BvIFUt-KdTwQKKvTz6hiJu8MiT68,2152
|
23
25
|
not1mm/data/logwindow.ui,sha256=_-wobHhIjALzCswyXIrqNadnLdc88eay1GNF23a-Qh0,970
|
24
|
-
not1mm/data/main.ui,sha256=
|
26
|
+
not1mm/data/main.ui,sha256=JLoTY7pl9GpxiZB-YirfPa7GSp5CzeIB4WV0LjEoTmw,45152
|
25
27
|
not1mm/data/new_contest.ui,sha256=9ujIv7Q5DRIhT77QKQE7Kddx3a7WiMFe4Dvy2UUDK6M,18584
|
26
28
|
not1mm/data/not1mm.html,sha256=Z52qcafOQJrgD_bbgy86C-PU-tb4kGlWv9gSxRXAZ1A,20129
|
27
29
|
not1mm/data/opon.ui,sha256=6r9_6ORGfNqwOnpzQjaJ1tWP_81amuXqLYlx1hHgdME,2018
|
@@ -81,7 +83,7 @@ not1mm/lib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
81
83
|
not1mm/lib/about.py,sha256=O9i8ypv2W9KZkPAp2bcRI0J5RgPE5R1vMfU8ZGlok_E,379
|
82
84
|
not1mm/lib/cat_interface.py,sha256=eKsxC42Mm9wv7E2YQmgL7tLA2Gom-vCVaHHRf2Kitdk,15676
|
83
85
|
not1mm/lib/cwinterface.py,sha256=fZ5BXVyF7by0yGTJVznUTjRCxRt3lJaUIsNC-_mCGP4,3123
|
84
|
-
not1mm/lib/database.py,sha256=
|
86
|
+
not1mm/lib/database.py,sha256=j_AHxc_cW8vyfwmNrqO9DUPW2HnJqtMKKQeqq233A64,40117
|
85
87
|
not1mm/lib/edit_contact.py,sha256=YwuX-BuIa7AuPtLRENs4jTzxOrtk6MCxZj3BR_bDPW8,357
|
86
88
|
not1mm/lib/edit_macro.py,sha256=lyToZb1nmcONNFh6W35NzYHLU48gbAs2_OsnuRQGHck,559
|
87
89
|
not1mm/lib/edit_opon.py,sha256=GqAOJMiC265_4YRVbkT1PflBuCa4HiCNfRhq8-J6ZVY,363
|
@@ -93,7 +95,8 @@ not1mm/lib/n1mm.py,sha256=O3U2dhOX-_5nBomyuiyfx-_GQk6rj067lwrcC9GtDpQ,5713
|
|
93
95
|
not1mm/lib/new_contest.py,sha256=mHKNCS3iKOKN-bT9d8ZK3JemThOZFQ0ikfUSS0-ZTpY,354
|
94
96
|
not1mm/lib/select_contest.py,sha256=XQdRUkPAIHIMVsilm82M54b_v9yWpYrZ1nfInJrtZoo,363
|
95
97
|
not1mm/lib/settings.py,sha256=tkCWgJ3CpNkGR_IyCTOCIlO6PEPZCShHO9FL-9Wono8,6885
|
96
|
-
not1mm/lib/
|
98
|
+
not1mm/lib/super_check_partial.py,sha256=GlXgtIblL602iW-V6Mmdf5S4FxtzJ95TbPMMa9xXUfg,1692
|
99
|
+
not1mm/lib/version.py,sha256=hATNe5bxbwO_Se-Zg1fJdhcw2L9NT4hAiGokiArFTD4,46
|
97
100
|
not1mm/lib/versiontest.py,sha256=8vDNptuBBunn-1IGkjNaquehqBYUJyjrPSF8Igmd4_Y,1286
|
98
101
|
not1mm/plugins/10_10_fall_cw.py,sha256=uV66Mmj6vuNGgY_4PX4lKWvvkyqBQNDf8gAAGTIoKxE,13883
|
99
102
|
not1mm/plugins/10_10_spring_cw.py,sha256=yAxvP1fR5vg4cqpADvG0BN-ibjW7lSAcn-p9VzXNW3g,13888
|
@@ -125,9 +128,9 @@ not1mm/testing/multicast_listener.py,sha256=2CkiyZ4EQxBX68_1QzGIX9g_UB9-CQq63OH-
|
|
125
128
|
not1mm/testing/n1mm_listener.py,sha256=UD-qyKEnppQua330WEFKMvMJaNjnYKi7dDuX_RGB5lQ,1099
|
126
129
|
not1mm/testing/test.py,sha256=wGblvMlyOCVkEiHbxE6wvLsorim15ehL72_EZLQeWkk,1660
|
127
130
|
testing/test.py,sha256=icT6vZPbJaZWJbEZ09nSbPmTFeiaL7LbXqM_sjhDisI,471
|
128
|
-
not1mm-23.
|
129
|
-
not1mm-23.
|
130
|
-
not1mm-23.
|
131
|
-
not1mm-23.
|
132
|
-
not1mm-23.
|
133
|
-
not1mm-23.
|
131
|
+
not1mm-23.8.6.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
132
|
+
not1mm-23.8.6.dist-info/METADATA,sha256=nvBHO-Wu4EX-eMTfuwrP4zuRJaqT7aOsIPqWXlT5xPM,23938
|
133
|
+
not1mm-23.8.6.dist-info/WHEEL,sha256=5sUXSg9e4bi7lTLOHcm6QEYwO5TIF1TNbTSVFVjcJcc,92
|
134
|
+
not1mm-23.8.6.dist-info/entry_points.txt,sha256=pMcZk_0dxFgLkcUkF0Q874ojpwOmF3OL6EKw9LgvocM,47
|
135
|
+
not1mm-23.8.6.dist-info/top_level.txt,sha256=-NwUrh4k1kzpOvf720xYWSTKSlr-zNSIz3D_eplrxLs,15
|
136
|
+
not1mm-23.8.6.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|