not1mm 24.8.27__py3-none-any.whl → 24.9.3__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 CHANGED
@@ -23,6 +23,7 @@ import threading
23
23
  import uuid
24
24
 
25
25
  from json import dumps, loads
26
+ from json.decoder import JSONDecodeError
26
27
  from pathlib import Path
27
28
  from shutil import copyfile
28
29
 
@@ -186,8 +187,7 @@ class MainWindow(QtWidgets.QMainWindow):
186
187
  )
187
188
  self.setCorner(Qt.Corner.TopLeftCorner, Qt.DockWidgetArea.LeftDockWidgetArea)
188
189
  self.setCorner(Qt.Corner.BottomLeftCorner, Qt.DockWidgetArea.LeftDockWidgetArea)
189
- data_path = fsutils.APP_DATA_PATH / "main.ui"
190
- uic.loadUi(data_path, self)
190
+ uic.loadUi(fsutils.APP_DATA_PATH / "main.ui", self)
191
191
  self.cw_entry.hide()
192
192
  self.leftdot.hide()
193
193
  self.rightdot.hide()
@@ -459,8 +459,15 @@ class MainWindow(QtWidgets.QMainWindow):
459
459
  self.setWindowIcon(
460
460
  QtGui.QIcon(str(fsutils.APP_DATA_PATH / "k6gte.not1mm-32.png"))
461
461
  )
462
- with open(fsutils.APP_DATA_PATH / "cty.json", "rt", encoding="utf-8") as c_file:
463
- self.ctyfile = loads(c_file.read())
462
+
463
+ try:
464
+ with open(
465
+ fsutils.APP_DATA_PATH / "cty.json", "rt", encoding="utf-8"
466
+ ) as c_file:
467
+ self.ctyfile = loads(c_file.read())
468
+ except (IOError, JSONDecodeError, TypeError):
469
+ logging.CRITICAL("There was an error parsing the BigCity file.")
470
+
464
471
  self.readpreferences()
465
472
 
466
473
  self.voice_process = Voice()
@@ -491,9 +498,7 @@ class MainWindow(QtWidgets.QMainWindow):
491
498
  self.make_op_dir()
492
499
 
493
500
  self.clearinputs()
494
-
495
- if self.pref.get("contest"):
496
- self.load_contest()
501
+ self.load_contest()
497
502
  self.read_cw_macros()
498
503
 
499
504
  # Featureset for wayland
@@ -1287,10 +1292,15 @@ class MainWindow(QtWidgets.QMainWindow):
1287
1292
  if updated:
1288
1293
  cty.dump(fsutils.APP_DATA_PATH / "cty.json")
1289
1294
  self.show_message_box("cty file updated.")
1290
- with open(
1291
- fsutils.APP_DATA_PATH / "cty.json", "rt", encoding="utf-8"
1292
- ) as ctyfile:
1293
- self.ctyfile = loads(ctyfile.read())
1295
+ try:
1296
+ with open(
1297
+ fsutils.APP_DATA_PATH / "cty.json", "rt", encoding="utf-8"
1298
+ ) as ctyfile:
1299
+ self.ctyfile = loads(ctyfile.read())
1300
+ except (IOError, JSONDecodeError, TypeError) as err:
1301
+ logging.CRITICAL(
1302
+ f"There was an error {err} parsing the BigCity file."
1303
+ )
1294
1304
  else:
1295
1305
  self.show_message_box("An Error occured updating file.")
1296
1306
  else:
@@ -1898,11 +1908,14 @@ class MainWindow(QtWidgets.QMainWindow):
1898
1908
  " "
1899
1909
  )[:19]
1900
1910
  self.contact["Call"] = self.callsign.text()
1901
- self.contact["Freq"] = round(float(self.radio_state.get("vfoa", 0.0)) / 1000, 2)
1902
- self.contact["QSXFreq"] = round(
1903
- float(self.radio_state.get("vfoa", 0.0)) / 1000, 2
1904
- )
1905
- self.contact["Mode"] = self.radio_state.get("mode", "")
1911
+ if self.contact.get("Mode") not in ("FT8", "FT4"):
1912
+ self.contact["Freq"] = round(
1913
+ float(self.radio_state.get("vfoa", 0.0)) / 1000, 2
1914
+ )
1915
+ self.contact["QSXFreq"] = round(
1916
+ float(self.radio_state.get("vfoa", 0.0)) / 1000, 2
1917
+ )
1918
+ self.contact["Mode"] = self.radio_state.get("mode", "")
1906
1919
  self.contact["ContestName"] = self.contest.cabrillo_name
1907
1920
  self.contact["ContestNR"] = self.pref.get("contest", "0")
1908
1921
  self.contact["StationPrefix"] = self.station.get("Call", "")
@@ -2093,7 +2106,7 @@ class MainWindow(QtWidgets.QMainWindow):
2093
2106
 
2094
2107
  def save_settings(self) -> None:
