not1mm 24.3.9__py3-none-any.whl → 24.3.19__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.
Files changed (43) hide show
  1. not1mm/__main__.py +2 -4
  2. not1mm/data/new_contest.ui +11 -1
  3. not1mm/data/not1mm.html +46 -108
  4. not1mm/lib/database.py +14 -0
  5. not1mm/lib/plugin_common.py +29 -2
  6. not1mm/lib/version.py +1 -1
  7. not1mm/plugins/10_10_fall_cw.py +2 -0
  8. not1mm/plugins/10_10_spring_cw.py +2 -0
  9. not1mm/plugins/10_10_summer_phone.py +2 -0
  10. not1mm/plugins/10_10_winter_phone.py +2 -0
  11. not1mm/plugins/arrl_10m.py +2 -0
  12. not1mm/plugins/arrl_dx_cw.py +2 -0
  13. not1mm/plugins/arrl_dx_ssb.py +2 -0
  14. not1mm/plugins/arrl_field_day.py +2 -0
  15. not1mm/plugins/arrl_ss_cw.py +2 -0
  16. not1mm/plugins/arrl_ss_phone.py +2 -0
  17. not1mm/plugins/arrl_vhf_jan.py +2 -0
  18. not1mm/plugins/arrl_vhf_jun.py +2 -0
  19. not1mm/plugins/arrl_vhf_sep.py +2 -0
  20. not1mm/plugins/canada_day.py +2 -0
  21. not1mm/plugins/cq_160_cw.py +411 -0
  22. not1mm/plugins/cq_160_ssb.py +411 -0
  23. not1mm/plugins/cq_wpx_cw.py +2 -0
  24. not1mm/plugins/cq_wpx_ssb.py +2 -0
  25. not1mm/plugins/cq_ww_cw.py +2 -0
  26. not1mm/plugins/cq_ww_ssb.py +2 -0
  27. not1mm/plugins/cwt.py +2 -0
  28. not1mm/plugins/iaru_hf.py +2 -0
  29. not1mm/plugins/jidx_cw.py +2 -0
  30. not1mm/plugins/jidx_ph.py +2 -0
  31. not1mm/plugins/naqp_cw.py +2 -0
  32. not1mm/plugins/naqp_ssb.py +2 -0
  33. not1mm/plugins/phone_weekly_test.py +2 -0
  34. not1mm/plugins/stew_perry_topband.py +2 -0
  35. not1mm/plugins/winter_field_day.py +2 -0
  36. {not1mm-24.3.9.dist-info → not1mm-24.3.19.dist-info}/METADATA +7 -1
  37. {not1mm-24.3.9.dist-info → not1mm-24.3.19.dist-info}/RECORD +43 -40
  38. {not1mm-24.3.9.dist-info → not1mm-24.3.19.dist-info}/WHEEL +1 -1
  39. testing/detectdark.py +35 -0
  40. testing/test.py +13 -11
  41. {not1mm-24.3.9.dist-info → not1mm-24.3.19.dist-info}/LICENSE +0 -0
  42. {not1mm-24.3.9.dist-info → not1mm-24.3.19.dist-info}/entry_points.txt +0 -0
  43. {not1mm-24.3.9.dist-info → not1mm-24.3.19.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,411 @@
1
+ """CQ 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 PyQt5 import QtWidgets
12
+
13
+ from not1mm.lib.plugin_common import gen_adif
14
+ from not1mm.lib.version import __version__
15
+
16
+ logger = logging.getLogger("__main__")
17
+
18
+ name = "CQ 160 CW"
19
+ cabrillo_name = "CQ-160-CW"
20
+ mode = "CW" # CW SSB BOTH RTTY
21
+
22
+ columns = [
23
+ "YYYY-MM-DD HH:MM:SS",
24
+ "Call",
25
+ "Freq",
26
+ "Snt",
27
+ "Rcv",
28
+ "PFX",
29
+ "Exchange1",
30
+ "M1",
31
+ "PTS",
32
+ ]
33
+
34
+ advance_on_space = [True, True, True, True, True]
35
+
36
+ # 1 once per contest, 2 work each band, 3 each band/mode, 4 no dupe checking
37
+ dupe_type = 1
38
+
39
+
40
+ def init_contest(self):
41
+ """setup plugin"""
42
+ set_tab_next(self)
43
+ set_tab_prev(self)
44
+ interface(self)
45
+ self.next_field = self.other_2
46
+
47
+
48
+ def interface(self):
49
+ """Setup user interface"""
50
+ self.field1.show()
51
+ self.field2.show()
52
+ self.field3.hide()
53
+ self.field4.show()
54
+ # self.other_label.setText("SentNR")
55
+ # self.field3.setAccessibleName("Sent Number")
56
+ self.exch_label.setText("ST/Prov/CQ Zone")
57
+ self.field4.setAccessibleName("Received Exchange")
58
+
59
+
60
+ def reset_label(self): # pylint: disable=unused-argument
61
+ """reset label after field cleared"""
62
+
63
+
64
+ def set_tab_next(self):
65
+ """Set TAB Advances"""
66
+ self.tab_next = {
67
+ self.callsign: self.field1.findChild(QtWidgets.QLineEdit),
68
+ self.field1.findChild(QtWidgets.QLineEdit): self.field2.findChild(
69
+ QtWidgets.QLineEdit
70
+ ),
71
+ self.field2.findChild(QtWidgets.QLineEdit): self.field4.findChild(
72
+ QtWidgets.QLineEdit
73
+ ),
74
+ self.field3.findChild(QtWidgets.QLineEdit): self.field4.findChild(
75
+ QtWidgets.QLineEdit
76
+ ),
77
+ self.field4.findChild(QtWidgets.QLineEdit): self.callsign,
78
+ }
79
+
80
+
81
+ def set_tab_prev(self):
82
+ """Set TAB Advances"""
83
+ self.tab_prev = {
84
+ self.callsign: self.field4.findChild(QtWidgets.QLineEdit),
85
+ self.field1.findChild(QtWidgets.QLineEdit): self.callsign,
86
+ self.field2.findChild(QtWidgets.QLineEdit): self.field1.findChild(
87
+ QtWidgets.QLineEdit
88
+ ),
89
+ self.field3.findChild(QtWidgets.QLineEdit): self.field2.findChild(
90
+ QtWidgets.QLineEdit
91
+ ),
92
+ self.field4.findChild(QtWidgets.QLineEdit): self.field2.findChild(
93
+ QtWidgets.QLineEdit
94
+ ),
95
+ }
96
+
97
+
98
+ def set_contact_vars(self):
99
+ """Contest Specific"""
100
+ self.contact["SNT"] = self.sent.text()
101
+ self.contact["RCV"] = self.receive.text()
102
+ self.contact["SentNr"] = self.contest_settings.get("SentExchange", 0)
103
+ self.contact["Exchange1"] = self.other_2.text()
104
+ self.contact["IsMultiplier1"] = 0
105
+ # check if mult
106
+ if "/MM" in self.contact.get("Call", ""):
107
+ return
108
+ if self.contact["CountryPrefix"] == "K":
109
+ # check unique state.
110
+ query = f"select count(*) as count from dxlog where CountryPrefix = 'K' and Exchange1 = '{self.contact.get('Exchange1', '')}' and ContestNR = '{self.pref.get('contest', '0')}'"
111
+ result = self.database.exec_sql(query)
112
+ if result.get("count", 0) > 0:
113
+ self.contact["IsMultiplier1"] = 0
114
+ else:
115
+ self.contact["IsMultiplier1"] = 1
116
+ return
117
+ if self.contact["CountryPrefix"] == "VE":
118
+ # check unique province.
119
+ query = f"select count(*) as count from dxlog where CountryPrefix = 'VE' and Exchange1 = '{self.contact.get('Exchange1', '')}' and ContestNR = '{self.pref.get('contest', '0')}'"
120
+ result = self.database.exec_sql(query)
121
+ if result.get("count", 0) > 0:
122
+ self.contact["IsMultiplier1"] = 0
123
+ else:
124
+ self.contact["IsMultiplier1"] = 1
125
+ return
126
+ # check all others
127
+ query = f"select count(*) as count from dxlog where CountryPrefix = '{self.contact.get('CountryPrefix', '')}' and ContestNR = '{self.pref.get('contest', '0')}'"
128
+ result = self.database.exec_sql(query)
129
+ if result.get("count", 0) > 0:
130
+ self.contact["IsMultiplier1"] = 0
131
+ else:
132
+ self.contact["IsMultiplier1"] = 1
133
+ return
134
+
135
+
136
+ def predupe(self): # pylint: disable=unused-argument
137
+ """called after callsign entered"""
138
+
139
+
140
+ def prefill(self):
141
+ """Fill SentNR"""
142
+
143
+
144
+ def points(self):
145
+ """Calc point"""
146
+ # Maritime Mobile
147
+ if "/MM" in self.contact.get("Call", ""):
148
+ return 5
149
+
150
+ result = self.cty_lookup(self.station.get("Call", ""))
151
+ if result:
152
+ for item in result.items():
153
+ mycountry = item[1].get("entity", "")
154
+ mycontinent = item[1].get("continent", "")
155
+ result = self.cty_lookup(self.contact.get("Call", ""))
156
+ if result:
157
+ for item in result.items():
158
+ entity = item[1].get("entity", "")
159
+ continent = item[1].get("continent", "")
160
+
161
+ # Both in same country
162
+ if mycountry.upper() == entity.upper():
163
+ return 2
164
+
165
+ # Same Continent
166
+ if mycontinent == continent:
167
+ return 5
168
+
169
+ # Different Continent
170
+ return 10
171
+ return 0
172
+
173
+
174
+ def show_mults(self):
175
+ """Return display string for mults"""
176
+ result = self.database.fetch_mult1_count()
177
+ count = result.get("count", 0)
178
+ return count
179
+
180
+
181
+ def show_qso(self):
182
+ """Return qso count"""
183
+ result = self.database.fetch_qso_count()
184
+ if result:
185
+ return int(result.get("qsos", 0))
186
+ return 0
187
+
188
+
189
+ def calc_score(self):
190
+ """Return calculated score"""
191
+ result = self.database.fetch_points()
192
+ if result is not None:
193
+ score = result.get("Points", "0")
194
+ if score is None:
195
+ score = "0"
196
+ contest_points = int(score)
197
+ result = self.database.fetch_mult1_count()
198
+ mults = int(result.get("count", 0))
199
+ return contest_points * mults
200
+ return 0
201
+
202
+
203
+ def adif(self):
204
+ """Call the generate ADIF function"""
205
+ gen_adif(self, cabrillo_name, "CQ-160-CW")
206
+
207
+
208
+ def cabrillo(self):
209
+ """Generates Cabrillo file. Maybe."""
210
+ # https://www.cw160.com/cabrillo.htm
211
+ logger.debug("******Cabrillo*****")
212
+ logger.debug("Station: %s", f"{self.station}")
213
+ logger.debug("Contest: %s", f"{self.contest_settings}")
214
+ now = datetime.datetime.now()
215
+ date_time = now.strftime("%Y-%m-%d_%H-%M-%S")
216
+ filename = (
217
+ str(Path.home())
218
+ + "/"
219
+ + f"{self.station.get('Call', '').upper()}_{cabrillo_name}_{date_time}.log"
220
+ )
221
+ logger.debug("%s", filename)
222
+ log = self.database.fetch_all_contacts_asc()
223
+ try:
224
+ with open(filename, "w", encoding="ascii") as file_descriptor:
225
+ print("START-OF-LOG: 3.0", end="\r\n", file=file_descriptor)
226
+ print(
227
+ f"CREATED-BY: Not1MM v{__version__}",
228
+ end="\r\n",
229
+ file=file_descriptor,
230
+ )
231
+ print(
232
+ f"CONTEST: {cabrillo_name}",
233
+ end="\r\n",
234
+ file=file_descriptor,
235
+ )
236
+ if self.station.get("Club", ""):
237
+ print(
238
+ f"CLUB: {self.station.get('Club', '').upper()}",
239
+ end="\r\n",
240
+ file=file_descriptor,
241
+ )
242
+ print(
243
+ f"CALLSIGN: {self.station.get('Call','')}",
244
+ end="\r\n",
245
+ file=file_descriptor,
246
+ )
247
+ print(
248
+ f"LOCATION: {self.station.get('ARRLSection', '')}",
249
+ end="\r\n",
250
+ file=file_descriptor,
251
+ )
252
+
253
+ print(
254
+ f"CATEGORY-OPERATOR: {self.contest_settings.get('OperatorCategory','')}",
255
+ end="\r\n",
256
+ file=file_descriptor,
257
+ )
258
+ print(
259
+ f"CATEGORY-ASSISTED: {self.contest_settings.get('AssistedCategory','')}",
260
+ end="\r\n",
261
+ file=file_descriptor,
262
+ )
263
+ print(
264
+ f"CATEGORY-BAND: {self.contest_settings.get('BandCategory','')}",
265
+ end="\r\n",
266
+ file=file_descriptor,
267
+ )
268
+ print(
269
+ f"CATEGORY-MODE: {self.contest_settings.get('ModeCategory','')}",
270
+ end="\r\n",
271
+ file=file_descriptor,
272
+ )
273
+ print(
274
+ f"CATEGORY-TRANSMITTER: {self.contest_settings.get('TransmitterCategory','')}",
275
+ end="\r\n",
276
+ file=file_descriptor,
277
+ )
278
+ if self.contest_settings.get("OverlayCategory", "") != "N/A":
279
+ print(
280
+ f"CATEGORY-OVERLAY: {self.contest_settings.get('OverlayCategory','')}",
281
+ end="\r\n",
282
+ file=file_descriptor,
283
+ )
284
+ print(
285
+ f"GRID-LOCATOR: {self.station.get('GridSquare','')}",
286
+ end="\r\n",
287
+ file=file_descriptor,
288
+ )
289
+
290
+ print(
291
+ f"CATEGORY-POWER: {self.contest_settings.get('PowerCategory','')}",
292
+ end="\r\n",
293
+ file=file_descriptor,
294
+ )
295
+
296
+ print(
297
+ f"CLAIMED-SCORE: {calc_score(self)}",
298
+ end="\r\n",
299
+ file=file_descriptor,
300
+ )
301
+ ops = f"@{self.station.get('Call','')}"
302
+ list_of_ops = self.database.get_ops()
303
+ for op in list_of_ops:
304
+ ops += f", {op.get('Operator', '')}"
305
+ print(
306
+ f"OPERATORS: {ops}",
307
+ end="\r\n",
308
+ file=file_descriptor,
309
+ )
310
+ print(
311
+ f"NAME: {self.station.get('Name', '')}",
312
+ end="\r\n",
313
+ file=file_descriptor,
314
+ )
315
+ print(
316
+ f"ADDRESS: {self.station.get('Street1', '')}",
317
+ end="\r\n",
318
+ file=file_descriptor,
319
+ )
320
+ print(
321
+ f"ADDRESS-CITY: {self.station.get('City', '')}",
322
+ end="\r\n",
323
+ file=file_descriptor,
324
+ )
325
+ print(
326
+ f"ADDRESS-STATE-PROVINCE: {self.station.get('State', '')}",
327
+ end="\r\n",
328
+ file=file_descriptor,
329
+ )
330
+ print(
331
+ f"ADDRESS-POSTALCODE: {self.station.get('Zip', '')}",
332
+ end="\r\n",
333
+ file=file_descriptor,
334
+ )
335
+ print(
336
+ f"ADDRESS-COUNTRY: {self.station.get('Country', '')}",
337
+ end="\r\n",
338
+ file=file_descriptor,
339
+ )
340
+ print(
341
+ f"EMAIL: {self.station.get('Email', '')}",
342
+ end="\r\n",
343
+ file=file_descriptor,
344
+ )
345
+ for contact in log:
346
+ the_date_and_time = contact.get("TS", "")
347
+ themode = contact.get("Mode", "")
348
+ if themode == "LSB" or themode == "USB":
349
+ themode = "PH"
350
+ frequency = str(int(contact.get("Freq", "0"))).rjust(5)
351
+
352
+ loggeddate = the_date_and_time[:10]
353
+ loggedtime = the_date_and_time[11:13] + the_date_and_time[14:16]
354
+ print(
355
+ f"QSO: {frequency} {themode} {loggeddate} {loggedtime} "
356
+ f"{contact.get('StationPrefix', '').ljust(13)} "
357
+ f"{str(contact.get('SNT', '')).ljust(3)} "
358
+ f"{str(contact.get('SentNr', '')).ljust(6)} "
359
+ f"{contact.get('Call', '').ljust(13)} "
360
+ f"{str(contact.get('RCV', '')).ljust(3)} "
361
+ f"{str(contact.get('Exchange1', '')).ljust(6)}",
362
+ end="\r\n",
363
+ file=file_descriptor,
364
+ )
365
+ print("END-OF-LOG:", end="\r\n", file=file_descriptor)
366
+ self.show_message_box(f"Cabrillo saved to: {filename}")
367
+ except IOError as exception:
368
+ logger.critical("cabrillo: IO error: %s, writing to %s", exception, filename)
369
+ self.show_message_box(f"Error saving Cabrillo: {exception} {filename}")
370
+ return
371
+
372
+
373
+ def trigger_update(self):
374
+ """Triggers the log window to update."""
375
+ cmd = {}
376
+ cmd["cmd"] = "UPDATELOG"
377
+ cmd["station"] = platform.node()
378
+ self.multicast_interface.send_as_json(cmd)
379
+
380
+
381
+ def recalculate_mults(self):
382
+ """Recalculates multipliers after change in logged qso."""
383
+ all_contacts = self.database.fetch_all_contacts_asc()
384
+ for contact in all_contacts:
385
+ time_stamp = contact.get("TS", "")
386
+ if contact.get("CountryPrefix", "") == "K":
387
+ 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')}'"
388
+ result = self.database.exec_sql(query)
389
+ if result.get("count", 0) == 0:
390
+ contact["IsMultiplier1"] = 1
391
+ else:
392
+ contact["IsMultiplier1"] = 0
393
+ self.database.change_contact(contact)
394
+ continue
395
+ if contact.get("CountryPrefix", "") == "VE":
396
+ 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')}'"
397
+ result = self.database.exec_sql(query)
398
+ if result.get("count", 0) == 0:
399
+ contact["IsMultiplier1"] = 1
400
+ else:
401
+ contact["IsMultiplier1"] = 0
402
+ self.database.change_contact(contact)
403
+ continue
404
+ query = f"select count(*) as count from dxlog where TS < '{time_stamp}' and CountryPrefix = '{contact.get('CountryPrefix', '')}' and ContestNR = '{self.pref.get('contest', '0')}'"
405
+ result = self.database.exec_sql(query)
406
+ if result.get("count", 0) == 0:
407
+ contact["IsMultiplier1"] = 1
408
+ else:
409
+ contact["IsMultiplier1"] = 0
410
+ self.database.change_contact(contact)
411
+ trigger_update(self)