not1mm 23.3.24.1__py3-none-any.whl → 23.3.27__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
@@ -206,6 +206,7 @@ class MainWindow(QtWidgets.QMainWindow):
206
206
  self.actionPreferences.triggered.connect(self.preference_selected)
207
207
  self.actionQRZ_Settings.triggered.connect(self.qrz_preference_selected)
208
208
  self.actionGenerate_Cabrillo.triggered.connect(self.generate_cabrillo)
209
+ self.actionGenerate_ADIF.triggered.connect(self.generate_adif)
209
210
  self.actionLog_Window.triggered.connect(self.launch_log_window)
210
211
  self.radioButton_run.clicked.connect(self.run_sp_buttons_clicked)
211
212
  self.radioButton_sp.clicked.connect(self.run_sp_buttons_clicked)
@@ -511,7 +512,7 @@ class MainWindow(QtWidgets.QMainWindow):
511
512
  def save_contact(self):
512
513
  """Save to db"""
513
514
  logger.debug("saving")
514
- if len(self.callsign.text()) < 4:
515
+ if len(self.callsign.text()) < 3:
515
516
  return
516
517
  if not any(char.isdigit() for char in self.callsign.text()):
517
518
  return
@@ -662,7 +663,7 @@ class MainWindow(QtWidgets.QMainWindow):
662
663
 
663
664
  def select_contest(self):
664
665
  """Load contest"""
665
- self.contest = doimp("cq_wpx_ssb")
666
+ self.contest = doimp("cq_wpx_cw")
666
667
  logger.debug("Loaded Contest Name = %s", self.contest.name)
667
668
  self.contest.init_contest(self)
668
669
 
@@ -983,10 +984,12 @@ class MainWindow(QtWidgets.QMainWindow):
983
984
  """Called when text in the callsign field has changed"""
984
985
  text = self.callsign.text()
985
986
  text = text.upper()
986
- stripped_text = text.strip()
987
+ position = self.callsign.cursorPosition()
988
+ stripped_text = text.strip().replace(" ", "")
987
989
  self.callsign.setText(stripped_text)
990
+ self.callsign.setCursorPosition(position)
988
991
 
989
- if text[-1:] == " ":
992
+ if " " in text:
990
993
  if stripped_text == "CW":
991
994
  self.setmode("CW")
992
995
  self.radio_state["mode"] = "CW"
@@ -1112,9 +1115,18 @@ class MainWindow(QtWidgets.QMainWindow):
1112
1115
  """Checks if a callsign is a dupe on current band/mode."""
1113
1116
  band = float(get_logged_band(str(self.radio_state.get("vfoa", 0.0))))
1114
1117
  mode = self.radio_state.get("mode", "")
1115
- debugline = f"Call: {call} Band: {band} Mode: {mode}"
1118
+ debugline = (
1119
+ f"Call: {call} Band: {band} Mode: {mode} Dupetype: {self.contest.dupe_type}"
1120
+ )
1116
1121
  logger.debug("%s", debugline)
1117
- result = self.database.check_dupe_on_band_mode(call, band, mode)
1122
+ if self.contest.dupe_type == 1:
1123
+ result = self.database.check_dupe(call)
1124
+ if self.contest.dupe_type == 2:
1125
+ result = self.database.check_dupe_on_band(call, band)
1126
+ if self.contest.dupe_type == 3:
1127
+ result = self.database.check_dupe_on_band_mode(call, band, mode)
1128
+ if self.contest.dupe_type == 4:
1129
+ result = {"isdupe": False}
1118
1130
  debugline = f"{result}"
1119
1131
  logger.debug("%s", debugline)
1120
1132
  return result.get("isdupe", False)
@@ -1232,135 +1244,16 @@ class MainWindow(QtWidgets.QMainWindow):
1232
1244
  self.F12.setText(f"F12: {self.fkeys['F12'][0]}")
1233
1245
  self.F12.setToolTip(self.fkeys["F12"][1])
1234
1246
 
1247
+ def generate_adif(self):
1248
+ """Generate ADIF"""
1249
+ logger.debug("******ADIF*****")
1250
+ self.contest.adif(self)
1251
+
1235
1252
  def generate_cabrillo(self):
1236
1253
  """Generates Cabrillo file. Maybe."""
1237
1254
  # https://www.cqwpx.com/cabrillo.htm
1238
1255
  logger.debug("******Cabrillo*****")