2095
2108
  """
2096
- Save settings to database.
2109
+ Save Station settings to database.
2097
2110
 
2098
2111
  Parameters
2099
2112
  ----------
@@ -2322,7 +2335,7 @@ class MainWindow(QtWidgets.QMainWindow):
2322
2335
  with open(fsutils.CONFIG_FILE, "wt", encoding="utf-8") as file_descriptor:
2323
2336
  file_descriptor.write(dumps(self.pref, indent=4))
2324
2337
  # logger.info("writing: %s", self.pref)
2325
- except IOError as exception:
2338
+ except (IOError, TypeError, ValueError) as exception:
2326
2339
  logger.critical("writepreferences: %s", exception)
2327
2340
 
2328
2341
  def readpreferences(self) -> None:
@@ -2344,7 +2357,12 @@ class MainWindow(QtWidgets.QMainWindow):
2344
2357
  with open(
2345
2358
  fsutils.CONFIG_FILE, "rt", encoding="utf-8"
2346
2359
  ) as file_descriptor:
2347
- self.pref = loads(file_descriptor.read())
2360
+ try:
2361
+ self.pref = loads(file_descriptor.read())
2362
+ except (JSONDecodeError, TypeError):
2363
+ logging.CRITICAL(
2364
+ "There was an error parsing the preference file."
2365
+ )
2348
2366
  logger.info("%s", self.pref)
2349
2367
  else:
2350
2368
  logger.info("No preference file. Writing preference.")
@@ -2354,7 +2372,7 @@ class MainWindow(QtWidgets.QMainWindow):
2354
2372
  self.pref = self.pref_ref.copy()
2355
2373
  file_descriptor.write(dumps(self.pref, indent=4))
2356
2374
  logger.info("%s", self.pref)
2357
- except IOError as exception:
2375
+ except (IOError, TypeError, ValueError) as exception:
2358
2376
  logger.critical("Error: %s", exception)
2359
2377
 
2360
2378
  self.look_up = None
@@ -3197,10 +3215,14 @@ class MainWindow(QtWidgets.QMainWindow):
3197
3215
  else:
3198
3216
  macro_file = "ssbmacros.txt"
3199
3217
  if not (fsutils.USER_DATA_PATH / macro_file).exists():
3200
- logger.debug("read_cw_macros: copying default macro file.")
3201
- copyfile(
3202
- fsutils.APP_DATA_PATH / macro_file, fsutils.USER_DATA_PATH / macro_file
3203
- )
3218
+ logger.debug("copying default macro file.")
3219
+ try:
3220
+ copyfile(
3221
+ fsutils.APP_DATA_PATH / macro_file,
3222
+ fsutils.USER_DATA_PATH / macro_file,
3223
+ )
3224
+ except IOError as err:
3225
+ logger.critical(f"Error {err} copying macro file.")
3204
3226
  try:
3205
3227
  fsutils.openFileWithOS(fsutils.USER_DATA_PATH / macro_file)
3206
3228
  except FileNotFoundError | PermissionError | OSError as err:
@@ -3221,22 +3243,26 @@ class MainWindow(QtWidgets.QMainWindow):
3221
3243
  macro_file = "ssbmacros.txt"
3222
3244
 
3223
3245
  if not (fsutils.USER_DATA_PATH / macro_file).exists():
3224
- logger.debug("read_cw_macros: copying default macro file.")
3225
- copyfile(
3226
- fsutils.APP_DATA_PATH / macro_file, fsutils.USER_DATA_PATH / macro_file
3227
- )
3228
- with open(
3229
- fsutils.USER_DATA_PATH / macro_file, "r", encoding="utf-8"
3230
- ) as file_descriptor:
3231
- for line in file_descriptor:
3232
- try:
3246
+ logger.debug("copying default macro file.")
3247
+ try:
3248
+ copyfile(
3249
+ fsutils.APP_DATA_PATH / macro_file,
3250
+ fsutils.USER_DATA_PATH / macro_file,
3251
+ )
3252
+ except IOError as err:
3253
+ logger.critical(f"Error {err} copying macro file.")
3254
+ try:
3255
+ with open(
3256
+ fsutils.USER_DATA_PATH / macro_file, "r", encoding="utf-8"
3257
+ ) as file_descriptor:
3258
+ for line in file_descriptor:
3233
3259
  mode, fkey, buttonname, cwtext = line.split("|")
3234
3260
  if mode.strip().upper() == "R" and self.pref.get("run_state"):
3235
3261
  self.fkeys[fkey.strip()] = (buttonname.strip(), cwtext.strip())
3236
3262
  if mode.strip().upper() != "R" and not self.pref.get("run_state"):
3237
3263
  self.fkeys[fkey.strip()] = (buttonname.strip(), cwtext.strip())
3238
- except ValueError as err:
3239
- logger.info("read_cw_macros: %s", err)
3264
+ except (IOError, ValueError) as err:
3265
+ logger.info("read_cw_macros: %s", err)
3240
3266
  keys = self.fkeys.keys()
3241
3267
  if "F1" in keys:
3242
3268
  self.F1.setText(f"F1: {self.fkeys['F1'][0]}")
not1mm/lib/database.py CHANGED
@@ -886,28 +886,14 @@ class DataBase:
886
886
  logger.debug("%s", exception)
887
887
  return {}
888
888
 
889
- def fetch_mult1_count(self) -> dict:
889
+ def fetch_mult_count(self, mult: int) -> dict:
890
890
  """return QSO count"""
891
891
  try:
892
892
  with sqlite3.connect(self.database) as conn:
893
893
  conn.row_factory = self.row_factory
894
894
  cursor = conn.cursor()
895
895
  cursor.execute(
896
- f"select count(*) as count from dxlog where IsMultiplier1 = 1 and ContestNR = {self.current_contest};"
897
- )
898
- return cursor.fetchone()
899
- except sqlite3.OperationalError as exception:
900
- logger.debug("%s", exception)
901
- return {}
902
-
903
- def fetch_mult2_count(self) -> dict:
904
- """return QSO count"""
905
- try:
906
- with sqlite3.connect(self.database) as conn:
907
- conn.row_factory = self.row_factory
908
- cursor = conn.cursor()
909
- cursor.execute(
910
- f"select count(*) as count from dxlog where IsMultiplier2 = 1 and ContestNR = {self.current_contest};"
896
+ f"select count(*) as count from dxlog where IsMultiplier{mult} = 1 and ContestNR = {self.current_contest};"
911
897
  )
912
898
  return cursor.fetchone()
913
899
  except sqlite3.OperationalError as exception:
not1mm/lib/version.py CHANGED
@@ -1,3 +1,3 @@
1
1
  """It's the version"""
2
2
 
3
- __version__ = "24.8.27"
3
+ __version__ = "24.9.3"
@@ -8,6 +8,7 @@ import logging
8
8
  from pathlib import Path
9
9
  from PyQt6 import QtWidgets
10
10
 
11
+ from not1mm.lib.ham_utility import get_logged_band
11
12
  from not1mm.lib.plugin_common import gen_adif, get_points
12
13
  from not1mm.lib.version import __version__
13
14
 
@@ -114,7 +115,7 @@ def points(self):
114
115
  _mode = self.contact.get("Mode", "")
115
116
  if _mode in "SSB, USB, LSB, FM, AM":
116
117
  return 1
117
- if _mode in "CW, RTTY":
118
+ if _mode in "CW, RTTY, FT8":
118
119
  return 2
119
120
  return 0
120
121
 
@@ -295,8 +296,10 @@ def cabrillo(self):
295
296
  for contact in log:
296
297
  the_date_and_time = contact.get("TS", "")
297
298
  themode = contact.get("Mode", "")
298
- if themode == "LSB" or themode == "USB":
299
+ if themode in ("LSB", "USB"):
299
300
  themode = "PH"
301
+ if themode in ("FT8", "FT4"):
302
+ themode = "DG"
300
303
  frequency = str(int(contact.get("Freq", "0"))).rjust(5)
301
304
 
302
305
  loggeddate = the_date_and_time[:10]
@@ -362,7 +365,15 @@ def ft8_handler(the_packet: dict):
362
365
  ALTEREGO.contact["RCV"] = ALTEREGO.receive.text()
