cabrillo

A Python library to parse Cabrillo-format amateur radio contest logs.


License
BSD-3-Clause
Install
pip install cabrillo==0.0.4

Documentation

cabrillo Build Status

A Python library to parse Cabrillo-format amateur radio contest logs, with no external dependencies.

Getting Started

Basic Parsing

>>> from cabrillo.parser import parse_log_file
>>> cab = parse_log_file('tests/CQWPX.log')
>>> cab.callsign
'AA1ZZZ'
>>> cab.qso
[<cabrillo.qso.QSO object at 0x10cb09f28>, <cabrillo.qso.QSO object at 0x10cbc8860>]
>>> cab.text()
'START-OF-LOG: 3.0\nCREATED-BY: WriteLog V10.72C\nCALLSIGN: AA1ZZZ\n[...snip...]END-OF-LOG:\n'

You can also write to a file:

with open('out.cbr', 'w') as o:
    cab.write(o)

The same works for text-file-like objects.

Finally, if you desire to parse Cabrillo data already present as a Python string, you can do so with, e.g.,

from cabrillo.parser import parse_log_text

cabrillo_text = """START-OF-LOG: 3.0
END-OF-LOG:
"""

cab = parse_log_text(cabrillo_text)

Construct a Log

For an up-to-date list of attributes to use in constructing objects manually, see cabrillo/cabrillo.py and cabrillo/qso.py. This is helpful if your software maintains QSOs in a different database format and you are just exporting logs in the very end. For example:

>>> from cabrillo import QSO, Cabrillo
>>> from datetime import datetime
# >>> help(QSO)
# >>> help(Cabrillo)
>>> qso1 = QSO('14313', 'PH', datetime.strptime('May 30 2018 10:15PM', '%b %d %Y %I:%M%p'), 'KX0XXX', 'KX9XXX', de_exch=['59', '10', 'CO'], dx_exch=['44', '20', 'IN'], t=None)
>>> cab = Cabrillo(callsign='KX0XXX', email='kx0xxx@example.com', soapbox=['Son of a gun!'])
>>> print(cab.text())
START-OF-LOG: 3.0
CALLSIGN: KX0XXX
EMAIL: kx0xxx@example.com
CREATED-BY: cabrillo (Python)
SOAPBOX: Son of a gun!
END-OF-LOG:

Matching Two QSOs in Contest Scoring

>>> # We start off with a pair with complementary data.
>>> from cabrillo import QSO
>>> from datetime import datetime
>>> qso1 = QSO('14313', 'PH', datetime.strptime('May 30 2018 10:15PM', '%b %d %Y %I:%M%p'), 'KX0XXX', 'KX9XXX', de_exch=['59', '10', 'CO'], dx_exch=['44', '20', 'IN'], t=None)
>>> qso2 = QSO('14313', 'PH', datetime.strptime('May 30 2018 10:10PM', '%b %d %Y %I:%M%p'), 'KX9XXX', 'KX0XXX', de_exch=['44', '20', 'IN'], dx_exch=['59', '10', 'CO'], t=None)
>>> qso1.match_against(qso2)
True
>>> qso1.freq = '14000'  # Same band, still will match.
>>> qso1.match_against(qso2)
True
>>> qso1.match_against(qso2, max_time_delta=1)  # Make time checking less lenient.
False
>>> # All flags.
>>> qso1.match_against(qso2, max_time_delta=30, check_exch=True, check_band=True)

Tips

Ignoring Malorder

Cabrillo logs must be time-sorted. If you want to read files that are not so sorted, but other than that are Cabrillo files, you can do so by adding a keyword argument ignore_order=False to either parse_log_file or parse_log_text. If you do that, the resulting Cabrillo object will refuse to generate (potentially non-)Cabrillo output.

Contributing

Pull requests are appreciated! Please test your changes using pytest.

# Activate a virtual environment using your favorite tool.
$ pip -r requirements_test.txt
$ pytest --cov-report term-missing --cov
*snip*
---------- coverage: platform linux, python 3.12.1-final-0 -----------
Name                       Stmts   Miss  Cover   Missing
--------------------------------------------------------
cabrillo/__init__.py           4      0   100%
cabrillo/cabrillo.py          60      0   100%
cabrillo/data.py              44      0   100%
cabrillo/errors.py             3      0   100%
cabrillo/parser.py            62      0   100%
cabrillo/qso.py               59      0   100%
tests/path_helper.py           6      0   100%
tests/test_cabrillo.py        62      0   100%
tests/test_parser_log.py      89      0   100%
tests/test_parser_qso.py      51      0   100%
tests/test_qso.py            100      0   100%
--------------------------------------------------------
TOTAL                        540      0   100%


====================================================================== 24 passed in 0.21s =======================================================================