not1mm 25.3.18__py3-none-any.whl → 25.3.19.1__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.
@@ -0,0 +1,848 @@
1
+ """DARC VHF plugin"""
2
+
3
+ # pylint: disable=invalid-name, unused-argument, unused-variable, c-extension-no-member, unused-import
4
+
5
+ # DARC VHF and above
6
+ # Status: Active
7
+ # Geographic Focus: Germany
8
+ # Participation: Worldwide
9
+ # Awards: Worldwide
10
+ # Mode: SSB, CW, FM
11
+ # Bands: 145 MHz, 435 MHz, 1.3 GHz, 2.3 GHz, 3.4 GHz, 5.7 GHz, 10 GHz, 24 GHz, 47 GHz, 76 GHz, 122 GHz, 134 GHz, 245 GHz, >300 GHz
12
+ # Classes: Single Op, Multi OP, Trainee
13
+ # Max power: 100 watts
14
+ # Exchange: RST + Locator
15
+ # Work stations: Once per band
16
+ # Points: 1 point per km distance between stations
17
+ # Multipliers: no multis
18
+ # Score Calculation: Total score = sum of all points
19
+ # Mail logs to: (none)
20
+ # Find rules at: https://www.darc.de/der-club/referate/conteste/ukw/tnukwcontest001/tnukwcontest007000/
21
+ # Cabrillo name: DARC VHF
22
+ # Log Format: EDI
23
+
24
+
25
+ import datetime
26
+ import logging
27
+
28
+ from pathlib import Path
29
+
30
+ from PyQt6 import QtWidgets
31
+
32
+ from not1mm.lib.plugin_common import gen_adif, get_points, online_score_xml
33
+ from not1mm.lib.version import __version__
34
+ from not1mm.lib.ham_utility import distance
35
+
36
+ logger = logging.getLogger(__name__)
37
+
38
+ EXCHANGE_HINT = "#"
39
+
40
+ name = "DARC VHF"
41
+ mode = "BOTH" # CW SSB BOTH RTTY
42
+ # columns = [0, 1, 2, 3, 4, 5, 6, 11, 15]
43
+ columns = [
44
+ "YYYY-MM-DD HH:MM:SS",
45
+ "Call",
46
+ "Freq",
47
+ "Snt",
48
+ "Rcv",
49
+ "SentNr",
50
+ "RcvNr",
51
+ "Exchange1",
52
+ "PTS",
53
+ ]
54
+ cabrillo_name = "DARC VHF"
55
+
56
+ advance_on_space = [True, True, True, True, False]
57
+ call_parse_exchange_on_edit = True
58
+
59
+ # 1 once per contest, 2 work each band, 3 each band/mode, 4 no dupe checking
60
+ dupe_type = 3
61
+
62
+
63
+ def init_contest(self):
64
+ """setup plugin"""
65
+ set_tab_next(self)
66
+ set_tab_prev(self)
67
+ interface(self)
68
+ self.next_field = self.other_2
69
+
70
+
71
+ def interface(self):
72
+ """Setup user interface"""
73
+ self.field1.show()
74
+ self.field2.show()
75
+ self.field3.show()
76
+ self.field4.show()
77
+ self.snt_label.setText("SNT")
78
+ self.field1.setAccessibleName("RST Sent")
79
+ self.other_label.setText("SentNR")
80
+ self.field3.setAccessibleName("Sent Number")
81
+ self.exch_label.setText("# Grid")
82
+ self.field4.setAccessibleName("Gridsquare")
83
+
84
+
85
+ def reset_label(self):
86
+ """reset label after field cleared"""
87
+ self.exch_label.setText("# Grid")
88
+
89
+
90
+ def set_tab_next(self):
91
+ """Set TAB Advances"""
92
+ self.tab_next = {
93
+ self.callsign: self.sent,
94
+ self.sent: self.receive,
95
+ self.receive: self.other_1,
96
+ self.other_1: self.other_2,
97
+ self.other_2: self.callsign,
98
+ }
99
+
100
+
101
+ def set_tab_prev(self):
102
+ """Set TAB Advances"""
103
+ self.tab_prev = {
104
+ self.callsign: self.other_2,
105
+ self.sent: self.callsign,
106
+ self.receive: self.sent,
107
+ self.other_1: self.receive,
108
+ self.other_2: self.other_1,
109
+ }
110
+
111
+
112
+ def set_contact_vars(self):
113
+ """Contest Specific"""
114
+ sn, grid = parse_exchange(self)
115
+ self.contact["SNT"] = self.sent.text()
116
+ self.contact["RCV"] = self.receive.text()
117
+ self.contact["SentNr"] = self.other_1.text()
118
+ self.contact["NR"] = sn
119
+ self.contact["Exchange1"] = grid
120
+
121
+
122
+ def parse_exchange(self):
123
+ """Parse exchange..."""
124
+ exchange = self.other_2.text()
125
+ exchange = exchange.upper()
126
+ sn = ""
127
+ grid = ""
128
+ for tokens in exchange.split():
129
+ if tokens.isdigit():
130
+ if sn == "":
131
+ sn = tokens
132
+ continue
133
+ elif tokens.isalnum():
134
+ if len(tokens) == 6:
135
+ grid = tokens
136
+ continue
137
+ label = f"Sn:{sn} Grid:{grid}"
138
+ self.exch_label.setText(label)
139
+ return (sn, grid)
140
+
141
+
142
+ def predupe(self):
143
+ """prefill his exchange with last known values"""
144
+
145
+
146
+ def prefill(self):
147
+ """Fill SentNR"""
148
+ sent_sxchange_setting = self.contest_settings.get("SentExchange", "")
149
+ if sent_sxchange_setting.strip() == "#":
150
+ result = self.database.get_serial()
151
+ serial_nr = str(result.get("serial_nr", "1")).zfill(3)
152
+ if serial_nr == "None":
153
+ serial_nr = "001"
154
+ if len(self.other_1.text()) == 0:
155
+ self.other_1.setText(serial_nr)
156
+ else:
157
+ self.other_1.setText(sent_sxchange_setting)
158
+
159
+
160
+ def points(self):
161
+ """Calc point"""
162
+ _points = 1
163
+ _kilometers = 0
164
+ _their_grid = self.contact["Exchange1"].upper()
165
+ _kilometers = distance(self.station.get("GridSquare", ""), _their_grid)
166
+ _points = max(1, _kilometers)
167
+ return _points
168
+
169
+
170
+ def show_mults(self, rtc=None):
171
+ """Return display string for mults"""
172
+
173
+
174
+ def show_qso(self):
175
+ """Return qso count"""
176
+ result = self.database.fetch_qso_count()
177
+ if result:
178
+ return int(result.get("qsos", 0))
179
+ return 0
180
+
181
+
182
+ def calc_score(self):
183
+ """Return calculated score"""
184
+ result = self.database.fetch_points()
185
+ if result is not None:
186
+ score = result.get("Points", "0")
187
+ if score is None:
188
+ score = "0"
189
+ contest_points = int(score)
190
+ return contest_points
191
+ return 0
192
+
193
+
194
+ def adif(self):
195
+ """Call the generate ADIF function"""
196
+ gen_adif(self, cabrillo_name, "DARC VHF Contest")
197
+
198
+
199
+ def edi(self):
200
+ """Generate an edi file"""
201
+ file_encoding = "ascii"
202
+ logger.debug("******EDI*****")
203
+ logger.debug("Station: %s", f"{self.station}")
204
+ logger.debug("Contest: %s", f"{self.contest_settings}")
205
+ now = datetime.datetime.now()
206
+ date_time = now.strftime("%Y-%m-%d_%H-%M-%S")
207
+ filename = (
208
+ str(Path.home())
209
+ + "/"
210
+ + f"{self.station.get('Call', '').upper()}_{cabrillo_name}_{date_time}.edi"
211
+ )
212
+ logger.debug("%s", filename)
213
+ log = self.database.fetch_all_contacts_asc()
214
+ try:
215
+ with open(filename, "w", encoding=file_encoding, newline="") as file_descriptor:
216
+ output_cabrillo_line(
217
+ "[REG1TEST;1]",
218
+ "\r\n",
219
+ file_descriptor,
220
+ file_encoding,
221
+ )
222
+ output_cabrillo_line(
223
+ f"TName: {cabrillo_name}",
224
+ "\r\n",
225
+ file_descriptor,
226
+ file_encoding,
227
+ )
228
+ value = self.contest_settings.get("StartDate")
229
+ loggedyear = value[0:4]
230
+ loggedmonth = value[5:7]
231
+ loggedday = value[8:10]
232
+ loggeddate = loggedyear + loggedmonth + loggedday
233
+ output_cabrillo_line(
234
+ f"TDate: {loggeddate}",
235
+ "\r\n",
236
+ file_descriptor,
237
+ file_encoding,
238
+ )
239
+ output_cabrillo_line(
240
+ f"PCall: {self.station.get('Call','')}",
241
+ "\r\n",
242
+ file_descriptor,
243
+ file_encoding,
244
+ )
245
+ output_cabrillo_line(
246
+ f"PWWLo: {self.station.get('GridSquare','')}",
247
+ "\r\n",
248
+ file_descriptor,
249
+ file_encoding,
250
+ )
251
+ output_cabrillo_line(
252
+ f"PExch: ",
253
+ "\r\n",
254
+ file_descriptor,
255
+ file_encoding,
256
+ )
257
+ output_cabrillo_line(
258
+ f"PAdr1: {self.station.get('Street1', '')}, {self.station.get('Zip', '')} {self.station.get('City', '')}, {self.station.get('Country', '')}",
259
+ "\r\n",
260
+ file_descriptor,
261
+ file_encoding,
262
+ )
263
+ output_cabrillo_line(
264
+ f"PAdr2:",
265
+ "\r\n",
266
+ file_descriptor,
267
+ file_encoding,
268
+ )
269
+ output_cabrillo_line(
270
+ f"PSect:{self.contest_settings.get('OperatorCategory','')}",
271
+ "\r\n",
272
+ file_descriptor,
273
+ file_encoding,
274
+ )
275
+ BandInMHz = bandinMHz(self.contest_settings.get("BandCategory", ""))
276
+ output_cabrillo_line(
277
+ f"PBand:{BandInMHz}",
278
+ "\r\n",
279
+ file_descriptor,
280
+ file_encoding,
281
+ )
282
+ output_cabrillo_line(
283
+ f"PClub:{self.station.get('Club', '').upper()}",
284
+ "\r\n",
285
+ file_descriptor,
286
+ file_encoding,
287
+ )
288
+ output_cabrillo_line(
289
+ f"RName:{self.station.get('Name', '')}",
290
+ "\r\n",
291
+ file_descriptor,
292
+ file_encoding,
293
+ )
294
+ output_cabrillo_line(
295
+ f"RCall:{self.station.get('Call','')}",
296
+ "\r\n",
297
+ file_descriptor,
298
+ file_encoding,
299
+ )
300
+ output_cabrillo_line(
301
+ f"RAdr1:{self.station.get('Street1', '')}",
302
+ "\r\n",
303
+ file_descriptor,
304
+ file_encoding,
305
+ )
306
+ output_cabrillo_line(
307
+ f"RAdr2:{self.station.get('Street2', '')}",
308
+ "\r\n",
309
+ file_descriptor,
310
+ file_encoding,
311
+ )
312
+ output_cabrillo_line(
313
+ f"RPoCo:{self.station.get('Zip', '')}",
314
+ "\r\n",
315
+ file_descriptor,
316
+ file_encoding,
317
+ )
318
+ output_cabrillo_line(
319
+ f"RCity:{self.station.get('City', '')}",
320
+ "\r\n",
321
+ file_descriptor,
322
+ file_encoding,
323
+ )
324
+ output_cabrillo_line(
325
+ f"RCoun:{self.station.get('Country', '')} ",
326
+ "\r\n",
327
+ file_descriptor,
328
+ file_encoding,
329
+ )
330
+ output_cabrillo_line(
331
+ f"RPhon:",
332
+ "\r\n",
333
+ file_descriptor,
334
+ file_encoding,
335
+ )
336
+ output_cabrillo_line(
337
+ f"RHBBS:{self.station.get('Email', '')}",
338
+ "\r\n",
339
+ file_descriptor,
340
+ file_encoding,
341
+ )
342
+ output_cabrillo_line(
343
+ f"MOpe1:",
344
+ "\r\n",
345
+ file_descriptor,
346
+ file_encoding,
347
+ )
348
+ output_cabrillo_line(
349
+ f"MOpe2:",
350
+ "\r\n",
351
+ file_descriptor,
352
+ file_encoding,
353
+ )
354
+ output_cabrillo_line(
355
+ f"STXEq:{self.station.get('stationtxrx', '')}",
356
+ "\r\n",
357
+ file_descriptor,
358
+ file_encoding,
359
+ )
360
+ output_cabrillo_line(
361
+ f"SPowe:{self.contest_settings.get('PowerCategory','')}",
362
+ "\r\n",
363
+ file_descriptor,
364
+ file_encoding,
365
+ )
366
+ output_cabrillo_line(
367
+ f"SRXEq:",
368
+ "\r\n",
369
+ file_descriptor,
370
+ file_encoding,
371
+ )
372
+ output_cabrillo_line(
373
+ f"SAnte:{self.station.get('SAnte', '')}",
374
+ "\r\n",
375
+ file_descriptor,
376
+ file_encoding,
377
+ )
378
+ output_cabrillo_line(
379
+ f"SAntH:{self.station.get('SAntH1', '')}",
380
+ "\r\n",
381
+ file_descriptor,
382
+ file_encoding,
383
+ )
384
+ NumberOfQsos = show_qso(self)
385
+ output_cabrillo_line(
386
+ f"CQSOs:{NumberOfQsos};1",
387
+ "\r\n",
388
+ file_descriptor,
389
+ file_encoding,
390
+ )
391
+ output_cabrillo_line(
392
+ f"CQSOP:{calc_score(self)}",
393
+ "\r\n",
394
+ file_descriptor,
395
+ file_encoding,
396
+ )
397
+ output_cabrillo_line(
398
+ f"CWWLs:0;0;1",
399
+ "\r\n",
400
+ file_descriptor,
401
+ file_encoding,
402
+ )
403
+ output_cabrillo_line(
404
+ f"CWWLB:0",
405
+ "\r\n",
406
+ file_descriptor,
407
+ file_encoding,
408
+ )
409
+ output_cabrillo_line(
410
+ f"CExcs:0;0;1",
411
+ "\r\n",
412
+ file_descriptor,
413
+ file_encoding,
414
+ )
415
+ output_cabrillo_line(
416
+ f"CExcB:0",
417
+ "\r\n",
418
+ file_descriptor,
419
+ file_encoding,
420
+ )
421
+ output_cabrillo_line(
422
+ f"CDXCs:0;0;1",
423
+ "\r\n",
424
+ file_descriptor,
425
+ file_encoding,
426
+ )
427
+ output_cabrillo_line(
428
+ f"CDXCB:0",
429
+ "\r\n",
430
+ file_descriptor,
431
+ file_encoding,
432
+ )
433
+ output_cabrillo_line(
434
+ f"CToSc:{calc_score(self)}",
435
+ "\r\n",
436
+ file_descriptor,
437
+ file_encoding,
438
+ )
439
+ output_cabrillo_line(
440
+ f"CODXC:",
441
+ "\r\n",
442
+ file_descriptor,
443
+ file_encoding,
444
+ )
445
+ output_cabrillo_line(
446
+ f"[Remarks]",
447
+ "\r\n",
448
+ file_descriptor,
449
+ file_encoding,
450
+ )
451
+ output_cabrillo_line(
452
+ f"[QSORecords;{NumberOfQsos}]",
453
+ "\r\n",
454
+ file_descriptor,
455
+ file_encoding,
456
+ )
457
+ for contact in log:
458
+ the_date_and_time = contact.get("TS", "")
459
+ themode = contact.get("Mode", "")
460
+ modeCode = 0
461
+ if themode == "LSB" or themode == "USB" or themode == "SSB":
462
+ modeCode = 1
463
+ if themode == "CW" or themode == "CWL" or themode == "CWU":
464
+ modeCode = 2
465
+ frequency = str(int(contact.get("Freq", "0"))).rjust(5)
466
+ loggedyear = the_date_and_time[2:4]
467
+ loggedmonth = the_date_and_time[5:7]
468
+ loggedday = the_date_and_time[8:10]
469
+ loggeddate = loggedyear + loggedmonth + loggedday
470
+ loggedtime = the_date_and_time[11:13] + the_date_and_time[14:16]
471
+ NumberSend = contact.get("SentNr", "")
472
+ NumberReceived = contact.get("NR", "")
473
+ output_cabrillo_line(
474
+ f"{loggeddate};"
475
+ f"{loggedtime};"
476
+ f"{contact.get('Call', '')};"
477
+ f"{modeCode};"
478
+ f"{str(contact.get('SNT', ''))};"
479
+ f"{NumberSend:03d};"
480
+ f"{str(contact.get('RCV', ''))};"
481
+ f"{NumberReceived:03d};"
482
+ f";"
483
+ f"{str(contact.get('Exchange1', ''))};"
484
+ f"{str(contact.get('Points', ''))};"
485
+ f"; ; ; ",
486
+ "\r\n",
487
+ file_descriptor,
488
+ file_encoding,
489
+ )
490
+ self.show_message_box(f"EDI saved to: {filename}")
491
+ except IOError as exception:
492
+ logger.critical("EDI: IO error: %s, writing to %s", exception, filename)
493
+ self.show_message_box(f"Error saving EDI: {exception} {filename}")
494
+ return
495
+
496
+
497
+ def bandinMHz(band):
498
+ switch = {
499
+ "6M": "50 MHz",
500
+ "4M": "70 MHz",
501
+ "2M": "144 MHz",
502
+ "70cm": "432 MHz",
503
+ "23cm": "1,3 GHz",
504
+ }
505
+ return switch.get(band, "Invalid input {band}")
506
+
507
+
508
+ def output_cabrillo_line(line_to_output, ending, file_descriptor, file_encoding):
509
+ """"""
510
+ print(
511
+ line_to_output.encode(file_encoding, errors="ignore").decode(),
512
+ end=ending,
513
+ file=file_descriptor,
514
+ )
515
+
516
+
517
+ def cabrillo(self, file_encoding):
518
+ """Generates Cabrillo file. Maybe."""
519
+ # https://www.cqwpx.com/cabrillo.htm
520
+ logger.debug("******Cabrillo*****")
521
+ logger.debug("Station: %s", f"{self.station}")
522
+ logger.debug("Contest: %s", f"{self.contest_settings}")
523
+ now = datetime.datetime.now()
524
+ date_time = now.strftime("%Y-%m-%d_%H-%M-%S")
525
+ filename = (
526
+ str(Path.home())
527
+ + "/"
528
+ + f"{self.station.get('Call', '').upper()}_{cabrillo_name}_{date_time}.log"
529
+ )
530
+ logger.debug("%s", filename)
531
+ log = self.database.fetch_all_contacts_asc()
532
+ try:
533
+ with open(filename, "w", encoding=file_encoding, newline="") as file_descriptor:
534
+ output_cabrillo_line(
535
+ "START-OF-LOG: 3.0",
536
+ "\r\n",
537
+ file_descriptor,
538
+ file_encoding,
539
+ )
540
+ output_cabrillo_line(
541
+ f"CREATED-BY: Not1MM v{__version__}",
542
+ "\r\n",
543
+ file_descriptor,
544
+ file_encoding,
545
+ )
546
+ output_cabrillo_line(
547
+ f"CONTEST: {cabrillo_name}",
548
+ "\r\n",
549
+ file_descriptor,
550
+ file_encoding,
551
+ )
552
+ if self.station.get("Club", ""):
553
+ output_cabrillo_line(
554
+ f"CLUB: {self.station.get('Club', '').upper()}",
555
+ "\r\n",
556
+ file_descriptor,
557
+ file_encoding,
558
+ )
559
+ output_cabrillo_line(
560
+ f"CALLSIGN: {self.station.get('Call','')}",
561
+ "\r\n",
562
+ file_descriptor,
563
+ file_encoding,
564
+ )
565
+ output_cabrillo_line(
566
+ f"LOCATION: {self.station.get('ARRLSection', '')}",
567
+ "\r\n",
568
+ file_descriptor,
569
+ file_encoding,
570
+ )
571
+ output_cabrillo_line(
572
+ f"CATEGORY-OPERATOR: {self.contest_settings.get('OperatorCategory','')}",
573
+ "\r\n",
574
+ file_descriptor,
575
+ file_encoding,
576
+ )
577
+ output_cabrillo_line(
578
+ f"CATEGORY-ASSISTED: {self.contest_settings.get('AssistedCategory','')}",
579
+ "\r\n",
580
+ file_descriptor,
581
+ file_encoding,
582
+ )
583
+ output_cabrillo_line(
584
+ f"CATEGORY-BAND: {self.contest_settings.get('BandCategory','')}",
585
+ "\r\n",
586
+ file_descriptor,
587
+ file_encoding,
588
+ )
589
+ mode = self.contest_settings.get("ModeCategory", "")
590
+ if mode in ["SSB+CW", "SSB+CW+DIGITAL"]:
591
+ mode = "MIXED"
592
+ output_cabrillo_line(
593
+ f"CATEGORY-MODE: {mode}",
594
+ "\r\n",
595
+ file_descriptor,
596
+ file_encoding,
597
+ )
598
+ output_cabrillo_line(
599
+ f"CATEGORY-TRANSMITTER: {self.contest_settings.get('TransmitterCategory','')}",
600
+ "\r\n",
601
+ file_descriptor,
602
+ file_encoding,
603
+ )
604
+ if self.contest_settings.get("OverlayCategory", "") != "N/A":
605
+ output_cabrillo_line(
606
+ f"CATEGORY-OVERLAY: {self.contest_settings.get('OverlayCategory','')}",
607
+ "\r\n",
608
+ file_descriptor,
609
+ file_encoding,
610
+ )
611
+ output_cabrillo_line(
612
+ f"GRID-LOCATOR: {self.station.get('GridSquare','')}",
613
+ "\r\n",
614
+ file_descriptor,
615
+ file_encoding,
616
+ )
617
+ output_cabrillo_line(
618
+ f"CATEGORY-POWER: {self.contest_settings.get('PowerCategory','')}",
619
+ "\r\n",
620
+ file_descriptor,
621
+ file_encoding,
622
+ )
623
+
624
+ output_cabrillo_line(
625
+ f"CLAIMED-SCORE: {calc_score(self)}",
626
+ "\r\n",
627
+ file_descriptor,
628
+ file_encoding,
629
+ )
630
+ ops = f"@{self.station.get('Call','')}"
631
+ list_of_ops = self.database.get_ops()
632
+ for op in list_of_ops:
633
+ ops += f", {op.get('Operator', '')}"
634
+ output_cabrillo_line(
635
+ f"OPERATORS: {ops}",
636
+ "\r\n",
637
+ file_descriptor,
638
+ file_encoding,
639
+ )
640
+ output_cabrillo_line(
641
+ f"NAME: {self.station.get('Name', '')}",
642
+ "\r\n",
643
+ file_descriptor,
644
+ file_encoding,
645
+ )
646
+ output_cabrillo_line(
647
+ f"ADDRESS: {self.station.get('Street1', '')}",
648
+ "\r\n",
649
+ file_descriptor,
650
+ file_encoding,
651
+ )
652
+ output_cabrillo_line(
653
+ f"ADDRESS-CITY: {self.station.get('City', '')}",
654
+ "\r\n",
655
+ file_descriptor,
656
+ file_encoding,
657
+ )
658
+ output_cabrillo_line(
659
+ f"ADDRESS-STATE-PROVINCE: {self.station.get('State', '')}",
660
+ "\r\n",
661
+ file_descriptor,
662
+ file_encoding,
663
+ )
664
+ output_cabrillo_line(
665
+ f"ADDRESS-POSTALCODE: {self.station.get('Zip', '')}",
666
+ "\r\n",
667
+ file_descriptor,
668
+ file_encoding,
669
+ )
670
+ output_cabrillo_line(
671
+ f"ADDRESS-COUNTRY: {self.station.get('Country', '')}",
672
+ "\r\n",
673
+ file_descriptor,
674
+ file_encoding,
675
+ )
676
+ output_cabrillo_line(
677
+ f"EMAIL: {self.station.get('Email', '')}",
678
+ "\r\n",
679
+ file_descriptor,
680
+ file_encoding,
681
+ )
682
+ for contact in log:
683
+ the_date_and_time = contact.get("TS", "")
684
+ themode = contact.get("Mode", "")
685
+ if themode == "LSB" or themode == "USB":
686
+ themode = "PH"
687
+ frequency = str(int(contact.get("Freq", "0"))).rjust(5)
688
+
689
+ loggeddate = the_date_and_time[:10]
690
+ loggedtime = the_date_and_time[11:13] + the_date_and_time[14:16]
691
+ output_cabrillo_line(
692
+ f"QSO: {frequency} {themode} {loggeddate} {loggedtime} "
693
+ f"{contact.get('StationPrefix', '').ljust(13)} "
694
+ f"{str(contact.get('SNT', '')).ljust(3)} "
695
+ f"{str(contact.get('SentNr', '')).ljust(6)} "
696
+ f"{contact.get('Call', '').ljust(13)} "
697
+ f"{str(contact.get('RCV', '')).ljust(3)} "
698
+ f"{str(contact.get('NR', '')).ljust(6)}",
699
+ "\r\n",
700
+ file_descriptor,
701
+ file_encoding,
702
+ )
703
+ output_cabrillo_line("END-OF-LOG:", "\r\n", file_descriptor, file_encoding)
704
+ self.show_message_box(f"Cabrillo saved to: {filename}")
705
+ except IOError as exception:
706
+ logger.critical("cabrillo: IO error: %s, writing to %s", exception, filename)
707
+ self.show_message_box(f"Error saving Cabrillo: {exception} {filename}")
708
+ return
709
+
710
+
711
+ def recalculate_mults(self):
712
+ """Recalculates multipliers after change in logged qso."""
713
+
714
+ all_contacts = self.database.fetch_all_contacts_asc()
715
+ for contact in all_contacts:
716
+ # recalculate points
717
+ _their_grid = contact.get("Exchange1").upper()
718
+ _kilometers = distance(self.station.get("GridSquare", ""), _their_grid)
719
+ _points = max(1, _kilometers)
720
+ contact["Points"] = _points
721
+
722
+ self.database.change_contact(contact)
723
+
724
+
725
+ def process_esm(self, new_focused_widget=None, with_enter=False):
726
+ """ESM State Machine"""
727
+
728
+ # self.pref["run_state"]
729
+
730
+ # -----===== Assigned F-Keys =====-----
731
+ # self.esm_dict["CQ"]
732
+ # self.esm_dict["EXCH"]
733
+ # self.esm_dict["QRZ"]
734
+ # self.esm_dict["AGN"]
735
+ # self.esm_dict["HISCALL"]
736
+ # self.esm_dict["MYCALL"]
737
+ # self.esm_dict["QSOB4"]
738
+
739
+ # ----==== text fields ====----
740
+ # self.callsign
741
+ # self.sent
742
+ # self.receive
743
+ # self.other_1
744
+ # self.other_2
745
+
746
+ if new_focused_widget is not None:
747
+ self.current_widget = self.inputs_dict.get(new_focused_widget)
748
+
749
+ # print(f"checking esm {self.current_widget=} {with_enter=} {self.pref.get("run_state")=}")
750
+
751
+ for a_button in [
752
+ self.F1,
753
+ self.F2,
754
+ self.F3,
755
+ self.F4,
756
+ self.F5,
757
+ self.F6,
758
+ self.F7,
759
+ self.F8,
760
+ self.F9,
761
+ self.F10,
762
+ self.F11,
763
+ self.F12,
764
+ ]:
765
+ self.restore_button_color(a_button)
766
+
767
+ buttons_to_send = []
768
+
769
+ if self.pref.get("run_state"):
770
+ if self.current_widget == "callsign":
771
+ if len(self.callsign.text()) < 3:
772
+ self.make_button_green(self.esm_dict["CQ"])
773
+ buttons_to_send.append(self.esm_dict["CQ"])
774
+ elif len(self.callsign.text()) > 2:
775
+ self.make_button_green(self.esm_dict["HISCALL"])
776
+ self.make_button_green(self.esm_dict["EXCH"])
777
+ buttons_to_send.append(self.esm_dict["HISCALL"])
778
+ buttons_to_send.append(self.esm_dict["EXCH"])
779
+
780
+ elif self.current_widget in ["other_1", "other_2"]:
781
+ if self.other_2.text() == "" or self.other_1.text() == "":
782
+ self.make_button_green(self.esm_dict["AGN"])
783
+ buttons_to_send.append(self.esm_dict["AGN"])
784
+ else:
785
+ self.make_button_green(self.esm_dict["QRZ"])
786
+ buttons_to_send.append(self.esm_dict["QRZ"])
787
+ buttons_to_send.append("LOGIT")
788
+
789
+ if with_enter is True and bool(len(buttons_to_send)):
790
+ for button in buttons_to_send:
791
+ if button:
792
+ if button == "LOGIT":
793
+ self.save_contact()
794
+ continue
795
+ self.process_function_key(button)
796
+ else:
797
+ if self.current_widget == "callsign":
798
+ if len(self.callsign.text()) > 2:
799
+ self.make_button_green(self.esm_dict["MYCALL"])
800
+ buttons_to_send.append(self.esm_dict["MYCALL"])
801
+
802
+ elif self.current_widget in ["other_1", "other_2"]:
803
+ if self.other_2.text() == "" or self.other_1.text() == "":
804
+ self.make_button_green(self.esm_dict["AGN"])
805
+ buttons_to_send.append(self.esm_dict["AGN"])
806
+ else:
807
+ self.make_button_green(self.esm_dict["EXCH"])
808
+ buttons_to_send.append(self.esm_dict["EXCH"])
809
+ buttons_to_send.append("LOGIT")
810
+
811
+ if with_enter is True and bool(len(buttons_to_send)):
812
+ for button in buttons_to_send:
813
+ if button:
814
+ if button == "LOGIT":
815
+ self.save_contact()
816
+ continue
817
+ self.process_function_key(button)
818
+
819
+
820
+ def populate_history_info_line(self):
821
+ result = self.database.fetch_call_history(self.callsign.text())
822
+ if result:
823
+ self.history_info.setText(
824
+ f"{result.get('Call', '')}, {result.get('Name', '')}, {result.get('Exch1', '')}, {result.get('UserText','...')}"
825
+ )
826
+ else:
827
+ self.history_info.setText("")
828
+
829
+
830
+ def check_call_history(self):
831
+ """"""
832
+ result = self.database.fetch_call_history(self.callsign.text())
833
+ if result:
834
+ self.history_info.setText(f"{result.get('UserText','')}")
835
+ if self.other_1.text() == "":
836
+ self.other_1.setText(f"{result.get('Exch1', '')}")
837
+
838
+
839
+ def get_mults(self):
840
+ """Get mults for RTC XML"""
841
+ mults = {}
842
+ mults["state"], mults["wpxprefix"] = show_mults(self, rtc=True)
843
+ return mults
844
+
845
+
846
+ def just_points(self):
847
+ """Get points for RTC XML"""
848
+ return get_points(self)