Skip to main content

cabrillo

build workflow badge

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', grid_locator='AB12', soapbox=['Son of a gun!', 'DX IS!'])
>>> cab.qso.append(qso1)
>>> print(cab.text())
START-OF-LOG: 3.0
CALLSIGN: KX0XXX
EMAIL: kx0xxx@example.com
GRID-LOCATOR: AB12
CREATED-BY: cabrillo (Python)
SOAPBOX: Son of a gun!
SOAPBOX: DX IS!
QSO: 14313 PH 2018-05-30 2215 KX0XXX 59 10 CO KX9XXX 44 20 IN
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 install -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 =======================================================================

Metadata

Release files for cabrillo 0.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for cabrillo 0.3.0
File Size Uploaded Provenance Actions
cabrillo-0.3.0.tar.gz 21.2 kB Without provenance Details for cabrillo-0.3.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for cabrillo 0.3.0
File Interpreter ABI Platform Actions
cabrillo-0.3.0-py3-none-any.whl Python 3 none any Details for cabrillo-0.3.0-py3-none-any.whl

Total release size: 36.4 kB

Release files / cabrillo-0.3.0.tar.gz

Download URL cabrillo-0.3.0.tar.gz
Size 21.2 kB
Tags Source
SHA-256 checksum
How to use checksums
7f09acc8cd22858164b3dc08ff1758a00ea94ad38357b4db5cf3de387ab106ee
BLAKE2b-256 checksum
How to use checksums
d0f113c0bb75934df397888ee798b555643369bfeef349101452e110192385d1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.14.5

Release files / cabrillo-0.3.0-py3-none-any.whl

Download URL cabrillo-0.3.0-py3-none-any.whl
Size 15.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
bed71db527fa4affe9bae9a4e88e752a169115cf18b046b301cb86698282b25b
BLAKE2b-256 checksum
How to use checksums
bb7c8fe02a74bd309a0ccf83ae3d3dc509c386a59b117338054e19b3e74a37f5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.14.5

Security for cabrillo 0.3.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page