cabrillo
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)
| 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)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| 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.