1239
- filename = (
1240
- str(Path.home())
1241
- + "/"
1242
- + f"{self.pref.get('callsign').upper()}_{self.contest.cabrillo_name}.log"
1243
- )
1244
- logger.debug("%s", filename)
1245
- # self.infobox.setTextColor(QtGui.QColor(211, 215, 207))
1246
- # self.infobox.insertPlainText(f"Saving cabrillo to: {filename}")
1247
- # app.processEvents()
1248
- # bonuses = 0
1249
- log = self.database.fetch_all_contacts_asc()
1250
- # catpower = ""
1251
- try:
1252
- with open(filename, "w", encoding="ascii") as file_descriptor:
1253
- print("START-OF-LOG: 3.0", end="\r\n", file=file_descriptor)
1254
- print(
1255
- f"CREATED-BY: Not1MM v{__version__}",
1256
- end="\r\n",
1257
- file=file_descriptor,
1258
- )
1259
- print(
1260
- f"CONTEST: {self.contest.cabrillo_name}",
1261
- end="\r\n",
1262
- file=file_descriptor,
1263
- )
1264
- print(
1265
- f"CALLSIGN: {self.pref.get('callsign','')}",
1266
- end="\r\n",
1267
- file=file_descriptor,
1268
- )
1269
- print(
1270
- f"LOCATION: {self.pref.get('section', '')}",
1271
- end="\r\n",
1272
- file=file_descriptor,
1273
- )
1274
- # print(
1275
- # f"ARRL-SECTION: {self.pref.get('section', '')}",
1276
- # end="\r\n",
1277
- # file=file_descriptor,
1278
- # )
1279
- print(
1280
- f"CATEGORY: {None}",
1281
- end="\r\n",
1282
- file=file_descriptor,
1283
- )
1284
- print("CATEGORY-POWER: ", end="\r\n", file=file_descriptor)
1285
-
1286
- print(
1287
- f"CLAIMED-SCORE: {self.contest.calc_score(self)}",
1288
- end="\r\n",
1289
- file=file_descriptor,
1290
- )
1291
- print(
1292
- "OPERATORS: ",
1293
- end="\r\n",
1294
- file=file_descriptor,
1295
- )
1296
- print(
1297
- f"NAME: {self.pref.get('name', '')}",
1298
- end="\r\n",
1299
- file=file_descriptor,
1300
- )
1301
- print(
1302
- f"ADDRESS: {self.pref.get('address1', '')}",
1303
- end="\r\n",
1304
- file=file_descriptor,
1305
- )
1306
- print(
1307
- f"ADDRESS-CITY: {self.pref.get('city', '')}",
1308
- end="\r\n",
1309
- file=file_descriptor,
1310
- )
1311
- print(
1312
- f"ADDRESS-STATE: {self.pref.get('state', '')}",
1313
- end="\r\n",
1314
- file=file_descriptor,
1315
- )
1316
- print(
1317
- f"ADDRESS-POSTALCODE: {self.pref.get('zip', '')}",
1318
- end="\r\n",
1319
- file=file_descriptor,
1320
- )
1321
- print(
1322
- f"ADDRESS-COUNTRY: {self.pref.get('country', '')}",
1323
- end="\r\n",
1324
- file=file_descriptor,
1325
- )
1326
- print(
1327
- f"EMAIL: {self.pref.get('email', '')}",
1328
- end="\r\n",
1329
- file=file_descriptor,
1330
- )
1331
- for contact in log:
1332
- # hiscall = contact.get("Call", "")
1333
- # hisclass = contact.get("class")
1334
- # hissection = contact.get("section")
1335
- the_date_and_time = contact.get("TS", "")
1336
- # band = contact.get("Band", "")
1337
- mode = contact.get("Mode", "")
1338
- if mode == "LSB" or mode == "USB":
1339
- mode = "PH"
1340
- frequency = str(int(contact.get("Freq", "0"))).rjust(5)
1341
-
1342
- loggeddate = the_date_and_time[:10]
1343
- loggedtime = the_date_and_time[11:13] + the_date_and_time[14:16]
1344
- print(
1345
- f"QSO: {frequency} {mode} {loggeddate} {loggedtime} "
1346
- f"{contact.get('StationPrefix', '').ljust(13)} "
1347
- f"{str(contact.get('SNT', '')).ljust(3)} "
1348
- f"{str(contact.get('SentNr', '')).ljust(6)} "
1349
- f"{contact.get('Call', '').ljust(13)} "
1350
- f"{str(contact.get('RCV', '')).ljust(3)} "
1351
- f"{str(contact.get('NR', '')).ljust(6)}",
1352
- end="\r\n",
1353
- file=file_descriptor,
1354
- )
1355
- print("END-OF-LOG:", end="\r\n", file=file_descriptor)
1356
- except IOError as exception:
1357
- logger.critical(
1358
- "cabrillo: IO error: %s, writing to %s", exception, filename
1359
- )
1360
- # self.infobox.insertPlainText(" Failed\n\n")
1361
- # app.processEvents()
1362
- return
1363
- # self.infobox.insertPlainText(" Done\n\n")
1256
+ self.contest.cabrillo(self)
1364
1257
 
1365
1258
 
1366
1259
  def load_fonts_from_dir(directory: str) -> set:
not1mm/data/main.ui CHANGED
@@ -789,6 +789,7 @@
789
789
  <addaction name="actionQRZ_Settings"/>
790
790
  <addaction name="actionConnection_Settings"/>
791
791
  <addaction name="actionGenerate_Cabrillo"/>
792
+ <addaction name="actionGenerate_ADIF"/>
792
793
  </widget>
793
794
  <widget class="QMenu" name="menuHelp">
794
795
  <property name="title">
@@ -930,6 +931,11 @@
930
931
  <string>Log Window</string>
931
932
  </property>
932
933
  </action>
934
+ <action name="actionGenerate_ADIF">
935
+ <property name="text">
936
+ <string>Generate ADIF</string>
937
+ </property>
938
+ </action>
933
939
  </widget>
934
940
  <resources/>
935
941
  <connections/>
not1mm/lib/database.py CHANGED
@@ -370,6 +370,30 @@ class DataBase:
370
370
  )
371
371
  return cursor.fetchone()
372
372
 
373
+ def check_dupe_on_band(self, call, band) -> dict:
374
+ """Checks if a call is dupe on band/mode"""
375
+ with sqlite3.connect(self.database) as conn:
376
+ conn.row_factory = self.row_factory
377
+ cursor = conn.cursor()
378
+ print(
379
+ f"select count(*) as isdupe from dxlog where Call = '{call}' and Band = '{band}';"
380
+ )
381
+ cursor.execute(
382
+ f"select count(*) as isdupe from dxlog where Call = '{call}' and Band = '{band}';"
383
+ )
384
+ return cursor.fetchone()
385
+
386
+ def check_dupe(self, call) -> dict:
387
+ """Checks if a call is dupe on band/mode"""
388
+ with sqlite3.connect(self.database) as conn:
389
+ conn.row_factory = self.row_factory
390
+ cursor = conn.cursor()
391
+ print(f"select count(*) as isdupe from dxlog where Call = '{call}';")
392
+ cursor.execute(
393
+ f"select count(*) as isdupe from dxlog where Call = '{call}';"
394
+ )
395
+ return cursor.fetchone()
396
+
373
397
  def fetch_points(self) -> dict:
374
398
  """return points"""
375
399
  with sqlite3.connect(self.database) as conn:
not1mm/lib/version.py CHANGED
@@ -1,2 +1,2 @@
1
1
  """It's the version"""
