not1mm 24.12.3__py3-none-any.whl → 24.12.5__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 +40 -5
- not1mm/data/configuration.ui +70 -0
- not1mm/data/new_contest.ui +5 -0
- not1mm/lib/database.py +17 -0
- not1mm/lib/plugin_common.py +63 -1
- not1mm/lib/settings.py +22 -0
- not1mm/lib/version.py +1 -1
- not1mm/lookupservice.py +2 -2
- not1mm/plugins/arrl_160m.py +543 -0
- not1mm/plugins/cq_ww_cw.py +14 -1
- not1mm/plugins/cwt.py +21 -1
- not1mm/plugins/icwc_mst.py +21 -1
- not1mm/rtc_service.py +80 -0
- {not1mm-24.12.3.dist-info → not1mm-24.12.5.dist-info}/METADATA +20 -24
- {not1mm-24.12.3.dist-info → not1mm-24.12.5.dist-info}/RECORD +19 -17
- {not1mm-24.12.3.dist-info → not1mm-24.12.5.dist-info}/LICENSE +0 -0
- {not1mm-24.12.3.dist-info → not1mm-24.12.5.dist-info}/WHEEL +0 -0
- {not1mm-24.12.3.dist-info → not1mm-24.12.5.dist-info}/entry_points.txt +0 -0
- {not1mm-24.12.3.dist-info → not1mm-24.12.5.dist-info}/top_level.txt +0 -0
not1mm/__main__.py
CHANGED
@@ -77,6 +77,7 @@ from not1mm.vfo import VfoWindow
|
|
77
77
|
from not1mm.radio import Radio
|
78
78
|
from not1mm.voice_keying import Voice
|
79
79
|
from not1mm.lookupservice import LookupService
|
80
|
+
from not1mm.rtc_service import RTCService
|
80
81
|
|
81
82
|
poll_time = datetime.datetime.now()
|
82
83
|
|
@@ -102,6 +103,11 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
102
103
|
"multicast_group": "239.1.1.1",
|
103
104
|
"multicast_port": 2239,
|
104
105
|
"interface_ip": "0.0.0.0",
|
106
|
+
"send_rtc_scores": False,
|
107
|
+
"rtc_url": "",
|
108
|
+
"rtc_user": "",
|
109
|
+
"rtc_pass": "",
|
110
|
+
"rtc_interval": 2,
|
105
111
|
"send_n1mm_packets": False,
|
106
112
|
"n1mm_station_name": "20M CW Tent",
|
107
113
|
"n1mm_operator": "Bernie",
|
@@ -156,7 +162,6 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
156
162
|
opon_dialog = None
|
157
163
|
dbname = fsutils.USER_DATA_PATH, "/ham.db"
|
158
164
|
radio_state = {}
|
159
|
-
rig_control = None
|
160
165
|
worked_list = {}
|
161
166
|
cw_entry_visible = False
|
162
167
|
last_focus = None
|
@@ -170,6 +175,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
170
175
|
radio_thread = QThread()
|
171
176
|
voice_thread = QThread()
|
172
177
|
fldigi_thread = QThread()
|
178
|
+
rtc_thread = QThread()
|
173
179
|
|
174
180
|
fldigi_watcher = None
|
175
181
|
rig_control = None
|
@@ -179,6 +185,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
179
185
|
vfo_window = None
|
180
186
|
lookup_service = None
|
181
187
|
fldigi_util = None
|
188
|
+
rtc_service = None
|
182
189
|
|
183
190
|
current_widget = None
|
184
191
|
|
@@ -712,7 +719,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
712
719
|
)
|
713
720
|
|
714
721
|
def load_call_history(self) -> None:
|
715
|
-
""""""
|
722
|
+
"""Display filepicker and load chosen call history file."""
|
716
723
|
filename = self.filepicker("other")
|
717
724
|
if filename:
|
718
725
|
self.database.create_callhistory_table()
|
@@ -755,13 +762,13 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
755
762
|
self.show_message_box(f"{err}")
|
756
763
|
|
757
764
|
def on_focus_changed(self, new):
|
758
|
-
""""""
|
765
|
+
"""Called when text entry focus has changed."""
|
759
766
|
if self.use_esm:
|
760
767
|
if hasattr(self.contest, "process_esm"):
|
761
768
|
self.contest.process_esm(self, new_focused_widget=new)
|
762
769
|
|
763
770
|
def make_button_green(self, the_button: QtWidgets.QPushButton) -> None:
|
764
|
-
"""
|
771
|
+
"""Takes supplied QPushButton object and turns it green."""
|
765
772
|
if the_button is not None:
|
766
773
|
pal = QPalette()
|
767
774
|
pal.isCopyOf(self.current_palette)
|
@@ -2457,7 +2464,10 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
2457
2464
|
self.worked_list = self.database.get_calls_and_bands()
|
2458
2465
|
self.send_worked_list()
|
2459
2466
|
self.clearinputs()
|
2460
|
-
|
2467
|
+
if self.pref.get("send_rtc_scores", False):
|
2468
|
+
if hasattr(self.contest, "online_score_xml"):
|
2469
|
+
if self.rtc_service is not None:
|
2470
|
+
self.rtc_service.xml = self.contest.online_score_xml(self)
|
2461
2471
|
cmd = {}
|
2462
2472
|
cmd["cmd"] = "UPDATELOG"
|
2463
2473
|
if self.log_window:
|
@@ -2898,6 +2908,25 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
2898
2908
|
self.setDarkMode(False)
|
2899
2909
|
self.actionDark_Mode_2.setChecked(False)
|
2900
2910
|
|
2911
|
+
try:
|
2912
|
+
if self.rtc_thread.isRunning():
|
2913
|
+
self.rtc_service.time_to_quit = True
|
2914
|
+
self.rtc_thread.quit()
|
2915
|
+
self.rtc_thread.wait(1000)
|
2916
|
+
|
2917
|
+
except (RuntimeError, AttributeError):
|
2918
|
+
...
|
2919
|
+
|
2920
|
+
self.rtc_service = None
|
2921
|
+
|
2922
|
+
if self.pref.get("send_rtc_scores", False):
|
2923
|
+
self.rtc_service = RTCService()
|
2924
|
+
self.rtc_service.moveToThread(self.rtc_thread)
|
2925
|
+
self.rtc_thread.started.connect(self.rtc_service.run)
|
2926
|
+
self.rtc_thread.finished.connect(self.rtc_service.deleteLater)
|
2927
|
+
# self.rtc_service.poll_callback.connect(self.rtc_result)
|
2928
|
+
self.rtc_thread.start()
|
2929
|
+
|
2901
2930
|
try:
|
2902
2931
|
if self.radio_thread.isRunning():
|
2903
2932
|
self.rig_control.time_to_quit = True
|
@@ -3046,6 +3075,12 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
3046
3075
|
self.esm_dict["MYCALL"] = fkey_dict.get(self.pref.get("esm_mycall", "DISABLED"))
|
3047
3076
|
self.esm_dict["QSOB4"] = fkey_dict.get(self.pref.get("esm_qsob4", "DISABLED"))
|
3048
3077
|
|
3078
|
+
self.send_rtc_scores = self.pref.get("send_rtc_scores", False)
|
3079
|
+
self.rtc_url = self.pref.get("rtc_url", "")
|
3080
|
+
self.rtc_user = self.pref.get("rtc_user", "")
|
3081
|
+
self.rtc_pass = self.pref.get("rtc_pass", "")
|
3082
|
+
self.rtc_interval = self.pref.get("rtc_interval", 2)
|
3083
|
+
|
3049
3084
|
def dark_mode_state_changed(self) -> None:
|
3050
3085
|
"""Called when the Dark Mode menu state is changed."""
|
3051
3086
|
self.pref["darkmode"] = self.actionDark_Mode_2.isChecked()
|
not1mm/data/configuration.ui
CHANGED
@@ -2173,6 +2173,76 @@
|
|
2173
2173
|
</property>
|
2174
2174
|
</widget>
|
2175
2175
|
</item>
|
2176
|
+
<item>
|
2177
|
+
<widget class="Line" name="line">
|
2178
|
+
<property name="orientation">
|
2179
|
+
<enum>Qt::Horizontal</enum>
|
2180
|
+
</property>
|
2181
|
+
</widget>
|
2182
|
+
</item>
|
2183
|
+
<item>
|
2184
|
+
<widget class="QCheckBox" name="send_rtc_scores">
|
2185
|
+
<property name="text">
|
2186
|
+
<string>Use RTC score reporting</string>
|
2187
|
+
</property>
|
2188
|
+
</widget>
|
2189
|
+
</item>
|
2190
|
+
<item>
|
2191
|
+
<widget class="QComboBox" name="rtc_url">
|
2192
|
+
<item>
|
2193
|
+
<property name="text">
|
2194
|
+
<string>https://hamscore.com/postxml/</string>
|
2195
|
+
</property>
|
2196
|
+
</item>
|
2197
|
+
<item>
|
2198
|
+
<property name="text">
|
2199
|
+
<string>https://contestonlinescore.com/post/</string>
|
2200
|
+
</property>
|
2201
|
+
</item>
|
2202
|
+
<item>
|
2203
|
+
<property name="text">
|
2204
|
+
<string>http://contest.run</string>
|
2205
|
+
</property>
|
2206
|
+
</item>
|
2207
|
+
</widget>
|
2208
|
+
</item>
|
2209
|
+
<item>
|
2210
|
+
<widget class="QLineEdit" name="rtc_user">
|
2211
|
+
<property name="placeholderText">
|
2212
|
+
<string>username</string>
|
2213
|
+
</property>
|
2214
|
+
</widget>
|
2215
|
+
</item>
|
2216
|
+
<item>
|
2217
|
+
<widget class="QLineEdit" name="rtc_pass">
|
2218
|
+
<property name="echoMode">
|
2219
|
+
<enum>QLineEdit::EchoMode::Password</enum>
|
2220
|
+
</property>
|
2221
|
+
<property name="placeholderText">
|
2222
|
+
<string>password</string>
|
2223
|
+
</property>
|
2224
|
+
</widget>
|
2225
|
+
</item>
|
2226
|
+
<item>
|
2227
|
+
<widget class="QLabel" name="label_28">
|
2228
|
+
<property name="text">
|
2229
|
+
<string>Score posting interval (minutes)</string>
|
2230
|
+
</property>
|
2231
|
+
<property name="alignment">
|
2232
|
+
<set>Qt::AlignCenter</set>
|
2233
|
+
</property>
|
2234
|
+
</widget>
|
2235
|
+
</item>
|
2236
|
+
<item>
|
2237
|
+
<widget class="QLineEdit" name="rtc_interval">
|
2238
|
+
<property name="text">
|
2239
|
+
<string>2</string>
|
2240
|
+
</property>
|
2241
|
+
<property name="alignment">
|
2242
|
+
<set>Qt::AlignCenter</set>
|
2243
|
+
</property>
|
2244
|
+
</widget>
|
2245
|
+
</item>
|
2176
2246
|
<item>
|
2177
2247
|
<spacer name="verticalSpacer_8">
|
2178
2248
|
<property name="orientation">
|
not1mm/data/new_contest.ui
CHANGED
not1mm/lib/database.py
CHANGED
@@ -695,6 +695,23 @@ class DataBase:
|
|
695
695
|
logger.debug("%s", exception)
|
696
696
|
return {}
|
697
697
|
|
698
|
+
def fetch_exchange1_unique_count(self) -> dict:
|
699
|
+
"""
|
700
|
+
Fetch count of unique countries
|
701
|
+
{exch1_count: count}
|
702
|
+
"""
|
703
|
+
try:
|
704
|
+
with sqlite3.connect(self.database) as conn:
|
705
|
+
conn.row_factory = self.row_factory
|
706
|
+
cursor = conn.cursor()
|
707
|
+
cursor.execute(
|
708
|
+
f"select count(DISTINCT(Exchange1)) as exch1_count from dxlog where Exchange1 != '' and ContestNR = {self.current_contest};"
|
709
|
+
)
|
710
|
+
return cursor.fetchone()
|
711
|
+
except sqlite3.OperationalError as exception:
|
712
|
+
logger.debug("%s", exception)
|
713
|
+
return {}
|
714
|
+
|
698
715
|
def fetch_arrldx_country_band_count(self) -> dict:
|
699
716
|
"""
|
700
717
|
returns dict with count of unique NR.
|
not1mm/lib/plugin_common.py
CHANGED
@@ -4,6 +4,58 @@ import datetime
|
|
4
4
|
from decimal import Decimal
|
5
5
|
from pathlib import Path
|
6
6
|
from not1mm.lib.ham_utility import get_adif_band
|
7
|
+
from not1mm.lib.version import __version__
|
8
|
+
|
9
|
+
|
10
|
+
def online_score_xml(self):
|
11
|
+
"""generate online xml"""
|
12
|
+
|
13
|
+
mults = self.contest.get_mults(self)
|
14
|
+
the_mults = ""
|
15
|
+
for thing in mults:
|
16
|
+
the_mults += (
|
17
|
+
f'<mult band="total" mode="ALL" type="{thing}">{mults.get(thing,0)}</mult>'
|
18
|
+
)
|
19
|
+
|
20
|
+
the_points = self.contest.just_points(self)
|
21
|
+
|
22
|
+
the_date_time = datetime.datetime.now(datetime.timezone.utc).isoformat(" ")[:19]
|
23
|
+
assisted = self.contest_settings.get("AssistedCategory", "")
|
24
|
+
bands = self.contest_settings.get("BandCategory", "")
|
25
|
+
modes = self.contest_settings.get("ModeCategory", "")
|
26
|
+
xmiter = self.contest_settings.get("TransmitterCategory", "")
|
27
|
+
ops = self.contest_settings.get("OperatorCategory", "")
|
28
|
+
overlay = self.contest_settings.get("OverlayCategory", "")
|
29
|
+
power = self.contest_settings.get("PowerCategory", "")
|
30
|
+
|
31
|
+
the_xml = (
|
32
|
+
'<?xml version="1.0"?>'
|
33
|
+
"<dynamicresults>"
|
34
|
+
f"<contest>{self.contest.cabrillo_name}</contest>"
|
35
|
+
f'<call>{self.station.get("Call", "")}</call>'
|
36
|
+
# <ops>NR9Q</ops>
|
37
|
+
f'<class power="{power}" assisted = "{assisted}" transmitter="{xmiter}" ops="{ops}" bands="{bands}" mode="{modes}" overlay="{overlay}"></class>'
|
38
|
+
f"<club>{self.station.get('Club', '').upper()}</club>"
|
39
|
+
"<soft>Not1MM</soft>"
|
40
|
+
f"<version>{__version__}</version>"
|
41
|
+
"<qth>"
|
42
|
+
# <dxcccountry>K</dxcccountry>
|
43
|
+
f"<cqzone>{self.station.get('CQZone','')}</cqzone>"
|
44
|
+
f"<iaruzone>{self.station.get('IARUZone','')}</iaruzone>"
|
45
|
+
f"<arrlsection>{self.station.get('ARRLSection', '')}</arrlsection>"
|
46
|
+
f"<stprvoth>{self.station.get('State','')}</stprvoth>"
|
47
|
+
f"<grid6>{self.station.get('GridSquare','')}</grid6>"
|
48
|
+
"</qth>"
|
49
|
+
"<breakdown>"
|
50
|
+
f'<qso band="total" mode="ALL">{self.contest.show_qso(self)}</qso>'
|
51
|
+
f"{the_mults}"
|
52
|
+
f'<point band="total" mode="ALL">{the_points}</point>'
|
53
|
+
"</breakdown>"
|
54
|
+
f"<score>{self.contest.calc_score(self)}</score>"
|
55
|
+
f"<timestamp>{the_date_time}</timestamp>"
|
56
|
+
"</dynamicresults>"
|
57
|
+
)
|
58
|
+
return the_xml
|
7
59
|
|
8
60
|
|
9
61
|
def get_points(self):
|
@@ -21,10 +73,11 @@ def gen_adif(self, cabrillo_name: str, contest_id=""):
|
|
21
73
|
"""
|
22
74
|
now = datetime.datetime.now()
|
23
75
|
date_time = now.strftime("%Y-%m-%d_%H-%M-%S")
|
76
|
+
station_callsign = self.station.get('Call', '').upper()
|
24
77
|
filename = (
|
25
78
|
str(Path.home())
|
26
79
|
+ "/"
|
27
|
-
+ f"{
|
80
|
+
+ f"{station_callsign}_{cabrillo_name}_{date_time}.adi"
|
28
81
|
)
|
29
82
|
log = self.database.fetch_all_contacts_asc()
|
30
83
|
try:
|
@@ -71,6 +124,15 @@ def gen_adif(self, cabrillo_name: str, contest_id=""):
|
|
71
124
|
except TypeError:
|
72
125
|
...
|
73
126
|
|
127
|
+
try:
|
128
|
+
print(
|
129
|
+
f"<STATION_CALLSIGN:{len(station_callsign)}>{station_callsign}",
|
130
|
+
end="\r\n",
|
131
|
+
file=file_descriptor,
|
132
|
+
)
|
133
|
+
except TypeError:
|
134
|
+
...
|
135
|
+
|
74
136
|
try:
|
75
137
|
print(
|
76
138
|
f"<CALL:{len(hiscall)}>{hiscall.upper()}",
|
not1mm/lib/settings.py
CHANGED
@@ -41,6 +41,19 @@ class Settings(QtWidgets.QDialog):
|
|
41
41
|
def setup(self):
|
42
42
|
"""setup dialog"""
|
43
43
|
|
44
|
+
self.send_rtc_scores.setChecked(
|
45
|
+
bool(self.preference.get("send_rtc_scores", False))
|
46
|
+
)
|
47
|
+
|
48
|
+
value = self.preference.get("rtc_url", "")
|
49
|
+
index = self.rtc_url.findText(value)
|
50
|
+
if index != -1:
|
51
|
+
self.rtc_url.setCurrentIndex(index)
|
52
|
+
|
53
|
+
self.rtc_user.setText(str(self.preference.get("rtc_user", "")))
|
54
|
+
self.rtc_pass.setText(str(self.preference.get("rtc_pass", "")))
|
55
|
+
self.rtc_interval.setText(str(self.preference.get("rtc_interval", "2")))
|
56
|
+
|
44
57
|
self.use_call_history.setChecked(
|
45
58
|
bool(self.preference.get("use_call_history", False))
|
46
59
|
)
|
@@ -195,6 +208,15 @@ class Settings(QtWidgets.QDialog):
|
|
195
208
|
"""
|
196
209
|
Write preferences to json file.
|
197
210
|
"""
|
211
|
+
self.preference["send_rtc_scores"] = self.send_rtc_scores.isChecked()
|
212
|
+
self.preference["rtc_url"] = self.rtc_url.currentText()
|
213
|
+
self.preference["rtc_user"] = self.rtc_user.text()
|
214
|
+
self.preference["rtc_pass"] = self.rtc_pass.text()
|
215
|
+
try:
|
216
|
+
self.preference["rtc_interval"] = int(self.rtc_interval.text())
|
217
|
+
except ValueError:
|
218
|
+
self.preference["rtc_interval"] = 2
|
219
|
+
|
198
220
|
self.preference["use_call_history"] = self.use_call_history.isChecked()
|
199
221
|
self.preference["use_esm"] = self.use_esm.isChecked()
|
200
222
|
self.preference["esm_cq"] = self.esm_cq.currentText()
|
not1mm/lib/version.py
CHANGED
not1mm/lookupservice.py
CHANGED
@@ -3,8 +3,8 @@
|
|
3
3
|
not1mm Contest logger
|
4
4
|
Email: michael.bridak@gmail.com
|
5
5
|
GPL V3
|
6
|
-
Class:
|
7
|
-
Purpose:
|
6
|
+
Class: LookupService
|
7
|
+
Purpose: Lookup callsigns with online services.
|
8
8
|
"""
|
9
9
|
|
10
10
|
# pylint: disable=unused-import, c-extension-no-member, no-member, invalid-name, too-many-lines
|
@@ -0,0 +1,543 @@
|
|
1
|
+
"""ARRL 160 CW plugin"""
|
2
|
+
|
3
|
+
# pylint: disable=invalid-name, c-extension-no-member, unused-import, line-too-long
|
4
|
+
|
5
|
+
import datetime
|
6
|
+
import logging
|
7
|
+
import platform
|
8
|
+
|
9
|
+
from pathlib import Path
|
10
|
+
|
11
|
+
from PyQt6 import QtWidgets
|
12
|
+
|
13
|
+
from not1mm.lib.plugin_common import gen_adif
|
14
|
+
from not1mm.lib.version import __version__
|
15
|
+
|
16
|
+
logger = logging.getLogger(__name__)
|
17
|
+
|
18
|
+
EXCHANGE_HINT = "ST/Prov or DX CQ Zone"
|
19
|
+
|
20
|
+
name = "ARRL 160-Meter"
|
21
|
+
cabrillo_name = "ARRL-160"
|
22
|
+
mode = "CW" # CW SSB BOTH RTTY
|
23
|
+
|
24
|
+
columns = [
|
25
|
+
"YYYY-MM-DD HH:MM:SS",
|
26
|
+
"Call",
|
27
|
+
"Freq",
|
28
|
+
"Snt",
|
29
|
+
"Rcv",
|
30
|
+
"PFX",
|
31
|
+
"Exchange1",
|
32
|
+
"PTS",
|
33
|
+
]
|
34
|
+
|
35
|
+
advance_on_space = [True, True, True, True, True]
|
36
|
+
|
37
|
+
# 1 once per contest, 2 work each band, 3 each band/mode, 4 no dupe checking
|
38
|
+
dupe_type = 1
|
39
|
+
|
40
|
+
|
41
|
+
def init_contest(self):
|
42
|
+
"""setup plugin"""
|
43
|
+
set_tab_next(self)
|
44
|
+
set_tab_prev(self)
|
45
|
+
interface(self)
|
46
|
+
self.next_field = self.other_2
|
47
|
+
|
48
|
+
|
49
|
+
def interface(self):
|
50
|
+
"""Setup user interface"""
|
51
|
+
self.field1.show()
|
52
|
+
self.field2.show()
|
53
|
+
self.field3.hide()
|
54
|
+
self.field4.show()
|
55
|
+
self.snt_label.setText("SNT")
|
56
|
+
self.field1.setAccessibleName("RST Sent")
|
57
|
+
self.exch_label.setText("ARRL/RAC Section")
|
58
|
+
self.field4.setAccessibleName("Received Exchange")
|
59
|
+
|
60
|
+
|
61
|
+
def reset_label(self): # pylint: disable=unused-argument
|
62
|
+
"""reset label after field cleared"""
|
63
|
+
|
64
|
+
|
65
|
+
def set_tab_next(self):
|
66
|
+
"""Set TAB Advances"""
|
67
|
+
self.tab_next = {
|
68
|
+
self.callsign: self.sent,
|
69
|
+
self.sent: self.receive,
|
70
|
+
self.receive: self.other_2,
|
71
|
+
self.other_1: self.other_2,
|
72
|
+
self.other_2: self.callsign,
|
73
|
+
}
|
74
|
+
|
75
|
+
|
76
|
+
def set_tab_prev(self):
|
77
|
+
"""Set TAB Advances"""
|
78
|
+
self.tab_prev = {
|
79
|
+
self.callsign: self.other_2,
|
80
|
+
self.sent: self.callsign,
|
81
|
+
self.receive: self.sent,
|
82
|
+
self.other_1: self.receive,
|
83
|
+
self.other_2: self.receive,
|
84
|
+
}
|
85
|
+
|
86
|
+
|
87
|
+
def set_contact_vars(self):
|
88
|
+
"""Contest Specific"""
|
89
|
+
self.contact["SNT"] = self.sent.text()
|
90
|
+
self.contact["RCV"] = self.receive.text()
|
91
|
+
self.contact["SentNr"] = self.contest_settings.get("SentExchange", 0)
|
92
|
+
self.contact["Exchange1"] = self.other_2.text()
|
93
|
+
|
94
|
+
|
95
|
+
def predupe(self): # pylint: disable=unused-argument
|
96
|
+
"""called after callsign entered"""
|
97
|
+
|
98
|
+
|
99
|
+
def prefill(self):
|
100
|
+
"""Fill SentNR"""
|
101
|
+
|
102
|
+
|
103
|
+
def points(self):
|
104
|
+
"""Calc point"""
|
105
|
+
# Each contact between W/VE stations counts for two (2) QSO points. Each contact with a DX station counts five (5) QSO points
|
106
|
+
call = self.contact.get("Call", "")
|
107
|
+
dupe_check = self.database.check_dupe(call)
|
108
|
+
if dupe_check.get("isdupe", 0) > 0:
|
109
|
+
return 0
|
110
|
+
result = self.cty_lookup(self.station.get("Call", ""))
|
111
|
+
if result:
|
112
|
+
for item in result.items():
|
113
|
+
mypfx = item[1].get("primary_pfx", "")
|
114
|
+
# mycountry = item[1].get("entity", "")
|
115
|
+
# mycontinent = item[1].get("continent", "")
|
116
|
+
|
117
|
+
result = self.cty_lookup(self.contact.get("Call", ""))
|
118
|
+
if result:
|
119
|
+
for item in result.items():
|
120
|
+
pfx = item[1].get("primary_pfx", "")
|
121
|
+
# entity = item[1].get("entity", "")
|
122
|
+
# continent = item[1].get("continent", "")
|
123
|
+
|
124
|
+
# Both in same country
|
125
|
+
|
126
|
+
if mypfx in ["K", "VE"] and pfx in ["K", "VE"]:
|
127
|
+
return 2
|
128
|
+
|
129
|
+
if mypfx.upper() != pfx.upper():
|
130
|
+
return 5
|
131
|
+
|
132
|
+
return 0
|
133
|
+
|
134
|
+
|
135
|
+
def show_mults(self):
|
136
|
+
"""Return display string for mults"""
|
137
|
+
result = self.database.fetch_country_count()
|
138
|
+
mults = int(result.get("dxcc_count", 0))
|
139
|
+
|
140
|
+
result = self.database.fetch_exchange1_unique_count()
|
141
|
+
mults2 = int(result.get("exch1_count", 0))
|
142
|
+
|
143
|
+
return mults + mults2
|
144
|
+
|
145
|
+
|
146
|
+
def show_qso(self):
|
147
|
+
"""Return qso count"""
|
148
|
+
result = self.database.fetch_qso_count()
|
149
|
+
if result:
|
150
|
+
return int(result.get("qsos", 0))
|
151
|
+
return 0
|
152
|
+
|
153
|
+
|
154
|
+
def calc_score(self):
|
155
|
+
"""Return calculated score"""
|
156
|
+
result = self.database.fetch_points()
|
157
|
+
if result is not None:
|
158
|
+
score = result.get("Points", "0")
|
159
|
+
if score is None:
|
160
|
+
score = "0"
|
161
|
+
contest_points = int(score)
|
162
|
+
|
163
|
+
result = self.database.fetch_country_count()
|
164
|
+
mults = int(result.get("dxcc_count", 0))
|
165
|
+
|
166
|
+
result = self.database.fetch_exchange1_unique_count()
|
167
|
+
mults2 = int(result.get("exch1_count", 0))
|
168
|
+
return contest_points * (mults + mults2)
|
169
|
+
return 0
|
170
|
+
|
171
|
+
|
172
|
+
def adif(self):
|
173
|
+
"""Call the generate ADIF function"""
|
174
|
+
gen_adif(self, cabrillo_name, "ARRL 160-Meter")
|
175
|
+
|
176
|
+
|
177
|
+
def output_cabrillo_line(line_to_output, ending, file_descriptor, file_encoding):
|
178
|
+
""""""
|
179
|
+
print(
|
180
|
+
line_to_output.encode(file_encoding, errors="ignore").decode(),
|
181
|
+
end=ending,
|
182
|
+
file=file_descriptor,
|
183
|
+
)
|
184
|
+
|
185
|
+
|
186
|
+
def cabrillo(self, file_encoding):
|
187
|
+
"""Generates Cabrillo file. Maybe."""
|
188
|
+
# https://www.cw160.com/cabrillo.htm
|
189
|
+
logger.debug("******Cabrillo*****")
|
190
|
+
logger.debug("Station: %s", f"{self.station}")
|
191
|
+
logger.debug("Contest: %s", f"{self.contest_settings}")
|
192
|
+
now = datetime.datetime.now()
|
193
|
+
date_time = now.strftime("%Y-%m-%d_%H-%M-%S")
|
194
|
+
filename = (
|
195
|
+
str(Path.home())
|
196
|
+
+ "/"
|
197
|
+
+ f"{self.station.get('Call', '').upper()}_{cabrillo_name}_{date_time}.log"
|
198
|
+
)
|
199
|
+
logger.debug("%s", filename)
|
200
|
+
log = self.database.fetch_all_contacts_asc()
|
201
|
+
try:
|
202
|
+
with open(filename, "w", encoding=file_encoding) as file_descriptor:
|
203
|
+
output_cabrillo_line(
|
204
|
+
"START-OF-LOG: 3.0",
|
205
|
+
"\r\n",
|
206
|
+
file_descriptor,
|
207
|
+
file_encoding,
|
208
|
+
)
|
209
|
+
output_cabrillo_line(
|
210
|
+
f"CREATED-BY: Not1MM v{__version__}",
|
211
|
+
"\r\n",
|
212
|
+
file_descriptor,
|
213
|
+
file_encoding,
|
214
|
+
)
|
215
|
+
output_cabrillo_line(
|
216
|
+
f"CONTEST: {cabrillo_name}",
|
217
|
+
"\r\n",
|
218
|
+
file_descriptor,
|
219
|
+
file_encoding,
|
220
|
+
)
|
221
|
+
if self.station.get("Club", ""):
|
222
|
+
output_cabrillo_line(
|
223
|
+
f"CLUB: {self.station.get('Club', '').upper()}",
|
224
|
+
"\r\n",
|
225
|
+
file_descriptor,
|
226
|
+
file_encoding,
|
227
|
+
)
|
228
|
+
output_cabrillo_line(
|
229
|
+
f"CALLSIGN: {self.station.get('Call','')}",
|
230
|
+
"\r\n",
|
231
|
+
file_descriptor,
|
232
|
+
file_encoding,
|
233
|
+
)
|
234
|
+
output_cabrillo_line(
|
235
|
+
f"LOCATION: {self.station.get('ARRLSection', '')}",
|
236
|
+
"\r\n",
|
237
|
+
file_descriptor,
|
238
|
+
file_encoding,
|
239
|
+
)
|
240
|
+
output_cabrillo_line(
|
241
|
+
f"CATEGORY-OPERATOR: {self.contest_settings.get('OperatorCategory','')}",
|
242
|
+
"\r\n",
|
243
|
+
file_descriptor,
|
244
|
+
file_encoding,
|
245
|
+
)
|
246
|
+
output_cabrillo_line(
|
247
|
+
f"CATEGORY-ASSISTED: {self.contest_settings.get('AssistedCategory','')}",
|
248
|
+
"\r\n",
|
249
|
+
file_descriptor,
|
250
|
+
file_encoding,
|
251
|
+
)
|
252
|
+
output_cabrillo_line(
|
253
|
+
f"CATEGORY-BAND: {self.contest_settings.get('BandCategory','')}",
|
254
|
+
"\r\n",
|
255
|
+
file_descriptor,
|
256
|
+
file_encoding,
|
257
|
+
)
|
258
|
+
output_cabrillo_line(
|
259
|
+
f"CATEGORY-MODE: {self.contest_settings.get('ModeCategory','')}",
|
260
|
+
"\r\n",
|
261
|
+
file_descriptor,
|
262
|
+
file_encoding,
|
263
|
+
)
|
264
|
+
output_cabrillo_line(
|
265
|
+
f"CATEGORY-TRANSMITTER: {self.contest_settings.get('TransmitterCategory','')}",
|
266
|
+
"\r\n",
|
267
|
+
file_descriptor,
|
268
|
+
file_encoding,
|
269
|
+
)
|
270
|
+
if self.contest_settings.get("OverlayCategory", "") != "N/A":
|
271
|
+
output_cabrillo_line(
|
272
|
+
f"CATEGORY-OVERLAY: {self.contest_settings.get('OverlayCategory','')}",
|
273
|
+
"\r\n",
|
274
|
+
file_descriptor,
|
275
|
+
file_encoding,
|
276
|
+
)
|
277
|
+
output_cabrillo_line(
|
278
|
+
f"GRID-LOCATOR: {self.station.get('GridSquare','')}",
|
279
|
+
"\r\n",
|
280
|
+
file_descriptor,
|
281
|
+
file_encoding,
|
282
|
+
)
|
283
|
+
output_cabrillo_line(
|
284
|
+
f"CATEGORY-POWER: {self.contest_settings.get('PowerCategory','')}",
|
285
|
+
"\r\n",
|
286
|
+
file_descriptor,
|
287
|
+
file_encoding,
|
288
|
+
)
|
289
|
+
|
290
|
+
output_cabrillo_line(
|
291
|
+
f"CLAIMED-SCORE: {calc_score(self)}",
|
292
|
+
"\r\n",
|
293
|
+
file_descriptor,
|
294
|
+
file_encoding,
|
295
|
+
)
|
296
|
+
ops = f"@{self.station.get('Call','')}"
|
297
|
+
list_of_ops = self.database.get_ops()
|
298
|
+
for op in list_of_ops:
|
299
|
+
ops += f", {op.get('Operator', '')}"
|
300
|
+
output_cabrillo_line(
|
301
|
+
f"OPERATORS: {ops}",
|
302
|
+
"\r\n",
|
303
|
+
file_descriptor,
|
304
|
+
file_encoding,
|
305
|
+
)
|
306
|
+
output_cabrillo_line(
|
307
|
+
f"NAME: {self.station.get('Name', '')}",
|
308
|
+
"\r\n",
|
309
|
+
file_descriptor,
|
310
|
+
file_encoding,
|
311
|
+
)
|
312
|
+
output_cabrillo_line(
|
313
|
+
f"ADDRESS: {self.station.get('Street1', '')}",
|
314
|
+
"\r\n",
|
315
|
+
file_descriptor,
|
316
|
+
file_encoding,
|
317
|
+
)
|
318
|
+
output_cabrillo_line(
|
319
|
+
f"ADDRESS-CITY: {self.station.get('City', '')}",
|
320
|
+
"\r\n",
|
321
|
+
file_descriptor,
|
322
|
+
file_encoding,
|
323
|
+
)
|
324
|
+
output_cabrillo_line(
|
325
|
+
f"ADDRESS-STATE-PROVINCE: {self.station.get('State', '')}",
|
326
|
+
"\r\n",
|
327
|
+
file_descriptor,
|
328
|
+
file_encoding,
|
329
|
+
)
|
330
|
+
output_cabrillo_line(
|
331
|
+
f"ADDRESS-POSTALCODE: {self.station.get('Zip', '')}",
|
332
|
+
"\r\n",
|
333
|
+
file_descriptor,
|
334
|
+
file_encoding,
|
335
|
+
)
|
336
|
+
output_cabrillo_line(
|
337
|
+
f"ADDRESS-COUNTRY: {self.station.get('Country', '')}",
|
338
|
+
"\r\n",
|
339
|
+
file_descriptor,
|
340
|
+
file_encoding,
|
341
|
+
)
|
342
|
+
output_cabrillo_line(
|
343
|
+
f"EMAIL: {self.station.get('Email', '')}",
|
344
|
+
"\r\n",
|
345
|
+
file_descriptor,
|
346
|
+
file_encoding,
|
347
|
+
)
|
348
|
+
for contact in log:
|
349
|
+
the_date_and_time = contact.get("TS", "")
|
350
|
+
themode = contact.get("Mode", "")
|
351
|
+
if themode == "LSB" or themode == "USB":
|
352
|
+
themode = "PH"
|
353
|
+
frequency = str(int(contact.get("Freq", "0"))).rjust(5)
|
354
|
+
|
355
|
+
loggeddate = the_date_and_time[:10]
|
356
|
+
loggedtime = the_date_and_time[11:13] + the_date_and_time[14:16]
|
357
|
+
thesentnr = contact.get("SentNr", "---")
|
358
|
+
if thesentnr == "":
|
359
|
+
thesentnr = "---"
|
360
|
+
theexch = contact.get("Exchange1", "---")
|
361
|
+
if theexch == "":
|
362
|
+
theexch = "---"
|
363
|
+
|
364
|
+
output_cabrillo_line(
|
365
|
+
f"QSO: {frequency} {themode} {loggeddate} {loggedtime} "
|
366
|
+
f"{contact.get('StationPrefix', '').ljust(13)} "
|
367
|
+
f"{str(contact.get('SNT', '')).ljust(3)} "
|
368
|
+
f"{str(thesentnr).ljust(6)} "
|
369
|
+
f"{contact.get('Call', '').ljust(13)} "
|
370
|
+
f"{str(contact.get('RCV', '')).ljust(3)} "
|
371
|
+
f"{str(theexch).ljust(6)}",
|
372
|
+
"\r\n",
|
373
|
+
file_descriptor,
|
374
|
+
file_encoding,
|
375
|
+
)
|
376
|
+
output_cabrillo_line("END-OF-LOG:", "\r\n", file_descriptor, file_encoding)
|
377
|
+
self.show_message_box(f"Cabrillo saved to: {filename}")
|
378
|
+
except IOError as exception:
|
379
|
+
logger.critical("cabrillo: IO error: %s, writing to %s", exception, filename)
|
380
|
+
self.show_message_box(f"Error saving Cabrillo: {exception} {filename}")
|
381
|
+
return
|
382
|
+
|
383
|
+
|
384
|
+
# def trigger_update(self):
|
385
|
+
# """Triggers the log window to update."""
|
386
|
+
# cmd = {}
|
387
|
+
# cmd["cmd"] = "UPDATELOG"
|
388
|
+
# cmd["station"] = platform.node()
|
389
|
+
# self.multicast_interface.send_as_json(cmd)
|
390
|
+
|
391
|
+
|
392
|
+
def recalculate_mults(self):
|
393
|
+
"""Recalculates multipliers after change in logged qso."""
|
394
|
+
# all_contacts = self.database.fetch_all_contacts_asc()
|
395
|
+
# for contact in all_contacts:
|
396
|
+
# time_stamp = contact.get("TS", "")
|
397
|
+
# if contact.get("CountryPrefix", "") == "K":
|
398
|
+
# query = f"select count(*) as count from dxlog where TS < '{time_stamp}' and CountryPrefix = 'K' and Exchange1 = '{contact.get('Exchange1', '')}' and ContestNR = '{self.pref.get('contest', '0')}'"
|
399
|
+
# result = self.database.exec_sql(query)
|
400
|
+
# if result.get("count", 0) == 0:
|
401
|
+
# contact["IsMultiplier1"] = 1
|
402
|
+
# else:
|
403
|
+
# contact["IsMultiplier1"] = 0
|
404
|
+
# self.database.change_contact(contact)
|
405
|
+
# continue
|
406
|
+
# if contact.get("CountryPrefix", "") == "VE":
|
407
|
+
# query = f"select count(*) as count from dxlog where TS < '{time_stamp}' and CountryPrefix = 'VE' and Exchange1 = '{contact.get('Exchange1', '')}' and ContestNR = '{self.pref.get('contest', '0')}'"
|
408
|
+
# result = self.database.exec_sql(query)
|
409
|
+
# if result.get("count", 0) == 0:
|
410
|
+
# contact["IsMultiplier1"] = 1
|
411
|
+
# else:
|
412
|
+
# contact["IsMultiplier1"] = 0
|
413
|
+
# self.database.change_contact(contact)
|
414
|
+
# continue
|
415
|
+
# query = f"select count(*) as count from dxlog where TS < '{time_stamp}' and CountryPrefix = '{contact.get('CountryPrefix', '')}' and ContestNR = '{self.pref.get('contest', '0')}'"
|
416
|
+
# result = self.database.exec_sql(query)
|
417
|
+
# if result.get("count", 0) == 0:
|
418
|
+
# contact["IsMultiplier1"] = 1
|
419
|
+
# else:
|
420
|
+
# contact["IsMultiplier1"] = 0
|
421
|
+
# self.database.change_contact(contact)
|
422
|
+
# trigger_update(self)
|
423
|
+
|
424
|
+
|
425
|
+
def process_esm(self, new_focused_widget=None, with_enter=False):
|
426
|
+
"""ESM State Machine"""
|
427
|
+
|
428
|
+
# self.pref["run_state"]
|
429
|
+
|
430
|
+
# -----===== Assigned F-Keys =====-----
|
431
|
+
# self.esm_dict["CQ"]
|
432
|
+
# self.esm_dict["EXCH"]
|
433
|
+
# self.esm_dict["QRZ"]
|
434
|
+
# self.esm_dict["AGN"]
|
435
|
+
# self.esm_dict["HISCALL"]
|
436
|
+
# self.esm_dict["MYCALL"]
|
437
|
+
# self.esm_dict["QSOB4"]
|
438
|
+
|
439
|
+
# ----==== text fields ====----
|
440
|
+
# self.callsign
|
441
|
+
# self.sent
|
442
|
+
# self.receive
|
443
|
+
# self.other_1
|
444
|
+
# self.other_2
|
445
|
+
|
446
|
+
if new_focused_widget is not None:
|
447
|
+
self.current_widget = self.inputs_dict.get(new_focused_widget)
|
448
|
+
|
449
|
+
# print(f"checking esm {self.current_widget=} {with_enter=} {self.pref.get("run_state")=}")
|
450
|
+
|
451
|
+
for a_button in [
|
452
|
+
self.esm_dict["CQ"],
|
453
|
+
self.esm_dict["EXCH"],
|
454
|
+
self.esm_dict["QRZ"],
|
455
|
+
self.esm_dict["AGN"],
|
456
|
+
self.esm_dict["HISCALL"],
|
457
|
+
self.esm_dict["MYCALL"],
|
458
|
+
self.esm_dict["QSOB4"],
|
459
|
+
]:
|
460
|
+
if a_button is not None:
|
461
|
+
self.restore_button_color(a_button)
|
462
|
+
|
463
|
+
buttons_to_send = []
|
464
|
+
|
465
|
+
if self.pref.get("run_state"):
|
466
|
+
if self.current_widget == "callsign":
|
467
|
+
if len(self.callsign.text()) < 3:
|
468
|
+
self.make_button_green(self.esm_dict["CQ"])
|
469
|
+
buttons_to_send.append(self.esm_dict["CQ"])
|
470
|
+
elif len(self.callsign.text()) > 2:
|
471
|
+
self.make_button_green(self.esm_dict["HISCALL"])
|
472
|
+
self.make_button_green(self.esm_dict["EXCH"])
|
473
|
+
buttons_to_send.append(self.esm_dict["HISCALL"])
|
474
|
+
buttons_to_send.append(self.esm_dict["EXCH"])
|
475
|
+
|
476
|
+
elif self.current_widget in ["other_2"]:
|
477
|
+
if self.contact.get("CountryPrefix", "") in ["K", "VE"]:
|
478
|
+
if self.other_2.text() == "":
|
479
|
+
self.make_button_green(self.esm_dict["AGN"])
|
480
|
+
buttons_to_send.append(self.esm_dict["AGN"])
|
481
|
+
else:
|
482
|
+
self.make_button_green(self.esm_dict["QRZ"])
|
483
|
+
buttons_to_send.append(self.esm_dict["QRZ"])
|
484
|
+
buttons_to_send.append("LOGIT")
|
485
|
+
else:
|
486
|
+
self.make_button_green(self.esm_dict["QRZ"])
|
487
|
+
buttons_to_send.append(self.esm_dict["QRZ"])
|
488
|
+
buttons_to_send.append("LOGIT")
|
489
|
+
|
490
|
+
if with_enter is True and bool(len(buttons_to_send)):
|
491
|
+
for button in buttons_to_send:
|
492
|
+
if button:
|
493
|
+
if button == "LOGIT":
|
494
|
+
self.save_contact()
|
495
|
+
continue
|
496
|
+
self.process_function_key(button)
|
497
|
+
else:
|
498
|
+
if self.current_widget == "callsign":
|
499
|
+
if len(self.callsign.text()) > 2:
|
500
|
+
self.make_button_green(self.esm_dict["MYCALL"])
|
501
|
+
buttons_to_send.append(self.esm_dict["MYCALL"])
|
502
|
+
|
503
|
+
elif self.current_widget in ["other_2"]:
|
504
|
+
if self.contact.get("CountryPrefix", "") in ["K", "VE"]:
|
505
|
+
if self.other_2.text() == "":
|
506
|
+
self.make_button_green(self.esm_dict["AGN"])
|
507
|
+
buttons_to_send.append(self.esm_dict["AGN"])
|
508
|
+
else:
|
509
|
+
self.make_button_green(self.esm_dict["EXCH"])
|
510
|
+
buttons_to_send.append(self.esm_dict["EXCH"])
|
511
|
+
buttons_to_send.append("LOGIT")
|
512
|
+
|
513
|
+
else:
|
514
|
+
self.make_button_green(self.esm_dict["EXCH"])
|
515
|
+
buttons_to_send.append(self.esm_dict["EXCH"])
|
516
|
+
buttons_to_send.append("LOGIT")
|
517
|
+
|
518
|
+
if with_enter is True and bool(len(buttons_to_send)):
|
519
|
+
for button in buttons_to_send:
|
520
|
+
if button:
|
521
|
+
if button == "LOGIT":
|
522
|
+
self.save_contact()
|
523
|
+
continue
|
524
|
+
self.process_function_key(button)
|
525
|
+
|
526
|
+
|
527
|
+
def populate_history_info_line(self):
|
528
|
+
result = self.database.fetch_call_history(self.callsign.text())
|
529
|
+
if result:
|
530
|
+
self.history_info.setText(
|
531
|
+
f"{result.get('Call', '')}, {result.get('Name', '')}, {result.get('Exch1', '')}, {result.get('UserText','...')}"
|
532
|
+
)
|
533
|
+
else:
|
534
|
+
self.history_info.setText("")
|
535
|
+
|
536
|
+
|
537
|
+
def check_call_history(self):
|
538
|
+
""""""
|
539
|
+
result = self.database.fetch_call_history(self.callsign.text())
|
540
|
+
if result:
|
541
|
+
self.history_info.setText(f"{result.get('UserText','')}")
|
542
|
+
if self.other_2.text() == "":
|
543
|
+
self.other_2.setText(f"{result.get('Exch1', '')}")
|
not1mm/plugins/cq_ww_cw.py
CHANGED
@@ -43,7 +43,7 @@ from pathlib import Path
|
|
43
43
|
|
44
44
|
from PyQt6 import QtWidgets
|
45
45
|
|
46
|
-
from not1mm.lib.plugin_common import gen_adif, get_points
|
46
|
+
from not1mm.lib.plugin_common import gen_adif, get_points, online_score_xml
|
47
47
|
from not1mm.lib.version import __version__
|
48
48
|
from not1mm.lib.ham_utility import get_logged_band
|
49
49
|
|
@@ -177,6 +177,19 @@ def points(self):
|
|
177
177
|
return 0
|
178
178
|
|
179
179
|
|
180
|
+
def get_mults(self):
|
181
|
+
""""""
|
182
|
+
mults = {}
|
183
|
+
mults["zone"] = self.database.fetch_zn_band_count().get("zb_count", 0)
|
184
|
+
mults["country"] = self.database.fetch_country_band_count().get("cb_count", 0)
|
185
|
+
return mults
|
186
|
+
|
187
|
+
|
188
|
+
def just_points(self):
|
189
|
+
""""""
|
190
|
+
return self.database.fetch_points().get("Points", "0")
|
191
|
+
|
192
|
+
|
180
193
|
def show_mults(self):
|
181
194
|
"""Return display string for mults"""
|
182
195
|
result1 = self.database.fetch_zn_band_count()
|
not1mm/plugins/cwt.py
CHANGED
@@ -33,7 +33,7 @@ from pathlib import Path
|
|
33
33
|
|
34
34
|
from PyQt6 import QtWidgets
|
35
35
|
|
36
|
-
from not1mm.lib.plugin_common import gen_adif, get_points
|
36
|
+
from not1mm.lib.plugin_common import gen_adif, get_points, online_score_xml
|
37
37
|
from not1mm.lib.version import __version__
|
38
38
|
|
39
39
|
logger = logging.getLogger(__name__)
|
@@ -519,3 +519,23 @@ def check_call_history(self):
|
|
519
519
|
self.other_1.setText(f"{result.get('Name', '')}")
|
520
520
|
if self.other_2.text() == "":
|
521
521
|
self.other_2.setText(f"{result.get('Exch1', '')}")
|
522
|
+
|
523
|
+
|
524
|
+
# --------RTC Stuff-----------
|
525
|
+
def get_mults(self):
|
526
|
+
""""""
|
527
|
+
|
528
|
+
mults = {}
|
529
|
+
mults["state"] = show_mults(self)
|
530
|
+
return mults
|
531
|
+
|
532
|
+
|
533
|
+
def just_points(self):
|
534
|
+
""""""
|
535
|
+
result = self.database.fetch_points()
|
536
|
+
if result is not None:
|
537
|
+
score = result.get("Points", "0")
|
538
|
+
if score is None:
|
539
|
+
score = "0"
|
540
|
+
return int(score)
|
541
|
+
return 0
|
not1mm/plugins/icwc_mst.py
CHANGED
@@ -34,7 +34,7 @@ from pathlib import Path
|
|
34
34
|
|
35
35
|
from PyQt6 import QtWidgets
|
36
36
|
|
37
|
-
from not1mm.lib.plugin_common import gen_adif, get_points
|
37
|
+
from not1mm.lib.plugin_common import gen_adif, get_points, online_score_xml
|
38
38
|
from not1mm.lib.version import __version__
|
39
39
|
|
40
40
|
logger = logging.getLogger(__name__)
|
@@ -493,3 +493,23 @@ def check_call_history(self):
|
|
493
493
|
self.history_info.setText(f"{result.get('UserText','')}")
|
494
494
|
if self.other_2.text() == "":
|
495
495
|
self.other_2.setText(f"{result.get('Name', '')}")
|
496
|
+
|
497
|
+
|
498
|
+
# --------RTC Stuff-----------
|
499
|
+
def get_mults(self):
|
500
|
+
""""""
|
501
|
+
|
502
|
+
mults = {}
|
503
|
+
mults["state"] = show_mults(self)
|
504
|
+
return mults
|
505
|
+
|
506
|
+
|
507
|
+
def just_points(self):
|
508
|
+
""""""
|
509
|
+
result = self.database.fetch_points()
|
510
|
+
if result is not None:
|
511
|
+
score = result.get("Points", "0")
|
512
|
+
if score is None:
|
513
|
+
score = "0"
|
514
|
+
return int(score)
|
515
|
+
return 0
|
not1mm/rtc_service.py
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
not1mm Contest logger
|
4
|
+
Email: michael.bridak@gmail.com
|
5
|
+
GPL V3
|
6
|
+
Class: RTCService
|
7
|
+
Purpose: Service to post 'real time' scores.
|
8
|
+
"""
|
9
|
+
|
10
|
+
# pylint: disable=unused-import, c-extension-no-member, no-member, invalid-name, too-many-lines
|
11
|
+
# pylint: disable=logging-fstring-interpolation, line-too-long, no-name-in-module
|
12
|
+
|
13
|
+
import datetime
|
14
|
+
import logging
|
15
|
+
import os
|
16
|
+
from json import loads
|
17
|
+
|
18
|
+
import requests
|
19
|
+
from requests.auth import HTTPBasicAuth
|
20
|
+
|
21
|
+
from PyQt6.QtCore import QObject, pyqtSignal, QThread, QEventLoop
|
22
|
+
|
23
|
+
import not1mm.fsutils as fsutils
|
24
|
+
|
25
|
+
logger = logging.getLogger(__name__)
|
26
|
+
|
27
|
+
|
28
|
+
class RTCService(QObject):
|
29
|
+
"""The RTC Service class."""
|
30
|
+
|
31
|
+
poll_callback = pyqtSignal(dict)
|
32
|
+
delta = 2 # two minutes
|
33
|
+
poll_time = datetime.datetime.now() + datetime.timedelta(minutes=delta)
|
34
|
+
time_to_quit = False
|
35
|
+
xml = ""
|
36
|
+
|
37
|
+
def __init__(self):
|
38
|
+
super().__init__()
|
39
|
+
self.pref = self.get_settings()
|
40
|
+
self.delta = self.pref.get("rtc_interval", 2)
|
41
|
+
|
42
|
+
def run(self) -> None:
|
43
|
+
"""Send score xml object to rtc scoring site."""
|
44
|
+
while not self.time_to_quit:
|
45
|
+
# if self.pref.get("send_rtc_scores", False) is True:
|
46
|
+
if datetime.datetime.now() > self.poll_time:
|
47
|
+
self.poll_time = datetime.datetime.now() + datetime.timedelta(
|
48
|
+
minutes=self.delta
|
49
|
+
)
|
50
|
+
if len(self.xml):
|
51
|
+
headers = {"Content-Type": "text/xml"}
|
52
|
+
try:
|
53
|
+
result = requests.post(
|
54
|
+
self.pref.get("rtc_url", ""),
|
55
|
+
data=self.xml,
|
56
|
+
headers=headers,
|
57
|
+
auth=HTTPBasicAuth(
|
58
|
+
self.pref.get("rtc_user", ""),
|
59
|
+
self.pref.get("rtc_pass", ""),
|
60
|
+
),
|
61
|
+
timeout=30,
|
62
|
+
)
|
63
|
+
print(f"{self.xml=}\n{result=}\n{result.text}")
|
64
|
+
except requests.exceptions.Timeout:
|
65
|
+
print("RTC post timeout.")
|
66
|
+
except requests.exceptions.RequestException as e:
|
67
|
+
print(f"An RTC post error occurred: {e}")
|
68
|
+
else:
|
69
|
+
print("No XML data")
|
70
|
+
try:
|
71
|
+
self.poll_callback.emit({"success": True})
|
72
|
+
except QEventLoop:
|
73
|
+
...
|
74
|
+
QThread.msleep(1)
|
75
|
+
|
76
|
+
def get_settings(self) -> dict:
|
77
|
+
"""Get the settings."""
|
78
|
+
if os.path.exists(fsutils.CONFIG_FILE):
|
79
|
+
with open(fsutils.CONFIG_FILE, "rt", encoding="utf-8") as file_descriptor:
|
80
|
+
return loads(file_descriptor.read())
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: not1mm
|
3
|
-
Version: 24.12.
|
3
|
+
Version: 24.12.5
|
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
|
@@ -238,6 +238,9 @@ generated, 'cause I'm lazy, list of those who've submitted PR's.
|
|
238
238
|
|
239
239
|
## Recent Changes (Polishing the Turd)
|
240
240
|
|
241
|
+
- [24-12-5] Add 'real time' score posting to external sites.
|
242
|
+
- [24-12-4] Merged PR from @alduhoo Add STATION_CALLSIGN field to ADIF output
|
243
|
+
- [24-12-3-1] Adding ARRL 160
|
241
244
|
- [24-12-3] Add button to bandmap to delete marked spots.
|
242
245
|
|
243
246
|
See [CHANGELOG.md](CHANGELOG.md) for prior changes.
|
@@ -531,7 +534,7 @@ You can fill. You can fill. Everyone look at your keys.
|
|
531
534
|
|
532
535
|
### Changing station information
|
533
536
|
|
534
|
-
Station information can be changed any time by going
|
537
|
+
Station information can be changed any time by going to
|
535
538
|
`File` > `Station Settings` and editing the information.
|
536
539
|
|
537
540
|
## Selecting a contest (It's REQUIRED Russ)
|
@@ -584,10 +587,7 @@ onscreen icon for CAT status. Green good, Red bad, Grey neither.
|
|
584
587
|
|
585
588
|
Under the `CW` TAB, There are three options. `cwdaemon`, which normally uses IP
|
586
589
|
`127.0.0.1`port `6789`. `pywinkeyer` which normally uses IP `127.0.0.1` port `8000` and
|
587
|
-
`CAT` which if your radio supports it, sends Morse characters via rigctld.
|
588
|
-
as I can tell rigctld does not support setting the radios internal keyer speed. So
|
589
|
-
the CW speed control widget will not be functional and you'd need to control the
|
590
|
-
keyer speed thru the radios interface.
|
590
|
+
`CAT` which if your radio supports it, sends Morse characters via rigctld.
|
591
591
|
|
592
592
|
### Cluster
|
593
593
|
|
@@ -615,27 +615,21 @@ appear. Those without will not.
|
|
615
615
|
|
616
616
|
### Options
|
617
617
|
|
618
|
-
On the Options TAB you can
|
619
|
-
Select whether or not to use Call History info.
|
618
|
+
On the Options TAB you can:
|
620
619
|
|
621
|
-
|
620
|
+
- Select to use Enter Sends Message (ESM), and configure it's function keys.
|
621
|
+
- Select whether or not to use Call History info.
|
622
622
|
|
623
|
-
|
624
|
-
|
625
|
-
**Digital modes only working for:**
|
623
|
+

|
626
624
|
|
627
|
-
-
|
628
|
-
- ARRL VHF
|
629
|
-
- Weekly RTTY
|
630
|
-
- CQ WW DX RTTY
|
631
|
-
- CQ WPX RTTY
|
625
|
+
## Logging WSJT-X FT8/FT4/ETC and FLDIGI RTTY contacts
|
632
626
|
|
633
627
|
not1mm listens for WSJT-X UDP traffic on the Multicast address 224.0.0.1:2237.
|
634
628
|
No setup is needed to be done on not1mm's side. That's good because I'm lazy.
|
635
629
|
|
636
630
|
not1mm polls for fldigi QSOs via it's XMLRPC interface. It does this in a rather stupid
|
637
631
|
way. It just keeps asking what was the last QSO and compares it to the previous response.
|
638
|
-
If it's different, it's new.
|
632
|
+
If it's different, it's new.
|
639
633
|
|
640
634
|
The F1-F12 function keys be sent to fldigi via XMLRPC. Fldigi will be placed into TX
|
641
635
|
mode, the message will be sent and a ^r will be tacked onto the end to place it back into
|
@@ -648,18 +642,20 @@ Generic Contest. Make sure the Text Capture Order field says CALL EXCHANGE.
|
|
648
642
|
## Sending CW
|
649
643
|
|
650
644
|
Other than sending CW by hand, you can also send predefined CW text messages by
|
651
|
-
pressing F1 - F12. See next section on Editing macro keys.
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
645
|
+
pressing F1 - F12. See next section on Editing macro keys.
|
646
|
+
|
647
|
+
If you need to send something freeform, you can press `CTRL-SHIFT-K`, this will
|
648
|
+
expose an entry field at the bottom of the window which you can type directly into.
|
649
|
+
When you're done you can either press CTRL-SHIFT-K again, or press the Enter Key to
|
650
|
+
close the field.
|
656
651
|
|
657
652
|
## Editing macro keys
|
658
653
|
|
659
654
|
To edit the macros, choose `File` > `Edit Macros`. This will open your systems
|
660
655
|
registered text editor with current macros loaded. When your done just save the
|
661
656
|
file and close the editor. The file loaded to edit, CW, SSB or RTTY, will be
|
662
|
-
determined by your current operating mode.
|
657
|
+
determined by your current operating mode and contest. Each contest gets it's own
|
658
|
+
copy of the macros.
|
663
659
|
|
664
660
|
After editing and saving the macro file. You can force the logger to reload the
|
665
661
|
macro file by toggeling between `Run` and `S&P` states.
|
@@ -1,11 +1,12 @@
|
|
1
1
|
not1mm/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
-
not1mm/__main__.py,sha256=
|
2
|
+
not1mm/__main__.py,sha256=4NazDz0wMfre9nXHwdtAZZtkQkEbCUeo4IiquZeuxWg,144335
|
3
3
|
not1mm/bandmap.py,sha256=zD3aUf36NVQCy0plAcZLNxYhSEM9xZ8J1Cu9vrcFPYA,31136
|
4
4
|
not1mm/checkwindow.py,sha256=VFAcKYTcoWhmIf91chwY6tyao9FQMWPiUkgDDkkWaog,9670
|
5
5
|
not1mm/fsutils.py,sha256=ukHKxKTeNKxKwqRaJjtzRShL4X5Xl0jRBbADyy3Ifp8,1701
|
6
6
|
not1mm/logwindow.py,sha256=TvpzQTNB92hISlUO3iWBqtlPmlebdhOkAArx0DNGcOs,43966
|
7
|
-
not1mm/lookupservice.py,sha256=
|
7
|
+
not1mm/lookupservice.py,sha256=GkY_qHZfrW6XHf8upIoaG4hCFqm0fg6Ganu9ConGrIc,2628
|
8
8
|
not1mm/radio.py,sha256=c4m7Ci38uKGKxB0JUT5uOKalI_Mm8Vmixu5D_roN5z4,5400
|
9
|
+
not1mm/rtc_service.py,sha256=Fu_Ru0GR2wcTfmbnt8cIAC5BJfPQEb4kuSTyQohZoi8,2756
|
9
10
|
not1mm/test.py,sha256=RN71m2S9MPIOJMaoCi0wZhwEhpEZunvtosZxaKahRB4,101
|
10
11
|
not1mm/vfo.py,sha256=ggPyWtxMbdSE5RwdK0nDRwDNqOxdpb_pvnzZdbzZVQE,11136
|
11
12
|
not1mm/voice_keying.py,sha256=sA3gw5_k7kShTg2qhG7HkKDM5M6KheJVRkAc_C7mxDk,3006
|
@@ -16,7 +17,7 @@ not1mm/data/alpha bravo charlie delta.txt,sha256=d5QMmSWEUAe4Rj1XbNjTPLa_5Be4Se6
|
|
16
17
|
not1mm/data/bandmap.ui,sha256=krmUYxGPRt2EeqSGrEkY2cXG79ujel3T2_2ymrrbgzg,7942
|
17
18
|
not1mm/data/check.png,sha256=UvFOLr8V-79qnjW8wUaGItXk_OSP8m8hqPevs8NDlFY,387
|
18
19
|
not1mm/data/checkwindow.ui,sha256=PRD98K0julJ9EfWqoE89dT8UPuPKQzGiWBk_efAko3o,5141
|
19
|
-
not1mm/data/configuration.ui,sha256=
|
20
|
+
not1mm/data/configuration.ui,sha256=VJv_uJp53mPK6YyVXOu0aRRtgrgFvIha_wX_7P4M9t0,72554
|
20
21
|
not1mm/data/contests.sql,sha256=4hmJCDvrbxnA_Y5S4T5o52TZieeFk6QUwFerwlFePNA,89307
|
21
22
|
not1mm/data/cty.json,sha256=dPG9K1Pm4Rxd4uJom_gQ8y-sbqiZfILpl4kBAFnOveU,4877142
|
22
23
|
not1mm/data/cwmacros.txt,sha256=NztufsX6R52gAO7VyJ2AHr7wOh41pJTwHKh5Lcs32ds,468
|
@@ -31,7 +32,7 @@ not1mm/data/k6gte.not1mm-64.png,sha256=6ku45Gq1g5ezh04F07osoKRtanb3e4kbx5XdIEh3N
|
|
31
32
|
not1mm/data/logwindow.ui,sha256=f7vULj96tHIQuR1nJMyvPHHcmVgzkhv9D1isyojsnFU,1458
|
32
33
|
not1mm/data/logwindowx.ui,sha256=CwpI-h7cI1yqyldH9quKftsdHL5lTyL9ABOcf80nfqc,1632
|
33
34
|
not1mm/data/main.ui,sha256=pI-70TYESe85ENkRH8l1DXnKDOkwYqKXUdMk6KYaN50,63193
|
34
|
-
not1mm/data/new_contest.ui,sha256=
|
35
|
+
not1mm/data/new_contest.ui,sha256=KXLQixsrKPnS7siN12CznMxtJtwVInf8Aqdw7o4Q2kA,23844
|
35
36
|
not1mm/data/not1mm.html,sha256=c9-mfjMwDt4f5pySUruz2gREW33CQ2_rCddM2z5CZQo,23273
|
36
37
|
not1mm/data/opon.ui,sha256=QDicqAk2lORG2UWsHa6jHlsGn6uzrrI2R4HSAocpPes,2258
|
37
38
|
not1mm/data/pickcontest.ui,sha256=4hPBszCglObThx_eIWtmK9CEcbr7WBjbB1rKZdI-o3I,1707
|
@@ -97,7 +98,7 @@ not1mm/lib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
97
98
|
not1mm/lib/about.py,sha256=sWycfGcruN3SaEe4JmaJ61K6D8Itq0WxpUYT-lEcmYM,416
|
98
99
|
not1mm/lib/cat_interface.py,sha256=0R9ZiEZ0u0RzPrw_hrFNIdS4ybUBgAKtD74x2jdCVIU,25094
|
99
100
|
not1mm/lib/cwinterface.py,sha256=yQL8Dif9oOIynaRItHgvcmu4mYv1TnTpqCHPtkeb09o,4472
|
100
|
-
not1mm/lib/database.py,sha256=
|
101
|
+
not1mm/lib/database.py,sha256=mm3vJgKmAQcUn4Ej--feR0yc_RhWTN01JIwMelI1EdI,48971
|
101
102
|
not1mm/lib/edit_contact.py,sha256=Ki9bGPpqyQQBB1cU8VIBDCal3lbXeQ6qxhzklmhE2_w,353
|
102
103
|
not1mm/lib/edit_macro.py,sha256=raKWBwsHInj5EUKmvyLQ6gqc3ZFDlstsD3xqoM4PC8E,517
|
103
104
|
not1mm/lib/edit_opon.py,sha256=j3qJ1aBsQoIOnQ9yiBl3lyeISvKTP0I_rtBYBPAfgeI,359
|
@@ -110,11 +111,11 @@ not1mm/lib/lookup.py,sha256=KECMDi9tflRDzgTLeDfDl7HGWWRHvW3HCjNHyyjoWaY,10835
|
|
110
111
|
not1mm/lib/multicast.py,sha256=KJcruI-bOuHfHXPjl3SGQhL6I9sKrygy-sdFSvxffUM,3255
|
111
112
|
not1mm/lib/n1mm.py,sha256=H54mpgJF0GAmKavM-nb5OAq2SJFWYkux4eMWWiSRxJc,6288
|
112
113
|
not1mm/lib/new_contest.py,sha256=IznTDMq7yXHB6zBoGUEC_WDYPCPpsSZW4wwMJi16zK0,816
|
113
|
-
not1mm/lib/plugin_common.py,sha256
|
114
|
+
not1mm/lib/plugin_common.py,sha256=-aduWHc_YEQKAFFYXblC7UGq9ymcxhEPL_LKgwDD7_w,13157
|
114
115
|
not1mm/lib/select_contest.py,sha256=WsptLuwkouIHeocJL3oZ6-eUfEnhpwdc-x7eMZ_TIVM,359
|
115
|
-
not1mm/lib/settings.py,sha256=
|
116
|
+
not1mm/lib/settings.py,sha256=j5lIMLHJ-eqIaVr_QhI82gkbOl17_C-5suRkWbHYET8,14717
|
116
117
|
not1mm/lib/super_check_partial.py,sha256=hwT2NRwobu0PLDyw6ltmbmcAtGBD02CKGFbgGWjXMqA,2334
|
117
|
-
not1mm/lib/version.py,sha256=
|
118
|
+
not1mm/lib/version.py,sha256=Gq_FT5obxRUq85-MUXiLa6wBTa2vTHKIXlLWiMe9oRY,48
|
118
119
|
not1mm/lib/versiontest.py,sha256=8vDNptuBBunn-1IGkjNaquehqBYUJyjrPSF8Igmd4_Y,1286
|
119
120
|
not1mm/plugins/10_10_fall_cw.py,sha256=AsvB2VUd6Qb2_FzZkSBkSd1_qeP8Dt-B-exF1Pzb9tk,14469
|
120
121
|
not1mm/plugins/10_10_spring_cw.py,sha256=nA4v0oqlp-ivvKqNPakb19I-wE_ElhvH5bCzDRx00JU,14474
|
@@ -122,6 +123,7 @@ not1mm/plugins/10_10_summer_phone.py,sha256=FNcTQoyZCeAW2i3SKYYDZWuJS1vmk1CO4XO1
|
|
122
123
|
not1mm/plugins/10_10_winter_phone.py,sha256=NRAKgu4oYzrpUtjUKWgCfZQf3b85sdVe9oyl-yD6kJo,14486
|
123
124
|
not1mm/plugins/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
124
125
|
not1mm/plugins/arrl_10m.py,sha256=-stuBD3DCdlWctxE9bWEeznZrgIfNw7VcMIE1ZmsfMo,17942
|
126
|
+
not1mm/plugins/arrl_160m.py,sha256=azm0SV-bTDHe2LDK1TjVgRLajlv2vnKZrpN7wgTv7ho,18441
|
125
127
|
not1mm/plugins/arrl_dx_cw.py,sha256=1epTIf9TjeUCjYlpRDU54Ig3lJdXa2e5JZI9SCGE080,17400
|
126
128
|
not1mm/plugins/arrl_dx_ssb.py,sha256=IEZ6tlP9stW3Mdr5_qTBS77hjAUU43IpagyPjr0eqaQ,17403
|
127
129
|
not1mm/plugins/arrl_field_day.py,sha256=YEyXr1Ytllq12sCj54erOba0wrblwb0_bq51gj75OIQ,16478
|
@@ -137,17 +139,17 @@ not1mm/plugins/cq_160_ssb.py,sha256=_0bkvq0KvSdyTBhkqJ35VqXuaWIaDNmBGE1aQ-cxQz8,
|
|
137
139
|
not1mm/plugins/cq_wpx_cw.py,sha256=H1H4xL-hx0sqU_8fSQYnNzO0ZcOLV-KSOQT9wPvIva0,17843
|
138
140
|
not1mm/plugins/cq_wpx_rtty.py,sha256=nT2lMdAM1pRu2jNKI4FpkGei9kEGX0XcF_24FkL0lnY,20662
|
139
141
|
not1mm/plugins/cq_wpx_ssb.py,sha256=Zjga12w_ISh4aZjCYZbpwN0x0032Prc8p7aIGI7HJFQ,16410
|
140
|
-
not1mm/plugins/cq_ww_cw.py,sha256=
|
142
|
+
not1mm/plugins/cq_ww_cw.py,sha256=53N-q1mTKF4-GH-cyh-a43b4BQ2TP_PNsWWpZchxieI,18230
|
141
143
|
not1mm/plugins/cq_ww_rtty.py,sha256=Pfpr8xWJwp2NOci-WQMTUZaMpAtsUGq1jrIIUv6lQ2Y,21971
|
142
144
|
not1mm/plugins/cq_ww_ssb.py,sha256=IyPmEImq_eb5YSFuhHxiJU4EFAPB4D5dg2xED6Nu97k,17491
|
143
|
-
not1mm/plugins/cwt.py,sha256=
|
145
|
+
not1mm/plugins/cwt.py,sha256=WlZu8urd0J_4sEOKtgf-GrgHPukYT2E13GRXal8i794,17424
|
144
146
|
not1mm/plugins/darc_xmas.py,sha256=GdtAQVCLogKGzZaexJfzsZms5SbLLlO1YweFPjgvYWw,18458
|
145
147
|
not1mm/plugins/general_logging.py,sha256=NV_FCgpAEEQrVRxMDD7nQ2krJgPrhtopizxrGndtUNk,6686
|
146
148
|
not1mm/plugins/helvetia.py,sha256=SRKn7jflfYPUNrvmErDM44af5YWUe57h7JkIwFSbT0Q,19609
|
147
149
|
not1mm/plugins/iaru_fieldday_r1_cw.py,sha256=oWeFuKxvY15vRiUh2vW3z3o7mxJMae7vfpKx4OFU_yA,16816
|
148
150
|
not1mm/plugins/iaru_fieldday_r1_ssb.py,sha256=HylTAcNs0DSii5EDzMQlocjs4k7rQ579YvLrwn6sqIQ,16821
|
149
151
|
not1mm/plugins/iaru_hf.py,sha256=RcVf0UFaHX0eSpUZMMGHC0HTsOy_SwTH9Yi9SeJNQUA,15715
|
150
|
-
not1mm/plugins/icwc_mst.py,sha256=
|
152
|
+
not1mm/plugins/icwc_mst.py,sha256=iFV7iHdI8BLnag-gkQ2q0S4h9n7jXoZ0oTJxtTG14OU,16366
|
151
153
|
not1mm/plugins/jidx_cw.py,sha256=KJOE3fU0KVMqD5IqvnN3YDHPEwrMx3yJZBmCtAIP7WQ,15650
|
152
154
|
not1mm/plugins/jidx_ph.py,sha256=1l92EmDZJFRGZjR1VrISgFc8KoHVfmJvLsaVsuufIMs,14599
|
153
155
|
not1mm/plugins/k1usn_sst.py,sha256=71uO6nUf86J77qSQIiYl_H9EKa2iwyHijcMOk61q6uQ,16653
|
@@ -162,9 +164,9 @@ not1mm/plugins/ref_ssb.py,sha256=G2Gz4kApchmOZQVnBexEokSEvdb-mZWJAfyJ1D6JDGY,204
|
|
162
164
|
not1mm/plugins/stew_perry_topband.py,sha256=Gy_vv6tgkR-3vmvsUVO0pVfHMkUJSxpt7G4secn0RH8,15084
|
163
165
|
not1mm/plugins/weekly_rtty.py,sha256=PI0_AtEdZZKGAuKnP-b2EYn9xwCN1Ablk38trbNP3Rc,19603
|
164
166
|
not1mm/plugins/winter_field_day.py,sha256=9w3tDL9ZWiENSTERc3vzDbBktvI7pnyNvlH6fDjAi08,14841
|
165
|
-
not1mm-24.12.
|
166
|
-
not1mm-24.12.
|
167
|
-
not1mm-24.12.
|
168
|
-
not1mm-24.12.
|
169
|
-
not1mm-24.12.
|
170
|
-
not1mm-24.12.
|
167
|
+
not1mm-24.12.5.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
168
|
+
not1mm-24.12.5.dist-info/METADATA,sha256=Ar9RMBUq1lqLo-121C91gleMPhRr1xnRINb7-l50MfE,35509
|
169
|
+
not1mm-24.12.5.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
|
170
|
+
not1mm-24.12.5.dist-info/entry_points.txt,sha256=pMcZk_0dxFgLkcUkF0Q874ojpwOmF3OL6EKw9LgvocM,47
|
171
|
+
not1mm-24.12.5.dist-info/top_level.txt,sha256=0YmTxEcDzQlzXub-lXASvoLpg_mt1c2thb5cVkDf5J4,7
|
172
|
+
not1mm-24.12.5.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|