363
366
  ALTEREGO.contact["Exchange1"] = the_packet.get("CLASS", "ERR")
364
367
  ALTEREGO.contact["Sect"] = the_packet.get("ARRL_SECT", "ERR")
365
- ALTEREGO.contact["Mode"] = "FT8"
368
+ ALTEREGO.contact["Mode"] = the_packet.get("MODE", "ERR")
369
+ ALTEREGO.contact["Freq"] = round(float(the_packet.get("FREQ", "0.0")) * 1000, 2)
370
+ ALTEREGO.contact["QSXFreq"] = round(
371
+ float(the_packet.get("FREQ", "0.0")) * 1000, 2
372
+ )
373
+ ALTEREGO.contact["Band"] = get_logged_band(
374
+ str(int(float(the_packet.get("FREQ", "0.0")) * 1000000))
375
+ )
366
376
  ALTEREGO.other_1.setText(the_packet.get("CLASS", "ERR"))
367
377
  ALTEREGO.other_2.setText(the_packet.get("ARRL_SECT", "ERR"))
368
378
  print(f"\n{ALTEREGO.contact=}\n")
379
+ ALTEREGO.save_contact()
@@ -177,7 +177,7 @@ def points(self):
177
177
 
178
178
  def show_mults(self):
179
179
  """Return display string for mults"""
180
- result = self.database.fetch_mult1_count()
180
+ result = self.database.fetch_mult_count(1)
181
181
  count = result.get("count", 0)
182
182
  return count
183
183
 
@@ -198,7 +198,7 @@ def calc_score(self):
198
198
  if score is None:
199
199
  score = "0"
200
200
  contest_points = int(score)
201
- result = self.database.fetch_mult1_count()
201
+ result = self.database.fetch_mult_count(1)
202
202
  mults = int(result.get("count", 0))
203
203
  return contest_points * mults
204
204
  return 0
@@ -177,7 +177,7 @@ def points(self):
177
177
 
178
178
  def show_mults(self):
179
179
  """Return display string for mults"""
180
- result = self.database.fetch_mult1_count()
180
+ result = self.database.fetch_mult_count(1)
181
181
  count = result.get("count", 0)
182
182
  return count
183
183
 
@@ -198,7 +198,7 @@ def calc_score(self):
198
198
  if score is None:
199
199
  score = "0"
200
200
  contest_points = int(score)
201
- result = self.database.fetch_mult1_count()
201
+ result = self.database.fetch_mult_count(1)
202
202
  mults = int(result.get("count", 0))
203
203
  return contest_points * mults
204
204
  return 0
@@ -250,8 +250,8 @@ def points(self):
250
250
 
251
251
  def show_mults(self):
252
252
  """Return display string for mults"""
