-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathget_lotw_adif.py
218 lines (185 loc) · 9.29 KB
/
get_lotw_adif.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
#!/usr/binenv python3
"""
Get ADIF data files from LoTW.
Functionality factored out of the adif-log-analyzer,
it is not appropriate for that application to fetch data,
only to analyze it.
"""
__author__ = 'Jeffrey B. Otterson, N1KDO'
__copyright__ = 'Copyright 2020, 2021, 2023 Jeffrey B. Otterson'
__license__ = 'Simplified BSD'
__version__ = '0.05'
import argparse
import logging
import os.path
import time
import adif
import adif_log_analyzer
def get_file_date_size(filename):
if os.path.exists(filename):
file_timestamp = os.path.getmtime(filename)
file_size = os.path.getsize(filename)
return file_timestamp, file_size
else:
return None, None
def show_file_info(filename):
file_timestamp, file_size = get_file_date_size(filename)
if filename is None:
print('{} does not exist.'.format(filename))
else:
print('{} created {}, {} bytes'.format(filename, time.ctime(file_timestamp), file_size))
def menu():
valid_choices = '0123456'
while True:
print('1. Download complete LoTW ADIF')
print('2. Download updates to existing LoTW ADIF')
print('3. Download DXCC QSL cards ADIF')
print('4. Save current LoTW ADIF')
print('5. Merge LoTW and DXCC QSLs into combined ADIF')
print('6. Draw the charts from this data')
print('0. Exit this program')
print()
choice = input('Your Choice? ')
if len(choice) == 1 and choice in valid_choices:
return choice
print('bad choice.')
def get_password(password):
if password is not None and len(password) >= 6:
return password
while True:
password = input('Enter your LoTW Password: ')
if password is not None and len(password) > 0:
return password
def main():
parser = argparse.ArgumentParser(description='get LoTW Records and plot')
parser.add_argument('--debug', action='store_true', help='show logging informational output')
parser.add_argument('--info', action='store_true', help='show informational diagnostic output')
parser.add_argument('--login-callsign', type=str, help='Callsign to use to log in to LoTW')
parser.add_argument('--password', type=str, help='Password to use to log into to LoTW')
parser.add_argument('--callsign', type=str, help='Callsign to analyze records for')
args = parser.parse_args()
log_format = '%(asctime)s.%(msecs)03d %(levelname)-8s %(message)s'
log_date_format = '%Y-%m-%d %H:%M:%S'
if args.debug:
logging.basicConfig(format=log_format, datefmt=log_date_format, level=logging.DEBUG)
elif args.info:
logging.basicConfig(format=log_format, datefmt=log_date_format, level=logging.INFO)
else:
logging.basicConfig(format=log_format, datefmt=log_date_format, level=logging.WARNING)
logging.Formatter.converter = time.gmtime
login_callsign = ''
callsign = ''
password = None
if args.callsign is not None:
callsign = args.callsign
if args.login_callsign is None:
login_callsign = args.callsign
if args.login_callsign is not None:
login_callsign = args.login_callsign
if args.password is not None:
password = args.password
data_dir = 'data/'
if not os.path.isdir(data_dir):
print(f'cannot find data directory {data_dir}, creating...')
os.mkdir(data_dir)
if not os.path.isdir(adif_log_analyzer.charts_dir):
print(f'cannot find data directory {adif_log_analyzer.charts_dir}, creating...')
os.mkdir(adif_log_analyzer.charts_dir)
while len(login_callsign) < 3:
login_callsign = input('Please enter your lotw login callsign: ')
while len(callsign) < 3:
callsign = input(f'Please enter your callsign ({login_callsign}): ')
if callsign == '':
callsign = login_callsign
filename_callsign = callsign.replace('/', '-')
lotw_adif_file_name = '{}{}-lotw.adif'.format(data_dir, filename_callsign)
lotw_adif_new_qsos_file_name = '{}{}-lotw-new-qsos.adif'.format(data_dir, filename_callsign)
lotw_adif_new_qsls_file_name = '{}{}-lotw-new-qsls.adif'.format(data_dir, filename_callsign)
dxcc_qsls_file_name = '{}{}-cards.adif'.format(data_dir, filename_callsign)
if os.path.exists(lotw_adif_file_name):
lotw_header, lotw_qsos = adif.read_adif_file(lotw_adif_file_name)
if lotw_header.get('app_lotw_lastqsl') is None:
lotw_header['app_lotw_lastqsl'] = lotw_header.get('app_lotw_lastqsorx')
else:
lotw_header = None
lotw_qsos = None
if os.path.exists(dxcc_qsls_file_name):
dxcc_qsls_header, dxcc_qsl_cards = adif.read_adif_file(dxcc_qsls_file_name)
else:
dxcc_qsls_header = None
dxcc_qsl_cards = None
last_qso_date = None
last_qsl_date = None
while True:
print('---------------------------------------')
show_file_info(lotw_adif_file_name)
if lotw_header is not None:
print('{} QSOs'.format(len(lotw_qsos)))
last_qso_date = lotw_header.get('app_lotw_lastqsorx')
if last_qso_date is not None:
print('Last QSO Received {}'.format(last_qso_date))
last_qsl_date = lotw_header.get('app_lotw_lastqsl')
if last_qsl_date is not None:
print('Last QSL Received {}'.format(last_qsl_date))
print('---------------------------------------')
show_file_info(dxcc_qsls_file_name)
if dxcc_qsls_header is not None:
dxcc_record_updated = dxcc_qsls_header.get('app_lotw_dxccrecord_updated')
if dxcc_record_updated is not None:
print('DXCC Record Updated {}'.format(dxcc_record_updated))
# print(dxcc_qsls_header)
print('{} DXCC QSL Cards'.format(len(dxcc_qsl_cards)))
print('---------------------------------------')
choice = menu()
if choice == '0':
exit()
elif choice == '1':
password = get_password(password)
lotw_header, lotw_qsos = adif.get_lotw_adif(login_callsign, password, callsign, filename=lotw_adif_file_name)
elif choice == '2':
if last_qso_date is None:
print('Cannot update, no base, download first.')
else:
password = get_password(password)
logging.info(f'fetching new QSOs since {last_qso_date}')
try:
new_lotw_qsos_header, new_lotw_qsos = adif.get_lotw_adif(login_callsign,
password,
callsign,
filename=lotw_adif_new_qsos_file_name,
qso_qsorxsince=last_qso_date)
new_last_qso_date = lotw_header.get('app_lotw_lastqsorx')
logging.info(
'New last QSO Received {}, {} QSO records'.format(new_last_qso_date, len(new_lotw_qsos)))
lotw_header, lotw_qsos = adif.merge(lotw_header, lotw_qsos, new_lotw_qsos)
if new_lotw_qsos_header.get('app_lotw_lastqsorx') is not None:
lotw_header['app_lotw_lastqsorx'] = new_lotw_qsos_header.get('app_lotw_lastqsorx')
logging.info(f'fetching new QSLs since {last_qsl_date}')
new_lotw_qsls_header, new_lotw_qsls = adif.call_lotw(login=login_callsign,
password=password,
filename=lotw_adif_new_qsls_file_name,
qso_owncall=callsign,
qso_qsl='yes',
qso_qsldetail='yes',
qso_qslsince=last_qsl_date,
qso_query='1'
)
lotw_header, lotw_qsos = adif.merge(lotw_header, lotw_qsos, new_lotw_qsls)
if new_lotw_qsls_header.get('app_lotw_lastqsl') is not None:
lotw_header['app_lotw_lastqsl'] = new_lotw_qsls_header.get('app_lotw_lastqsl')
except Exception as ex:
print(ex)
elif choice == '3':
password = get_password(password)
dxcc_qsls_header, dxcc_qsl_cards = adif.get_qsl_cards(login_callsign, password, dxcc_qsls_file_name)
elif choice == '4': # save lotw qsos data
adif.write_adif_file(lotw_header, lotw_qsos, lotw_adif_file_name, abridge_results=False)
elif choice == '5':
if lotw_header is None or dxcc_qsls_header is None:
print('need both lotw qsos and dxcc cards in order to merge. sorry.')
else:
lotw_qsos = adif.combine_qsos(lotw_qsos, dxcc_qsl_cards)
elif choice == '6':
adif_log_analyzer.draw_charts(lotw_qsos, callsign)
if __name__ == '__main__':
main()