2
- __version__ = "23.3.24.1"
2
+ __version__ = "23.3.27"
@@ -0,0 +1,389 @@
1
+ """CQ WPX CW plugin"""
2
+
3
+ # pylint: disable=invalid-name
4
+ import logging
5
+ from pathlib import Path
6
+
7
+ from PyQt5 import QtWidgets
8
+
9
+ from not1mm.lib.version import __version__
10
+
11
+ logger = logging.getLogger("__main__")
12
+
13
+ name = "CQ WPX CW"
14
+ cabrillo_name = "CQ-WPX-CW"
15
+ mode = "CW" # CW SSB BOTH RTTY
16
+
17
+ # 1 once per contest, 2 work each band, 3 each band/mode, 4 no dupe checking
18
+ dupe_type = 2
19
+
20
+
21
+ def init_contest(self):
22
+ """setup plugin"""
23
+ set_tab_next(self)
24
+ set_tab_prev(self)
25
+ interface(self)
26
+ self.next_field = self.other_2
27
+
28
+
29
+ def interface(self):
30
+ """Setup user interface"""
31
+ self.field1.show()
32
+ self.field2.show()
33
+ self.field3.show()
34
+ self.field4.show()
35
+ label = self.field3.findChild(QtWidgets.QLabel)
36
+ label.setText("SentNR")
37
+ label = self.field4.findChild(QtWidgets.QLabel)
38
+ label.setText("RcvNR")
39
+
40
+
41
+ def set_tab_next(self):
42
+ """Set TAB Advances"""
43
+ self.tab_next = {
44
+ self.callsign: self.field1.findChild(QtWidgets.QLineEdit),
45
+ self.field1.findChild(QtWidgets.QLineEdit): self.field2.findChild(
46
+ QtWidgets.QLineEdit
47
+ ),
48
+ self.field2.findChild(QtWidgets.QLineEdit): self.field3.findChild(
49
+ QtWidgets.QLineEdit
50
+ ),
51
+ self.field3.findChild(QtWidgets.QLineEdit): self.field4.findChild(
52
+ QtWidgets.QLineEdit
53
+ ),
54
+ self.field4.findChild(QtWidgets.QLineEdit): self.callsign,
55
+ }
56
+
57
+
58
+ def set_tab_prev(self):
59
+ """Set TAB Advances"""
60
+ self.tab_prev = {
61
+ self.callsign: self.field4.findChild(QtWidgets.QLineEdit),
62
+ self.field1.findChild(QtWidgets.QLineEdit): self.callsign,
63
+ self.field2.findChild(QtWidgets.QLineEdit): self.field1.findChild(
64
+ QtWidgets.QLineEdit
65
+ ),
66
+ self.field3.findChild(QtWidgets.QLineEdit): self.field2.findChild(
67
+ QtWidgets.QLineEdit
68
+ ),
69
+ self.field4.findChild(QtWidgets.QLineEdit): self.field3.findChild(
70
+ QtWidgets.QLineEdit
71
+ ),
72
+ }
73
+
74
+
75
+ def set_contact_vars(self):
76
+ """Contest Specific"""
77
+ self.contact["SNT"] = self.sent.text()
78
+ self.contact["RCV"] = self.receive.text()
79
+ self.contact["SentNr"] = self.other_1.text()
80
+ self.contact["NR"] = self.other_2.text()
81
+ if self.contact.get("WPXPrefix"):
82
+ result = self.database.fetch_wpx_exists(self.contact.get("WPXPrefix", ""))
83
+ if result.get("wpx_count"):
84
+ self.contact["IsMultiplier1"] = 0
85
+ else:
86
+ self.contact["IsMultiplier1"] = 1
87
+
88
+
89
+ def prefill(self):
90
+ """Fill SentNR"""
91
+ result = self.database.get_serial()
92
+ serial_nr = str(result.get("serial_nr", "1"))
93
+ if serial_nr == "None":
94
+ serial_nr = "1"
95
+ field = self.field3.findChild(QtWidgets.QLineEdit)
96
+ if len(field.text()) == 0:
97
+ field.setText(serial_nr)
98
+
99
+
100
+ def points(self):
101
+ """Calc point"""
102
+ result = self.cty_lookup(self.pref.get("callsign", ""))
103
+ if result:
104
+ for item in result.items():
105
+ mycountry = item[1].get("entity", "")
106
+ mycontinent = item[1].get("continent", "")
107
+ result = self.cty_lookup(self.contact.get("Call", ""))
108
+ band = int(int(float(self.contact.get("Freq"))) / 1000)
109
+ if result:
110
+ for item in result.items():
111
+ entity = item[1].get("entity", "")
112
+ continent = item[1].get("continent", "")
113
+ if mycountry.upper() == entity.upper():
114
+ return 1
115
+ if mycontinent and continent == "NA":
116
+ if band in [28, 21, 14]:
117
+ return 2
118
+ return 4
119
+ if mycontinent == continent:
120
+ if band in [28, 21, 14]:
121
+ return 1
122
+ return 2
123
+ if band in [28, 21, 14]:
124
+ return 3
125
+ return 6
126
+ return 0
127
+
128
+
129
+ def show_mults(self):
130
+ """Return display string for mults"""
131
+ result = self.database.fetch_wpx_count()
132
+ if result:
133
+ return int(result.get("wpx_count", 0))
134
+ return 0
135
+
136
+
137
+ def show_qso(self):
138
+ """Return qso count"""
139
+ result = self.database.fetch_qso_count()
140
+ if result:
141
+ return int(result.get("qsos", 0))
142
+ return 0
143
+
144
+
145
+ def get_points(self):
146
+ """Return raw points before mults"""
147
+ result = self.database.fetch_points()
148
+ if result:
149
+ return int(result.get("Points", 0))
150
+ return 0
151
+
152
+
153
+ def calc_score(self):
154
+ """Return calculated score"""
155
+ result = self.database.fetch_points()
156
+ if result is not None:
157
+ score = result.get("Points", "0")
158
+ if score is None:
159
+ score = "0"
160
+ contest_points = int(score)
161
+ result = self.database.fetch_wpx_count()
162
+ mults = int(result.get("wpx_count", 0))
163
+ return contest_points * mults
164
+ return 0
165
+
166
+
167
+ def adif(self):
168
+ """
169
+ Creates an ADIF file of the contacts made.
170
+ """
171
+ filename = (
172
+ str(Path.home())
173
+ + "/"
174
+ + f"{self.pref.get('callsign').upper()}_{cabrillo_name}.adi"
175
+ )
176
+ log = self.database.fetch_all_contacts_asc()
177
+ try:
178
+ with open(filename, "w", encoding="utf-8") as file_descriptor:
179
+ print("<ADIF_VER:5>2.2.0", end="\r\n", file=file_descriptor)
180
+ print("<EOH>", end="\r\n", file=file_descriptor)
181
+ for contact in log:
182
+
183
+ hiscall = contact.get("Call", "")
184
+ the_date_and_time = contact.get("TS")
185
+ # band = contact.get("Band")
186
+ themode = contact.get("Mode")
187
+ frequency = str(contact.get("Freq", 0) / 1000)
188
+ sentrst = contact.get("SNT", "")
189
+ rcvrst = contact.get("RCV", "")
190
+ sentnr = str(contact.get("SentNr", "59"))
191
+ rcvnr = str(contact.get("NR", "59"))
192
+ grid = contact.get("GridSquare", "")
193
+ comment = contact.get("ContestName", "")
194
+ loggeddate = the_date_and_time[:10]
195
+ loggedtime = the_date_and_time[11:13] + the_date_and_time[14:16]
196
+ print(
197
+ f"<QSO_DATE:{len(''.join(loggeddate.split('-')))}:d>"
198
+ f"{''.join(loggeddate.split('-'))}",
199
+ end="\r\n",
200
+ file=file_descriptor,
201
+ )
202
+ print(
203
+ f"<TIME_ON:{len(loggedtime)}>{loggedtime}",
204
+ end="\r\n",
205
+ file=file_descriptor,
206
+ )
207
+ print(
208
+ f"<CALL:{len(hiscall)}>{hiscall}",
209
+ end="\r\n",
210
+ file=file_descriptor,
211
+ )
212
+ print(
213
+ f"<MODE:{len(themode)}>{themode}", end="\r\n", file=file_descriptor
214
+ )
215
+ # print(
216
+ # f"<BAND:{len(band + 'M')}>{band + 'M'}",
217
+ # end="\r\n",
218
+ # file=file_descriptor,
219
+ # )
220
+ try:
221
+ print(
222
+ f"<FREQ:{len(frequency)}>{frequency}",
223
+ end="\r\n",
224
+ file=file_descriptor,
225
+ )
226
+ except TypeError:
227
+ pass # This is bad form... I can't remember why this is in a try block
228
+
229
+ print(
230
+ f"<RST_SENT:{len(sentrst)}>{sentrst}",
231
+ end="\r\n",
232
+ file=file_descriptor,
233
+ )
234
+ print(
235
+ f"<RST_RCVD:{len(rcvrst)}>{rcvrst}",
236
+ end="\r\n",
237
+ file=file_descriptor,
238
+ )
239
+
240
+ print(
241
+ f"<STX_STRING:{len(sentnr)}>{sentnr}",
242
+ end="\r\n",
243
+ file=file_descriptor,
244
+ )
245
+ print(
246
+ f"<SRX_STRING:{len(rcvnr)}>{rcvnr}",
247
+ end="\r\n",
248
+ file=file_descriptor,
249
+ )
250
+ if len(grid) > 1:
251
+ print(
252
+ f"<GRIDSQUARE:{len(grid)}>{grid}",
253
+ end="\r\n",
254
+ file=file_descriptor,
255
+ )
256
+
257
+ print(
258
+ f"<COMMENT:{len(comment)}>{comment}",
259
+ end="\r\n",
260
+ file=file_descriptor,
261
+ )
262
+ print("<EOR>", end="\r\n", file=file_descriptor)
263
+ print("", end="\r\n", file=file_descriptor)
264
+ except IOError:
265
+ ...
266
+
267
+
268
+ def cabrillo(self):
269
+ """Generates Cabrillo file. Maybe."""
270
+ # https://www.cqwpx.com/cabrillo.htm
271
+ logger.debug("******Cabrillo*****")
272
+ filename = (
273
+ str(Path.home())
274
+ + "/"
275
+ + f"{self.pref.get('callsign').upper()}_{cabrillo_name}.log"
276
+ )
277
+ logger.debug("%s", filename)
278
+ log = self.database.fetch_all_contacts_asc()
279
+ try:
280
+ with open(filename, "w", encoding="ascii") as file_descriptor:
281
+ print("START-OF-LOG: 3.0", end="\r\n", file=file_descriptor)
282
+ print(
283
+ f"CREATED-BY: Not1MM v{__version__}",
284
+ end="\r\n",
285
+ file=file_descriptor,
286
+ )
287
+ print(
288
+ f"CONTEST: {cabrillo_name}",
289
+ end="\r\n",
290
+ file=file_descriptor,
291
+ )
292
+ print(
293
+ f"CALLSIGN: {self.pref.get('callsign','')}",
294
+ end="\r\n",
295
+ file=file_descriptor,
296
+ )
297
+ print(
298
+ f"LOCATION: {self.pref.get('section', '')}",
299
+ end="\r\n",
300
+ file=file_descriptor,
301
+ )
302
+ # print(
303
+ # f"ARRL-SECTION: {self.pref.get('section', '')}",
304
+ # end="\r\n",
305
+ # file=file_descriptor,
306
+ # )
307
+ # CATEGORY-OPERATOR: SINGLE-OP
308
+ # CATEGORY-ASSISTED: NON-ASSISTED
309
+ # CATEGORY-BAND: ALL
310
+ # CATEGORY-MODE: SSB
311
+ # CATEGORY-TRANSMITTER: ONE
312
+ # CATEGORY-OVERLAY: CLASSIC
313
+ # GRID-LOCATOR: DM13at
314
+ print(
315
+ f"CATEGORY: {None}",
316
+ end="\r\n",
317
+ file=file_descriptor,
318
+ )
319
+ print("CATEGORY-POWER: ", end="\r\n", file=file_descriptor)
320
+
321
+ print(
322
+ f"CLAIMED-SCORE: {calc_score(self)}",
323
+ end="\r\n",
324
+ file=file_descriptor,
325
+ )
326
+ print(
327
+ "OPERATORS: ",
328
+ end="\r\n",
329
+ file=file_descriptor,
330
+ )
331
+ print(
332
+ f"NAME: {self.pref.get('name', '')}",
333
+ end="\r\n",
334
+ file=file_descriptor,
335
+ )
336
+ print(
337
+ f"ADDRESS: {self.pref.get('address1', '')}",
338
+ end="\r\n",
339
+ file=file_descriptor,
340
+ )
341
+ print(
342
+ f"ADDRESS-CITY: {self.pref.get('city', '')}",
343
+ end="\r\n",
344
+ file=file_descriptor,
345
+ )
346
+ print(
347
+ f"ADDRESS-STATE-PROVINCE: {self.pref.get('state', '')}",
348
+ end="\r\n",
349
+ file=file_descriptor,
350
+ )
351
+ print(
352
+ f"ADDRESS-POSTALCODE: {self.pref.get('zip', '')}",
353
+ end="\r\n",
354
+ file=file_descriptor,
355
+ )
356
+ print(
357
+ f"ADDRESS-COUNTRY: {self.pref.get('country', '')}",
358
+ end="\r\n",
359
+ file=file_descriptor,
360
+ )
361
+ print(
362
+ f"EMAIL: {self.pref.get('email', '')}",
363
+ end="\r\n",
364
+ file=file_descriptor,
365
+ )
366
+ for contact in log:
367
+ the_date_and_time = contact.get("TS", "")
368
+ themode = contact.get("Mode", "")
369
+ if themode == "LSB" or themode == "USB":
370
+ themode = "PH"
371
+ frequency = str(int(contact.get("Freq", "0"))).rjust(5)
372
+
373
+ loggeddate = the_date_and_time[:10]
374
+ loggedtime = the_date_and_time[11:13] + the_date_and_time[14:16]
375
+ print(
376
+ f"QSO: {frequency} {themode} {loggeddate} {loggedtime} "
377
+ f"{contact.get('StationPrefix', '').ljust(13)} "
378
+ f"{str(contact.get('SNT', '')).ljust(3)} "
379
+ f"{str(contact.get('SentNr', '')).ljust(6)} "
380
+ f"{contact.get('Call', '').ljust(13)} "
381
+ f"{str(contact.get('RCV', '')).ljust(3)} "
382
+ f"{str(contact.get('NR', '')).ljust(6)}",
383
+ end="\r\n",
384
+ file=file_descriptor,
385
+ )
386
+ print("END-OF-LOG:", end="\r\n", file=file_descriptor)
387
+ except IOError as exception:
388
+ logger.critical("cabrillo: IO error: %s, writing to %s", exception, filename)
389
+ return
@@ -1,15 +1,21 @@
1
1
  """CQ WPX SSB plugin"""