253
- return int(self.database.fetch_mult1_count().get("count", 0)) + int(
254
- self.database.fetch_mult2_count().get("count", 0)
253
+ return int(self.database.fetch_mult_count(1).get("count", 0)) + int(
254
+ self.database.fetch_mult_count(2).get("count", 0)
255
255
  )
256
256
 
257
257
 
@@ -0,0 +1,502 @@
1
+ """
2
+ REF Contest, CW
3
+ Status: Active
4
+ Geographic Focus: France + overseas territories
5
+ Participation: Worldwide
6
+ Awards: Worldwide
7
+ Mode: CW
8
+ Bands: 80, 40, 20, 15, 10m
9
+ Classes: Single Op All Band
10
+ Single Op Single Band
11
+ Multi-Single
12
+ Club
13
+ SWL
14
+ Max power: HP: >100 Watts
15
+ LP: 100 Watts
16
+ QRP: 5 Watts
17
+
18
+ Exchange: French: RST + Department/Prefix
19
+ non-French: RST + Serial No.
20
+
21
+ Work stations: Once per band
22
+
23
+ QSO Points: French: 6 points per QSO with French station same continent
24
+ French: 15 points per QSO with French station on different continent
25
+ French: 1 point per QSO with non-French station same continent
26
+ French: 2 points per QSO with non-French station on different continent
27
+ non-French: 1 point per QSO with French station same continent
28
+ non-French: 3 points per QSO with French station on different continent
29
+
30
+ Multipliers: French/Corsica departments once per band
31
+ French overseas prefixes once per band
32
+ non-French DXCC countries once per band (available only to French stations)
33
+
34
+ Score Calculation: Total score = total QSO points x total mults
35
+
36
+ Upload log at: https://concours.r-e-f.org/contest/logs/upload-form/
37
+ Find rules at: https://concours.r-e-f.org/reglements/actuels/reg_cdfhfdx.pdf
38
+ Cabrillo name: REF-CW
39
+ Cabrillo name aliases: REF
40
+ """
41
+
42
+ import datetime
43
+ import logging
44
+ import platform
45
+
46
+ from pathlib import Path
47
+
48
+ from PyQt6 import QtWidgets
49
+
50
+ from not1mm.lib.plugin_common import gen_adif, get_points
51
+
52
+ from not1mm.lib.version import __version__
53
+
54
+ logger = logging.getLogger(__name__)
55
+
56
+ EXCHANGE_HINT = "Canton or #"
57
+
58
+ name = "French REF DX contest - CW"
59
+ cabrillo_name = "REF-CW"
60
+ mode = "CW" # CW SSB BOTH RTTY
61
+
62
+ columns = [
63
+ "YYYY-MM-DD HH:MM:SS",
64
+ "Call",
65
+ "Freq",
66
+ "Mode",
67
+ "Snt",
68
+ "Rcv",
69
+ "SentNr",
70
+ "RcvNr",
71
+ "M1",
72
+ "M2",
73
+ "PTS",
74
+ ]
75
+
76
+ advance_on_space = [True, True, True, True, True]
77
+
78
+ # 1 once per contest, 2 work each band, 3 each band/mode, 4 no dupe checking
79
+ dupe_type = 2
80
+
81
+
82
+ def init_contest(self):
83
+ """setup plugin"""
84
+ set_tab_next(self)
85
+ set_tab_prev(self)
86
+ interface(self)
87
+ self.next_field = self.other_2
88
+
89
+
90
+ def interface(self):
91
+ """Setup user interface"""
92
+ self.field1.show()
93
+ self.field2.show()
94
+ self.field3.show()
95
+ self.field4.show()
96
+ label = self.field3.findChild(QtWidgets.QLabel)
97
+ label.setText("Sent")
98
+ self.field3.setAccessibleName("Sent")
99
+ label = self.field4.findChild(QtWidgets.QLabel)
100
+ label.setText("Dep/Pfx/SN")
101
+ self.field4.setAccessibleName("Department, Prefix or SN")
102
+
103
+
104
+ def reset_label(self):
105
+ """reset label after field cleared"""
106
+
107
+
108
+ def set_tab_next(self):
109
+ """Set TAB Advances"""
110
+ self.tab_next = {
111
+ self.callsign: self.field3.findChild(QtWidgets.QLineEdit),
112
+ self.field1.findChild(QtWidgets.QLineEdit): self.field3.findChild(
113
+ QtWidgets.QLineEdit
114
+ ),
115
+ self.field2.findChild(QtWidgets.QLineEdit): self.field3.findChild(
116
+ QtWidgets.QLineEdit
117
+ ),
118
+ self.field3.findChild(QtWidgets.QLineEdit): self.field4.findChild(
119
+ QtWidgets.QLineEdit
120
+ ),
121
+ self.field4.findChild(QtWidgets.QLineEdit): self.callsign,
122
+ }
123
+
124
+
125
+ def set_tab_prev(self):
126
+ """Set TAB Advances"""
127
+ self.tab_prev = {
128
+ self.callsign: self.field4.findChild(QtWidgets.QLineEdit),
129
+ self.field1.findChild(QtWidgets.QLineEdit): self.callsign,
130
+ self.field2.findChild(QtWidgets.QLineEdit): self.callsign,
131
+ self.field3.findChild(QtWidgets.QLineEdit): self.callsign,
132
+ self.field4.findChild(QtWidgets.QLineEdit): self.field3.findChild(
133
+ QtWidgets.QLineEdit
134
+ ),
135
+ }
136
+
137
+
138
+ def set_contact_vars(self):
139
+ """
140
+ Contest Specific
141
+ Multipliers:
142
+ French/Corsica departments once per band
143
+ French overseas prefixes once per band
144
+ non-French DXCC countries once per band (available only to French stations)
145
+ """
146
+ self.contact["SNT"] = self.sent.text()
147
+ self.contact["RCV"] = self.receive.text()
148
+ self.contact["SentNr"] = self.other_1.text().upper()
149
+ self.contact["NR"] = self.other_2.text().upper()
150
+
151
+ self.contact["IsMultiplier1"] = 0
152
+ self.contact["IsMultiplier2"] = 0
153
+
154
+ if (
155
+ self.contact.get("CountryPrefix", "") == "F"
156
+ and self.contact.get("NR", "").isalpha()
157
+ ):
158
+ canton = self.contact.get("NR", "").upper()
159
+ band = self.contact.get("Band", "")
160
+ query = (
161
+ f"select count(*) as canton_count from dxlog where "
162
+ f"NR = '{canton}' "
163
+ f"and Band = '{band}' "
164
+ f"and ContestNR = {self.pref.get('contest', '1')};"
165
+ )
166
+ result = self.database.exec_sql(query)
167
+ count = int(result.get("canton_count", 0))
168
+ if count == 0:
169
+ self.contact["IsMultiplier1"] = 1
170
+
171
+ if self.contact.get("CountryPrefix", ""):
172
+ dxcc = self.contact.get("CountryPrefix", "")
173
+ band = self.contact.get("Band", "")
174
+ query = (
175
+ f"select count(*) as dxcc_count from dxlog where "
176
+ f"CountryPrefix = '{dxcc}' "
177
+ f"and Band = '{band}' "
178
+ f"and ContestNR = {self.pref.get('contest', '1')};"
179
+ )
180
+ result = self.database.exec_sql(query)
181
+ if not result.get("dxcc_count", ""):
182
+ self.contact["IsMultiplier2"] = 1
183
+
184
+
185
+ def predupe(self):
186
+ """called after callsign entered"""
187
+
188
+
189
+ def prefill(self):
190
+ """Fill SentNR"""
191
+ field = self.field3.findChild(QtWidgets.QLineEdit)
192
+ sent_sxchange_setting = self.contest_settings.get("SentExchange", "")
193
+ if sent_sxchange_setting.strip() == "#":
194
+ result = self.database.get_serial()
195
+ serial_nr = str(result.get("serial_nr", "1")).zfill(3)
196
+ if serial_nr == "None":
197
+ serial_nr = "001"
198
+ if len(field.text()) == 0:
199
+ field.setText(serial_nr)
200
+ else:
201
+ field.setText(sent_sxchange_setting)
202
+
203
+
204
+ def points(self):
205
+ """
206
+ Scoring:
207
+ French: 6 points per QSO with French station same continent
208
+ French: 15 points per QSO with French station on different continent
209
+ French: 1 point per QSO with non-French station same continent
210
+ French: 2 points per QSO with non-French station on different continent
211
+ non-French: 1 point per QSO with French station same continent
212
+ non-French: 3 points per QSO with French station on different continent
213
+
214
+ self.contact["CountryPrefix"]
215
+ self.contact["Continent"]
216
+ """
217
+
218
+ # Just incase the cty lookup fails
219
+ my_country = None
220
+ my_continent = None
221
+ their_continent = None
222
+ their_country = None
223
+
224
+ result = self.cty_lookup(self.station.get("Call", ""))
225
+ if result:
226
+ for item in result.items():
227
+ my_country = item[1].get("entity", "")
228
+ my_continent = item[1].get("continent", "")
229
+ result = self.cty_lookup(self.contact.get("Call", ""))
230
+ if result:
231
+ for item in result.items():
232
+ their_country = item[1].get("entity", "")
233
+ their_continent = item[1].get("continent", "")
234
+
235
+ if my_country == "France":
236
+ if their_country == "France":
237
+ if my_continent == their_continent:
238
+ return 6
239
+ else:
240
+ return 15
241
+ else:
242
+ if my_continent == their_continent:
243
+ return 1
244
+ else:
245
+ return 2
246
+ else:
247
+ if their_country == "France":
248
+ if their_continent == my_continent:
249
+ return 1
250
+ else:
251
+ return 3
252
+
253
+ return 0
254
+
255
+
256
+ def show_mults(self):
257
+ """Return display string for mults"""
258
+ return int(self.database.fetch_mult_count(1).get("count", 0)) + int(
259
+ self.database.fetch_mult_count(2).get("count", 0)
260
+ )
261
+
262
+
263
+ def show_qso(self):
264
+ """Return qso count"""
265
+ result = self.database.fetch_qso_count()
266
+ if result:
267
+ return int(result.get("qsos", 0))
268
+ return 0
269
+
270
+
271
+ def calc_score(self):
272
+ """Return calculated score"""
273
+ result = self.database.fetch_points()
274
+ if result is not None:
275
+ score = result.get("Points", "0")
276
+ if score is None:
277
+ score = "0"
278
+ contest_points = int(score)
279
+ mults = show_mults(self)
280
+ return contest_points * mults
281
+ return 0
282
+
283
+
284
+ def recalculate_mults(self):
285
+ """Recalculates multipliers after change in logged qso."""
286
+
287
+ all_contacts = self.database.fetch_all_contacts_asc()
288
+ for contact in all_contacts:
289
+
290
+ contact["IsMultiplier1"] = 0
291
+ contact["IsMultiplier2"] = 0
292
+
293
+ time_stamp = contact.get("TS", "")
294
+ canton = contact.get("NR", "")
295
+ dxcc = contact.get("CountryPrefix", "")
296
+ band = contact.get("Band", "")
297
+ if dxcc == "HB" and canton.isalpha():
298
+ query = (
299
+ f"select count(*) as canton_count from dxlog where TS < '{time_stamp}' "
300
+ f"and NR = '{canton.upper()}' "
301
+ f"and Band = '{band}' "
302
+ f"and ContestNR = {self.pref.get('contest', '1')};"
303
+ )
304
+ result = self.database.exec_sql(query)
305
+ count = int(result.get("canton_count", 0))
306
+ if count == 0:
307
+ contact["IsMultiplier1"] = 1
308
+
309
+ if dxcc:
310
+ query = (
311
+ f"select count(*) as dxcc_count from dxlog where TS < '{time_stamp}' "
312
+ f"and CountryPrefix = '{dxcc}' "
313
+ f"and Band = '{band}' "
314
+ f"and ContestNR = {self.pref.get('contest', '1')};"
315
+ )
316
+ result = self.database.exec_sql(query)
317
+ if not result.get("dxcc_count", ""):
318
+ contact["IsMultiplier2"] = 1
319
+
320
+ self.database.change_contact(contact)
321
+ cmd = {}
322
+ cmd["cmd"] = "UPDATELOG"
323
+ cmd["station"] = platform.node()
324
+ self.multicast_interface.send_as_json(cmd)
325
+
326
+
327
+ def adif(self):
328
+ """Call the generate ADIF function"""
329
+ gen_adif(self, cabrillo_name, "HELVETIA")
330
+
331
+
332
+ def cabrillo(self):
333
+ """Generates Cabrillo file. Maybe."""
334
+ # https://www.cqwpx.com/cabrillo.htm
335
+ logger.debug("******Cabrillo*****")
336
+ logger.debug("Station: %s", f"{self.station}")
337
+ logger.debug("Contest: %s", f"{self.contest_settings}")
338
+ now = datetime.datetime.now()
339
+ date_time = now.strftime("%Y-%m-%d_%H-%M-%S")
340
+ filename = (
341
+ str(Path.home())
342
+ + "/"
343
+ + f"{self.station.get('Call', '').upper()}_{cabrillo_name}_{date_time}.log"
344
+ )
345
+ logger.debug("%s", filename)
346
+ log = self.database.fetch_all_contacts_asc()
347
+ try:
348
+ with open(filename, "w", encoding="ascii") as file_descriptor:
349
+ print("START-OF-LOG: 3.0", end="\r\n", file=file_descriptor)
350
+ print(
351
+ f"CREATED-BY: Not1MM v{__version__}",
352
+ end="\r\n",
353
+ file=file_descriptor,
354
+ )
355
+ print(
356
+ f"CONTEST: {cabrillo_name}",
357
+ end="\r\n",
358
+ file=file_descriptor,
359
+ )
360
+ if self.station.get("Club", ""):
361
+ print(
362
+ f"CLUB: {self.station.get('Club', '').upper()}",
363
+ end="\r\n",
364
+ file=file_descriptor,
365
+ )
366
+ print(
367
+ f"CALLSIGN: {self.station.get('Call','')}",
368
+ end="\r\n",
369
+ file=file_descriptor,
370
+ )
371
+ print(
372
+ f"LOCATION: {self.station.get('ARRLSection', '')}",
373
+ end="\r\n",
374
+ file=file_descriptor,
375
+ )
376
+ # print(
377
+ # f"ARRL-SECTION: {self.pref.get('section', '')}",
378
+ # end="\r\n",
379
+ # file=file_descriptor,
380
+ # )
381
+ print(
382
+ f"CATEGORY-OPERATOR: {self.contest_settings.get('OperatorCategory','')}",
383
+ end="\r\n",
384
+ file=file_descriptor,
385
+ )
386
+ print(
387
+ f"CATEGORY-ASSISTED: {self.contest_settings.get('AssistedCategory','')}",
388
+ end="\r\n",
389
+ file=file_descriptor,
390
+ )
391
+ print(
392
+ f"CATEGORY-BAND: {self.contest_settings.get('BandCategory','')}",
393
+ end="\r\n",
394
+ file=file_descriptor,
395
+ )
396
+ print(
397
+ f"CATEGORY-MODE: {self.contest_settings.get('ModeCategory','')}",
398
+ end="\r\n",
399
+ file=file_descriptor,
400
+ )
401
+ print(
402
+ f"CATEGORY-TRANSMITTER: {self.contest_settings.get('TransmitterCategory','')}",
403
+ end="\r\n",
404
+ file=file_descriptor,
405
+ )
406
+ if self.contest_settings.get("OverlayCategory", "") != "N/A":
407
+ print(
408
+ f"CATEGORY-OVERLAY: {self.contest_settings.get('OverlayCategory','')}",
409
+ end="\r\n",
410
+ file=file_descriptor,
411
+ )
412
+ print(
413
+ f"GRID-LOCATOR: {self.station.get('GridSquare','')}",
414
+ end="\r\n",
415
+ file=file_descriptor,
416
+ )
417
+ # print(
418
+ # f"CATEGORY: {None}",
419
+ # end="\r\n",
420
+ # file=file_descriptor,
421
+ # )
422
+ print(
423
+ f"CATEGORY-POWER: {self.contest_settings.get('PowerCategory','')}",
424
+ end="\r\n",
425
+ file=file_descriptor,
426
+ )
427
+
428
+ print(
429
+ f"CLAIMED-SCORE: {calc_score(self)}",
430
+ end="\r\n",
431
+ file=file_descriptor,
432
+ )
433
+ ops = f"@{self.station.get('Call','')}"
434
+ list_of_ops = self.database.get_ops()
435
+ for op in list_of_ops:
436
+ ops += f", {op.get('Operator', '')}"
437
+ print(
438
+ f"OPERATORS: {ops}",
439
+ end="\r\n",
440
+ file=file_descriptor,
441
+ )
442
+ print(
443
+ f"NAME: {self.station.get('Name', '')}",
444
+ end="\r\n",
445
+ file=file_descriptor,
446
+ )
447
+ print(
448
+ f"ADDRESS: {self.station.get('Street1', '')}",
449
+ end="\r\n",
450
+ file=file_descriptor,
451
+ )
452
+ print(
453
+ f"ADDRESS-CITY: {self.station.get('City', '')}",
454
+ end="\r\n",
455
+ file=file_descriptor,
456
+ )
457
+ print(
458
+ f"ADDRESS-STATE-PROVINCE: {self.station.get('State', '')}",
459
+ end="\r\n",
460
+ file=file_descriptor,
461
+ )
462
+ print(
463
+ f"ADDRESS-POSTALCODE: {self.station.get('Zip', '')}",
464
+ end="\r\n",
465
+ file=file_descriptor,
466
+ )
467
+ print(
468
+ f"ADDRESS-COUNTRY: {self.station.get('Country', '')}",
469
+ end="\r\n",
470
+ file=file_descriptor,
471
+ )
472
+ print(
473
+ f"EMAIL: {self.station.get('Email', '')}",
474
+ end="\r\n",
475
+ file=file_descriptor,
476
+ )
477
+ for contact in log:
478
+ the_date_and_time = contact.get("TS", "")
479
+ themode = contact.get("Mode", "")
480
+ if themode == "LSB" or themode == "USB":
481
+ themode = "PH"
482
+ frequency = str(int(contact.get("Freq", "0"))).rjust(5)
483
+
484
+ loggeddate = the_date_and_time[:10]
485
+ loggedtime = the_date_and_time[11:13] + the_date_and_time[14:16]
486
+ print(
487
+ f"QSO: {frequency} {themode} {loggeddate} {loggedtime} "
488
+ f"{contact.get('StationPrefix', '').ljust(13)} "
489
+ f"{str(contact.get('SNT', '')).ljust(3)} "
490
+ f"{str(contact.get('SentNr', '')).ljust(6)} "
491
+ f"{contact.get('Call', '').ljust(13)} "
492
+ f"{str(contact.get('RCV', '')).ljust(3)} "
493
+ f"{str(contact.get('NR', '')).ljust(6)}",
494
+ end="\r\n",
495
+ file=file_descriptor,
496
+ )
497
+ print("END-OF-LOG:", end="\r\n", file=file_descriptor)
498
+ self.show_message_box(f"Cabrillo saved to: {filename}")
499
+ except IOError as exception:
500
+ logger.critical("cabrillo: IO error: %s, writing to %s", exception, filename)
501
+ self.show_message_box(f"Error saving Cabrillo: {exception} {filename}")
502
+ return
not1mm/test.py CHANGED
@@ -1,13 +1,10 @@
1
- #!/usr/bin/env python3
2
- """
3
- NOT1MM Logger
4
- Purpose: test alternative sound playing interface
5
- """
6
- # pylint: disable=unused-import, c-extension-no-member, no-member, invalid-name, too-many-lines, no-name-in-module
7
- # pylint: disable=logging-fstring-interpolation, logging-not-lazy, line-too-long, bare-except
1
+ class Somefunc:
2
+ def __init__(self):
3
+ """weee"""
8
4
 
9
- from not1mm.lib.ham_utility import parse_udc
5
+ def testfunc():
6
+ """wooo"""
10
7
 
11
- filename = "./testing/K1USNSSTOP.udc"
12
8
 
13
- print(f"{parse_udc(filename)}")
9
+ thetest = Somefunc()
10
+ print(f"{hasattr(thetest, "ft8_catcher")}")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: not1mm
3
- Version: 24.8.27
3
+ Version: 24.9.3
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
@@ -100,6 +100,7 @@ Requires-Dist: Levenshtein
100
100
  - [Cluster](#cluster)
101
101
  - [N1MM Packets](#n1mm-packets)
102
102
  - [Bands](#bands)
103
+ - [Logging WSJT-X FT8/FT4 contacts](#logging-wsjt-x-ft8ft4-contacts)
103
104
  - [Sending CW](#sending-cw)
104
105
  - [Editing macro keys](#editing-macro-keys)
105
106
  - [Macro substitutions](#macro-substitutions)
@@ -225,11 +226,7 @@ generated, 'cause I'm lazy, list of those who've submitted PR's.
225
226
 
226
227
  ## Recent Changes
227
228
 
228
- - [24-8-27] Added Helvetia contest.
229
- - [24-8-22] Add loading splash screen.
230
- - [24-8-20] Added K1USN Slow Speed Test
231
- - [24-8-17-1] Did an oops. Fixed the oops.
232
- - [24-8-17] Removed some cruft. Made dockable widgets not floatable since Wayland breaks this.
229
+ - [24-9-3] Added WSJT-X FT8 mode contacts to ARRL Field Day.
233
230
 
234
231
  See [CHANGELOG.md](CHANGELOG.md) for prior changes.
235
232
 
@@ -250,7 +247,7 @@ not1mm requires:
250
247
  - PyQt6
251
248
  - libportaudio2
252
249
  - libxcb-cursor0 (maybe... Depends on the distro)
253
-
250
+
254
251
  You should install these through your distribution's package manager before continuing.
255
252
 
256
253
  ### Common installation recipes for Ubuntu and Fedora
@@ -337,9 +334,7 @@ pamac build not1mm-git
337
334
  #### Mint
338
335
 
339
336
  ```bash
340
- sudo apt install python3-pip
341
- sudo apt install pipx
342
- sudo apt install libxcb-cursor0
337
+ sudo apt install python3-pip pipx libxcb-cursor0
343
338
  pipx install not1mm
344
339
  pipx ensurepath
345
340
  ```
@@ -608,6 +603,13 @@ appear. Those without will not.
608
603
 
609
604
  ![Bands Configuration Screen](https://github.com/mbridak/not1mm/raw/master/pic/configure_bands.png)
610
605
 
606
+ ## Logging WSJT-X FT8/FT4 contacts
607
+
608
+ **Currently only working for ARRL Field Day.**
609
+
610
+ not1mm listens for WSJT-X UDP traffic on the default localhost:2237. No setup is
611
+ needed to be done on not1mm's side.
612
+
611
613
  ## Sending CW
612
614
 
613
615
  Other than sending CW by hand, you can also send predefined CW text messages by
@@ -1,11 +1,11 @@
1
1
  not1mm/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- not1mm/__main__.py,sha256=QJRnfWEcHEQ40ESeIeEtPB6lOwMySP8r6Ao-4TBqLoc,121832
2
+ not1mm/__main__.py,sha256=xmqEIER571T8ONHjL-65TCuQ-PnUpDqH-nQFIutvUOk,122905
3
3
  not1mm/bandmap.py,sha256=1b5tXCfGTnpqqn6hPNt7zRA8SmuwSXzSwNHZXhCRt70,31434
4
4
  not1mm/checkwindow.py,sha256=aI-nr8OF90IWV7R_XRdmitvBJ9M85evCs72HoU3Jnvc,10374
5
5
  not1mm/fsutils.py,sha256=ukHKxKTeNKxKwqRaJjtzRShL4X5Xl0jRBbADyy3Ifp8,1701
6
6
  not1mm/logwindow.py,sha256=pwhiwolmGnW01LF4sjlu3ywLsgfxL6KuGuKuYKYmgeY,44403
7
7
  not1mm/radio.py,sha256=eiB04LPMPBeMrBRI021Z7VXth66EOYb0Ujh11T9877c,3362
8
- not1mm/test.py,sha256=ev_YgRFBurFw7_W1SXn509XyUkC6tWchLF5w_c8Z-ow,422
8
+ not1mm/test.py,sha256=H_5hAobuNebV0-PZk_CeX0_MO2FgGhMiauVBfNhfNTg,166
9
9
  not1mm/vfo.py,sha256=IvmUQYMIPzLJw_BHQGis4J_IEW-vlBtdfxZLXPh7OzI,12335
10
10
  not1mm/voice_keying.py,sha256=sA3gw5_k7kShTg2qhG7HkKDM5M6KheJVRkAc_C7mxDk,3006
11
11
  not1mm/data/JetBrainsMono-Regular.ttf,sha256=UOHctAKY_PzCGh7zy-6f6egnCcSK0wzmF0csBqO9lDY,203952
@@ -94,7 +94,7 @@ not1mm/lib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
94
94
  not1mm/lib/about.py,sha256=sWycfGcruN3SaEe4JmaJ61K6D8Itq0WxpUYT-lEcmYM,416
95
95
  not1mm/lib/cat_interface.py,sha256=aazvNTSeZAROq3KL8gPx-I95iVez2IiIOSk22qeqVCQ,19502
96
96
  not1mm/lib/cwinterface.py,sha256=Q8p3pScOHczZ8ptICfH1Yu6rCEwQJLgrNwYMN76B2i8,3389
97
- not1mm/lib/database.py,sha256=rX1wOoSiSuX7CCFghvD_yc4q8E0FHwZp1k7Z2iVNYv8,43680
97
+ not1mm/lib/database.py,sha256=0dx6SX40OiPFent2ePAGsFj_XmOVMNBHaqd0QtAiewY,43129
98
98
  not1mm/lib/edit_contact.py,sha256=Ki9bGPpqyQQBB1cU8VIBDCal3lbXeQ6qxhzklmhE2_w,353
99
99
  not1mm/lib/edit_macro.py,sha256=raKWBwsHInj5EUKmvyLQ6gqc3ZFDlstsD3xqoM4PC8E,517
100
100
  not1mm/lib/edit_opon.py,sha256=j3qJ1aBsQoIOnQ9yiBl3lyeISvKTP0I_rtBYBPAfgeI,359
@@ -110,7 +110,7 @@ not1mm/lib/plugin_common.py,sha256=wuG7B0OJx9zYc5Gew3fdt_lNyan8Ul9KNlPQ7PDKGsU,9
110
110
  not1mm/lib/select_contest.py,sha256=WsptLuwkouIHeocJL3oZ6-eUfEnhpwdc-x7eMZ_TIVM,359
111
111
  not1mm/lib/settings.py,sha256=MWiKXbasaFbzeHTjfzTaTqbCBrIijudP_-0a5jNmUAA,9265
112
112
  not1mm/lib/super_check_partial.py,sha256=p5l3u2ZOCBtlWgbvskC50FpuoaIpR07tfC6zTdRWbh4,2334
113
- not1mm/lib/version.py,sha256=1KiLcfpXjS8-V6c6tXOR_NoOKat-5IpaUjb1wrD5Q8g,48
113
+ not1mm/lib/version.py,sha256=3nHzm4TY2JdIkrbl-vZhxO25Akrnx3rGDTzqroFbYJg,47
114
114
  not1mm/lib/versiontest.py,sha256=8vDNptuBBunn-1IGkjNaquehqBYUJyjrPSF8Igmd4_Y,1286
115
115
  not1mm/plugins/10_10_fall_cw.py,sha256=IttjX1yy4nDdACGsiYlPteFG8eVseX_WtoFio6bqHE8,10953
116
116
  not1mm/plugins/10_10_spring_cw.py,sha256=ThCptdM3dX4ywhoy2JRcOEyHSqcJolFaT7O_PYzM1Mg,10958
@@ -120,7 +120,7 @@ not1mm/plugins/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
120
120
  not1mm/plugins/arrl_10m.py,sha256=EyNRb3Sm0Qb-GVm0p05EnadlHisVh7y-sKTBP2ddMLo,13768
121
121
  not1mm/plugins/arrl_dx_cw.py,sha256=LVnYDNFEUiIpQ1TlhOCcRK7JNwH5XPO5WzUoApSUMTY,13802
122
122
  not1mm/plugins/arrl_dx_ssb.py,sha256=fUFzuNbCAfA5sQSYm8ISV3P9Z_2xnuKeOdO6E66zn1k,13805
123
- not1mm/plugins/arrl_field_day.py,sha256=bMBrww0-zBnkrLY3JsG1tXumeWGDWgLi1QZiPtriFqE,11387
123
+ not1mm/plugins/arrl_field_day.py,sha256=-HaAp14eHJsiwo1vNnHjVQ7cbDyZ7NYl0Z3SEUx0fek,11903
124
124
  not1mm/plugins/arrl_rtty_ru.py,sha256=hKUS4isjdXo3EYxQrsqsDupPp2chW8fpoWj0T1pTgJ4,7994
125
125
  not1mm/plugins/arrl_ss_cw.py,sha256=4yN68xZMuJRaSUfdiY4hafC07A3lifl5q6DEUZ-oYPQ,13080
126
126
  not1mm/plugins/arrl_ss_phone.py,sha256=Yzc5sNjrY8TlnozbYF6k8hbEamyDuUAD_3-BNqHgXqY,13068
@@ -128,15 +128,15 @@ not1mm/plugins/arrl_vhf_jan.py,sha256=jkX9v2HHsjoawtTKiy8X0Td_HtfGQVT3MapJwpmM1q
128
128
  not1mm/plugins/arrl_vhf_jun.py,sha256=_9h6joxVCRCoFuNnyTOlnonafvrMv7H0eBERhYCoGug,11591
129
129
  not1mm/plugins/arrl_vhf_sep.py,sha256=XbS1OSAfGaYXFaTAP2HRyVvVeRD_Z6cefPc1_mOLJKw,11591
130
130
  not1mm/plugins/canada_day.py,sha256=OVpcCl1Chse_zLHf6PayTrgawWM4W-pmrTw40Al-o9s,11998
131
- not1mm/plugins/cq_160_cw.py,sha256=e9ajqIzDRteSI7zQaFpXUJ_6SNvBlIuNq213wadReUs,14139
132
- not1mm/plugins/cq_160_ssb.py,sha256=7MuMC_AVlj9ds_ohvEU4Tts-z9eTvnsSIENSMFKQCBc,14182
131
+ not1mm/plugins/cq_160_cw.py,sha256=5s6rIZdJEnmWe1SI06BEyz7p5vP0N2n9mI4l_mZ0icw,14139
132
+ not1mm/plugins/cq_160_ssb.py,sha256=zIwSMAjHSt6W2edrDzVbyTf860JowHoFkU9BKO8Enag,14182
133
133
  not1mm/plugins/cq_wpx_cw.py,sha256=9aNzAR-KhznIwUlxUFjAi_hbiw_6RrCMwUBk9I2f6Hs,14037
134
134
  not1mm/plugins/cq_wpx_ssb.py,sha256=-hGRovqHR9rfOUnG4LPOoABTb4heH8VAX6rYdJbCqsw,12687
135
135
  not1mm/plugins/cq_ww_cw.py,sha256=ltXFnSXabCOuW70s-WOydgghZTNpztX8TKLpVIV50B4,11194
136
136
  not1mm/plugins/cq_ww_ssb.py,sha256=kt-EQofmCbynX1iXFm9ehffi_TMW25ke8Qi9MiR69ZQ,11199
137
137
  not1mm/plugins/cwt.py,sha256=4xdXN6ZJM5k-6gn0hJzNheWfFlGiqquC2p0ZMEe516M,12818
138
138
  not1mm/plugins/general_logging.py,sha256=t02xtJs601qRICGdrvLs3G9y4GCG9H4AgQNkgA18CYs,3467
139
- not1mm/plugins/helvetia.py,sha256=OtMTOw3-SavrhTNRb_lulTX9BEaNbQdK5lLufowKihY,15432
139
+ not1mm/plugins/helvetia.py,sha256=6aOO4uiLzFFgHA-A3xz6IRdCJpqPOAm0egKxP5Y_Ie0,15432
140
140
  not1mm/plugins/iaru_hf.py,sha256=-ROUo2gBkw3xB89t8bd-4f7_1hROw2VXZXVHLFdB62s,11541
141
141
  not1mm/plugins/icwc_mst.py,sha256=BaUP2kzrT2D27un_WLGT4HCTTi1e7CNYC4NHcC_9r74,11842
142
142
  not1mm/plugins/jidx_cw.py,sha256=9oV4hDxMiGXa9wuYUNYOCsr-mz8LYB-4WMHBN8u2dFk,12153
@@ -145,11 +145,12 @@ not1mm/plugins/k1usn_sst.py,sha256=2Nu7SRiQeUG3mL9CLKReRLh8vKsNbWcizMgv9MTLkrg,1
145
145
  not1mm/plugins/naqp_cw.py,sha256=c0MuKqfkIxiYFvv2z7vqrBz3m9FSnSYkPK3f-DdkTIA,12632
146
146
  not1mm/plugins/naqp_ssb.py,sha256=VLWVrSzI0UP1AhSXYn61eZ7or1rz6a_pS_xCKfgS4Jw,11595
147
147
  not1mm/plugins/phone_weekly_test.py,sha256=fLpMe03WB9_KgRl6vMgQQt_aktFdqfNt2Sw81CTRAUs,12325
148
+ not1mm/plugins/ref_cw.py,sha256=aWjHHkqIKutjRUtzh09y5haFfnZK9poRQDWRQMDRxxU,16326
148
149
  not1mm/plugins/stew_perry_topband.py,sha256=CKBQbYl4ETxhXJd2dma4fg_C5pag_s7Nf61SCztZtqE,10668
149
150
  not1mm/plugins/winter_field_day.py,sha256=4rcfRtobwjHO6BNL3WOTHzBmyyeuX79BNGBG8PfjrI8,10238
150
- not1mm-24.8.27.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
151
- not1mm-24.8.27.dist-info/METADATA,sha256=PC9MtwYLjp66VOkyLGbCkMNw4Eb1Z5PoF3AkrKQCh6I,29688
152
- not1mm-24.8.27.dist-info/WHEEL,sha256=UvcQYKBHoFqaQd6LKyqHw9fxEolWLQnlzP0h_LgJAfI,91
153
- not1mm-24.8.27.dist-info/entry_points.txt,sha256=pMcZk_0dxFgLkcUkF0Q874ojpwOmF3OL6EKw9LgvocM,47
154
- not1mm-24.8.27.dist-info/top_level.txt,sha256=0YmTxEcDzQlzXub-lXASvoLpg_mt1c2thb5cVkDf5J4,7
155
- not1mm-24.8.27.dist-info/RECORD,,
151
+ not1mm-24.9.3.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
152
+ not1mm-24.9.3.dist-info/METADATA,sha256=11Kg9639B3UOSQ5rFKE5RgPZLY8Ww7rGP0hs4jW3QU8,29734
153
+ not1mm-24.9.3.dist-info/WHEEL,sha256=uCRv0ZEik_232NlR4YDw4Pv3Ajt5bKvMH13NUU7hFuI,91
154
+ not1mm-24.9.3.dist-info/entry_points.txt,sha256=pMcZk_0dxFgLkcUkF0Q874ojpwOmF3OL6EKw9LgvocM,47
155
+ not1mm-24.9.3.dist-info/top_level.txt,sha256=0YmTxEcDzQlzXub-lXASvoLpg_mt1c2thb5cVkDf5J4,7
156
+ not1mm-24.9.3.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (74.0.0)
2
+ Generator: setuptools (74.1.1)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5