2
2
 
3
3
  # pylint: disable=invalid-name
4
+ import logging
5
+ from pathlib import Path
4
6
 
5
7
  from PyQt5 import QtWidgets
6
8
 
9
+ from not1mm.lib.version import __version__
10
+
11
+ logger = logging.getLogger("__main__")
12
+
7
13
  name = "CQ WPX SSB"
8
14
  cabrillo_name = "CQ-WPX-SSB"
9
15
  mode = "SSB" # CW SSB BOTH RTTY
10
16
 
11
17
  # 1 once per contest, 2 work each band, 3 each band/mode, 4 no dupe checking
12
- dupe_type = 4
18
+ dupe_type = 2
13
19
 
14
20
 
15
21
  def init_contest(self):
@@ -156,3 +162,228 @@ def calc_score(self):
156
162
  mults = int(result.get("wpx_count", 0))
157
163
  return contest_points * mults
158
164
  return 0
165
+
166
+
167
+ def adif(self):
168
+ """
169
+ Creates an ADIF file of the contacts made.
170
+ """
171
+ filename = (
172
+ str(Path.home())
173
+ + "/"
174
+ + f"{self.pref.get('callsign').upper()}_{cabrillo_name}.adi"
175
+ )
176
+ log = self.database.fetch_all_contacts_asc()
177
+ try:
178
+ with open(filename, "w", encoding="utf-8") as file_descriptor:
179
+ print("<ADIF_VER:5>2.2.0", end="\r\n", file=file_descriptor)
180
+ print("<EOH>", end="\r\n", file=file_descriptor)
181
+ for contact in log:
182
+
183
+ hiscall = contact.get("Call", "")
184
+ the_date_and_time = contact.get("TS")
185
+ # band = contact.get("Band")
186
+ themode = contact.get("Mode")
187
+ frequency = str(contact.get("Freq", 0) / 1000)
188
+ sentrst = contact.get("SNT", "")
189
+ rcvrst = contact.get("RCV", "")
190
+ sentnr = str(contact.get("SentNr", "59"))
191
+ rcvnr = str(contact.get("NR", "59"))
192
+ grid = contact.get("GridSquare", "")
193
+ comment = contact.get("ContestName", "")
194
+ loggeddate = the_date_and_time[:10]
195
+ loggedtime = the_date_and_time[11:13] + the_date_and_time[14:16]
196
+ print(
197
+ f"<QSO_DATE:{len(''.join(loggeddate.split('-')))}:d>"
198
+ f"{''.join(loggeddate.split('-'))}",
199
+ end="\r\n",
200
+ file=file_descriptor,
201
+ )
202
+ print(
203
+ f"<TIME_ON:{len(loggedtime)}>{loggedtime}",
204
+ end="\r\n",
205
+ file=file_descriptor,
206
+ )
207
+ print(
208
+ f"<CALL:{len(hiscall)}>{hiscall}",
209
+ end="\r\n",
210
+ file=file_descriptor,
211
+ )
212
+ print(
213
+ f"<MODE:{len(themode)}>{themode}", end="\r\n", file=file_descriptor
214
+ )
215
+ # print(
216
+ # f"<BAND:{len(band + 'M')}>{band + 'M'}",
217
+ # end="\r\n",
218
+ # file=file_descriptor,
219
+ # )
220
+ try:
221
+ print(
222
+ f"<FREQ:{len(frequency)}>{frequency}",
223
+ end="\r\n",
224
+ file=file_descriptor,
225
+ )
226
+ except TypeError:
227
+ pass # This is bad form... I can't remember why this is in a try block
228
+
229
+ print(
230
+ f"<RST_SENT:{len(sentrst)}>{sentrst}",
231
+ end="\r\n",
232
+ file=file_descriptor,
233
+ )
234
+ print(
235
+ f"<RST_RCVD:{len(rcvrst)}>{rcvrst}",
236
+ end="\r\n",
237
+ file=file_descriptor,
238
+ )
239
+
240
+ print(
241
+ f"<STX_STRING:{len(sentnr)}>{sentnr}",
242
+ end="\r\n",
243
+ file=file_descriptor,
244
+ )
245
+ print(
246
+ f"<SRX_STRING:{len(rcvnr)}>{rcvnr}",
247
+ end="\r\n",
248
+ file=file_descriptor,
249
+ )
250
+ if len(grid) > 1:
251
+ print(
252
+ f"<GRIDSQUARE:{len(grid)}>{grid}",
253
+ end="\r\n",
254
+ file=file_descriptor,
255
+ )
256
+
257
+ print(
258
+ f"<COMMENT:{len(comment)}>{comment}",
259
+ end="\r\n",
260
+ file=file_descriptor,
261
+ )
262
+ print("<EOR>", end="\r\n", file=file_descriptor)
263
+ print("", end="\r\n", file=file_descriptor)
264
+ except IOError:
265
+ ...
266
+
267
+
268
+ def cabrillo(self):
269
+ """Generates Cabrillo file. Maybe."""
270
+ # https://www.cqwpx.com/cabrillo.htm
271
+ logger.debug("******Cabrillo*****")
272
+ filename = (
273
+ str(Path.home())
274
+ + "/"
275
+ + f"{self.pref.get('callsign').upper()}_{cabrillo_name}.log"
276
+ )
277
+ logger.debug("%s", filename)
278
+ log = self.database.fetch_all_contacts_asc()
279
+ try:
280
+ with open(filename, "w", encoding="ascii") as file_descriptor:
281
+ print("START-OF-LOG: 3.0", end="\r\n", file=file_descriptor)
282
+ print(
283
+ f"CREATED-BY: Not1MM v{__version__}",
284
+ end="\r\n",
285
+ file=file_descriptor,
286
+ )
287
+ print(
288
+ f"CONTEST: {cabrillo_name}",
289
+ end="\r\n",
290
+ file=file_descriptor,
291
+ )
292
+ print(
293
+ f"CALLSIGN: {self.pref.get('callsign','')}",
294
+ end="\r\n",
295
+ file=file_descriptor,
296
+ )
297
+ print(
298
+ f"LOCATION: {self.pref.get('section', '')}",
299
+ end="\r\n",
300
+ file=file_descriptor,
301
+ )
302
+ # print(
303
+ # f"ARRL-SECTION: {self.pref.get('section', '')}",
304
+ # end="\r\n",
305
+ # file=file_descriptor,
306
+ # )
307
+ # CATEGORY-OPERATOR: SINGLE-OP
308
+ # CATEGORY-ASSISTED: NON-ASSISTED
309
+ # CATEGORY-BAND: ALL
310
+ # CATEGORY-MODE: SSB
311
+ # CATEGORY-TRANSMITTER: ONE
312
+ # CATEGORY-OVERLAY: CLASSIC
313
+ # GRID-LOCATOR: DM13at
314
+ print(
315
+ f"CATEGORY: {None}",
316
+ end="\r\n",
317
+ file=file_descriptor,
318
+ )
319
+ print("CATEGORY-POWER: ", end="\r\n", file=file_descriptor)
320
+
321
+ print(
322
+ f"CLAIMED-SCORE: {calc_score(self)}",
323
+ end="\r\n",
324
+ file=file_descriptor,
325
+ )
326
+ print(
327
+ "OPERATORS: ",
328
+ end="\r\n",
329
+ file=file_descriptor,
330
+ )
331
+ print(
332
+ f"NAME: {self.pref.get('name', '')}",
333
+ end="\r\n",
334
+ file=file_descriptor,
335
+ )
336
+ print(
337
+ f"ADDRESS: {self.pref.get('address1', '')}",
338
+ end="\r\n",
339
+ file=file_descriptor,
340
+ )
341
+ print(
342
+ f"ADDRESS-CITY: {self.pref.get('city', '')}",
343
+ end="\r\n",
344
+ file=file_descriptor,
345
+ )
346
+ print(
347
+ f"ADDRESS-STATE-PROVINCE: {self.pref.get('state', '')}",
348
+ end="\r\n",
349
+ file=file_descriptor,
350
+ )
351
+ print(
352
+ f"ADDRESS-POSTALCODE: {self.pref.get('zip', '')}",
353
+ end="\r\n",
354
+ file=file_descriptor,
355
+ )
356
+ print(
357
+ f"ADDRESS-COUNTRY: {self.pref.get('country', '')}",
358
+ end="\r\n",
359
+ file=file_descriptor,
360
+ )
361
+ print(
362
+ f"EMAIL: {self.pref.get('email', '')}",
363
+ end="\r\n",
364
+ file=file_descriptor,
365
+ )
366
+ for contact in log:
367
+ the_date_and_time = contact.get("TS", "")
368
+ themode = contact.get("Mode", "")
369
+ if themode == "LSB" or themode == "USB":
370
+ themode = "PH"
371
+ frequency = str(int(contact.get("Freq", "0"))).rjust(5)
372
+
373
+ loggeddate = the_date_and_time[:10]
374
+ loggedtime = the_date_and_time[11:13] + the_date_and_time[14:16]
375
+ print(
376
+ f"QSO: {frequency} {themode} {loggeddate} {loggedtime} "
377
+ f"{contact.get('StationPrefix', '').ljust(13)} "
378
+ f"{str(contact.get('SNT', '')).ljust(3)} "
379
+ f"{str(contact.get('SentNr', '')).ljust(6)} "
380
+ f"{contact.get('Call', '').ljust(13)} "
381
+ f"{str(contact.get('RCV', '')).ljust(3)} "
382
+ f"{str(contact.get('NR', '')).ljust(6)}",
383
+ end="\r\n",
384
+ file=file_descriptor,
385
+ )
386
+ print("END-OF-LOG:", end="\r\n", file=file_descriptor)
387
+ except IOError as exception:
388
+ logger.critical("cabrillo: IO error: %s, writing to %s", exception, filename)
389
+ return
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: not1mm
3
- Version: 23.3.24.1
3
+ Version: 23.3.27
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
@@ -44,6 +44,7 @@ Requires-Dist: xmltodict
44
44
  - [Log Display](#log-display)
45
45
  - [Editing a contact](#editing-a-contact)
46
46
  - [Cabrillo](#cabrillo)
47
+ - [ADIF](#adif)
47
48
  - [Dupe checking](#dupe-checking)
48
49
  - [CAT](#cat)
49
50
 
@@ -74,6 +75,8 @@ Feature complete.
74
75
 
75
76
  ## Changes of note
76
77
 
78
+ - [23-3-27] Fix cursor behaviour when editing text in callsign field.
79
+ - [23-3-25] Fix minimum call length. Fix cabrillo tag. Add adif output.
77
80
  - [23-3-24] Added dupe checking. Added CAT check for flrig or rigctld. Added online flag for flrig.
78
81
  - [23-3-23] Added most of Cabrillo generation. Plan to test it this weekends CQ WPX SSB.
79
82
  - [23-3-22] Add prefill of serial nr. set OP call on startup. Set IsMultiplier1 new unique wpx. Add OP and contest name to window title. and stuff.
@@ -196,6 +199,14 @@ So for me it would be:
196
199
 
197
200
  K6GTE_CQ-WPX-SSB.log
198
201
 
202
+ ## ADIF
203
+
204
+ `File` > `Generate ADIF`
205
+
206
+ Boom... ADIF
207
+
208
+ `StationCall`_`ContestName`.adi
209
+
199
210
  ## Dupe checking
200
211
 
201
212
  Added dupe checking. Big Red 'Dupe' will appear if it's a dupe...
@@ -1,5 +1,5 @@
1
1
  not1mm/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- not1mm/__main__.py,sha256=aKa3xzSdqtR-8S9j4g-DEgIaqy1G7e_DkPFxby4doFE,54896
2
+ not1mm/__main__.py,sha256=oVY2SStvSXwOb_kWCmKSUquEQ6etJluzKz4EpERvoJo,50566
3
3
  not1mm/logwindow.py,sha256=yjBaHKlAUA3nDeZdBIT6-IGsHi-PZGuV1Kgolxunurk,24930
4
4
  not1mm/test.py,sha256=sCAd6OZQlakGm2gMbjhwZ17UyI-Y5L2CCqsYymx7IPQ,3906
5
5
  not1mm/data/Combinear.qss,sha256=SKqM0g8GvNXay1ovgtwCw3Egt0eLfN5P3iTREInC1eE,16590
@@ -17,7 +17,7 @@ not1mm/data/k6gte.not1mm-128.png,sha256=vWqt3Cgsaguj-BBiIoSJApzzhisPxldM8HZQbZ05
17
17
  not1mm/data/k6gte.not1mm-32.png,sha256=yucSwzlmqv3NegdWUvPvZzSgP7G22Ky3se8TWRXvzfI,1108
18
18
  not1mm/data/k6gte.not1mm-64.png,sha256=1KQvk0WBckUds79BvIFUt-KdTwQKKvTz6hiJu8MiT68,2152
19
19
  not1mm/data/logwindow.ui,sha256=_-wobHhIjALzCswyXIrqNadnLdc88eay1GNF23a-Qh0,970
20
- not1mm/data/main.ui,sha256=9bE0X3QT6qeHHditMK3jMBTzQnflNUNjbkIweJStIq0,29313
20
+ not1mm/data/main.ui,sha256=ro6ws39JgvJFHQvvpA6bO5hTevP8bQr2slFFyqAGpq8,29483
21
21
  not1mm/data/opon.ui,sha256=6r9_6ORGfNqwOnpzQjaJ1tWP_81amuXqLYlx1hHgdME,2018
22
22
  not1mm/data/reddot.png,sha256=M33jEMoU8W4rQ4_MVyzzKxDPDte1ypKBch5VnUMNLKE,565
23
23
  not1mm/data/settings.ui,sha256=-uEW4fQWolcv6QrNVpQ_TYhpJLCnhnCiggVSMR27cpA,35558
@@ -25,7 +25,7 @@ not1mm/data/use_qrz_dialog.ui,sha256=sXV1K1tsmYEF7K8nnV_t_UR3k1PzQp43CgvX4KSnR94
25
25
  not1mm/lib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
26
26
  not1mm/lib/cat_interface.py,sha256=5EhVLWfMOvg-zLkLTGtNLJj2VohxG48uaMCelEdL1wg,10342
27
27
  not1mm/lib/cwinterface.py,sha256=77I7RFtrIljuuWna3mjkjxaPSM1kGaPV4duPkoiT8wA,1772
28
- not1mm/lib/database.py,sha256=HBGiZI4cHk8SLhqzzLEvBztvmbk5LI4jaiPS9quJTZQ,16105
28
+ not1mm/lib/database.py,sha256=SCr97eurpJfongb_mA_-UCNxkNiqCJsPSdTxsSK12Hs,17127
29
29
  not1mm/lib/edit_contact.py,sha256=YwuX-BuIa7AuPtLRENs4jTzxOrtk6MCxZj3BR_bDPW8,357
30
30
  not1mm/lib/edit_macro.py,sha256=lyToZb1nmcONNFh6W35NzYHLU48gbAs2_OsnuRQGHck,559
31
31
  not1mm/lib/edit_opon.py,sha256=GqAOJMiC265_4YRVbkT1PflBuCa4HiCNfRhq8-J6ZVY,363
@@ -35,7 +35,7 @@ not1mm/lib/lookup.py,sha256=4OiWZWc3smcB0lqLk4WTs1U3i2k8yZFsX_1OoCGlaWc,13916
35
35
  not1mm/lib/multicast.py,sha256=4-vesMxt5n_pDk2oYnL7R_exxLl9C197t569AM2lrD4,1954
36
36
  not1mm/lib/n1mm.py,sha256=bK5d21Yfn6xRpQcu2RdpL2zR8lOlRWOadD6Ai4_lFdc,4434
37
37
  not1mm/lib/qrz_dialog.py,sha256=fLFBkvQ9cuIqKk0-1yl5OFuQ40n904fNi_Vt23je99E,350
38
- not1mm/lib/version.py,sha256=fOBHG97a4Ep1HAdcuBFWWrv7DF_4o_tJZCZXqguMeAs,49
38
+ not1mm/lib/version.py,sha256=H3AM53QOlozSvTFVbi7jIqx9rOS1-MEL3_qaM4R7rKU,47
39
39
  not1mm/plugins/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
40
40
  not1mm/plugins/arrl_dx_cw.py,sha256=VRP-H65SgEaCff6efF85DzU5KMoECkLQad4DraNgUec,1382
41
41
  not1mm/plugins/arrl_dx_phone.py,sha256=9uLZWogzTfLm0SjmcG149dTG_GjDbbVhdInl4Uz7Fck,1385
@@ -43,14 +43,15 @@ not1mm/plugins/arrl_field_day.py,sha256=GJhdpkX9pfm8imfIhKKSQh36PRpK4DrqDH3kcN5Z
43
43
  not1mm/plugins/arrl_rtty_ru.py,sha256=_cOuXmGTpQ2xbDOEzFVKuitmVlB_F2P5vP3x3fcsDa8,1390
44
44
  not1mm/plugins/arrl_ss_cw.py,sha256=Y0sYwcbzZGcR4843QkMCzZ8RBjXPtJFEa87TWyAGCBA,1391
45
45
  not1mm/plugins/arrl_ss_phone.py,sha256=4AY_S5wx-L0dC5pFLCDTX0FNdxTDFeg_YpaX-7z8nrY,1394
46
- not1mm/plugins/cq_wpx_ssb.py,sha256=wl3PXlCAN0LZOFwWInYUHL4T8f-h8dQoQbdMPs7r7xM,4573
46
+ not1mm/plugins/cq_wpx_cw.py,sha256=rEWFFBfhudT8X3GP1Vn4L8Q6vKrX9jsh5_NkVkTY6SI,12822
47
+ not1mm/plugins/cq_wpx_ssb.py,sha256=3WHd_aUyG1yZLAF3IWEAvR92a0QlP_ss7rgLoqagv6s,12826
47
48
  not1mm/plugins/cqww_dx_cw.py,sha256=eKBIQ8WdJXlGtSTDds7BuoFPpn3odEoiiu_RIQQLHb8,2086
48
49
  not1mm/plugins/cqww_dx_ssb.py,sha256=7lPh-VH8nb-qQ-9wYx5pd6Es_qSbiL3ETHpibqhH_r0,2090
49
50
  not1mm/plugins/general_logging.py,sha256=UwHO3g84bghuIaa1JyqMeMFVfmMRvLgV3NQY3zZWW-w,2077
50
51
  not1mm/plugins/winter_field_day.py,sha256=L7co3YEoKt-jaAFJfGw_Y3-uK-G6eQNsOQSCEkT5U24,1631
51
- not1mm-23.3.24.1.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
52
- not1mm-23.3.24.1.dist-info/METADATA,sha256=uULB7l6FwzkNN4KUWg3RIC0cFdl7prAeE6FyfbyK9eQ,8860
53
- not1mm-23.3.24.1.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92
54
- not1mm-23.3.24.1.dist-info/entry_points.txt,sha256=pMcZk_0dxFgLkcUkF0Q874ojpwOmF3OL6EKw9LgvocM,47
55
- not1mm-23.3.24.1.dist-info/top_level.txt,sha256=0YmTxEcDzQlzXub-lXASvoLpg_mt1c2thb5cVkDf5J4,7
56
- not1mm-23.3.24.1.dist-info/RECORD,,
52
+ not1mm-23.3.27.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
53
+ not1mm-23.3.27.dist-info/METADATA,sha256=auF7-fSveo2JxXCHcWeEsT6KntUfF1ZAs48GQ67-mxE,9100
54
+ not1mm-23.3.27.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92
55
+ not1mm-23.3.27.dist-info/entry_points.txt,sha256=pMcZk_0dxFgLkcUkF0Q874ojpwOmF3OL6EKw9LgvocM,47
56
+ not1mm-23.3.27.dist-info/top_level.txt,sha256=0YmTxEcDzQlzXub-lXASvoLpg_mt1c2thb5cVkDf5J4,7
57
+ not1mm-23.3.27.dist-info/RECORD,,