@@ 0,0 1,248 @@
+--
+-- Copyright (c) 2022-2024 Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
+--
+-- Permission is hereby granted, free of charge, to any person obtaining a copy
+-- of this software and associated documentation files (the "Software"), to deal
+-- in the Software without restriction, including without limitation the rights
+-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+-- copies of the Software, and to permit persons to whom the Software is
+-- furnished to do so, subject to the following conditions:
+--
+-- The above copyright notice and this permission notice shall be included in
+-- all copies or substantial portions of the Software.
+--
+-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+-- SOFTWARE.
+--
+
+local mt = { }
+
+local function county(counties, state_dxccs, duphash)
+ local out = { }
+
+ local tmp = hlog.set.new()
+ tmp:add(state_dxccs)
+ state_dxccs = tmp
+
+ local county_dxccs = hlog.set.new()
+ local states = hlog.set.new()
+ local name2abbrev = { }
+ local abbrev2name = { }
+ for _, info in ipairs(counties) do
+ local dxcc = info[1]
+ local a = info[3]
+ local n = info[2] .. info[4]
+ local s = info[2]
+
+ state_dxccs:add(dxcc)
+ county_dxccs:add(dxcc)
+ states:add(s)
+
+ assert(name2abbrev[n] == nil)
+ name2abbrev[n] = a
+
+ assert(abbrev2name[a] == nil)
+ abbrev2name[a] = info
+ end
+
+ -- NOTE: dxcc-state is a superset of dxcc-county
+ out['dxcc-county'] = county_dxccs -- set of dxccs that may use counties
+ out['dxcc-state'] = state_dxccs -- set of dxccs that may use states
+ out['states-county'] = states -- set of states that use counties
+ out['county'] = counties -- the raw data from the user
+ out['n2a'] = name2abbrev -- map from <state,name> to abbrev
+ out['a2n'] = abbrev2name -- map from abbrev to info
+ out['dups'] = hlog.set.new() -- set of dup hashes
+
+ -- set up an object with the data
+ local obj = {
+ duphash = duphash,
+ data = out,
+ }
+
+ setmetatable(obj, mt)
+
+ return obj
+end
+
+local function name2abbrev(self, dxcc, state, county)
+ local map = self.data
+
+ -- outside of DXCCs that use states or counties
+ if not map['dxcc-state']:has(dxcc) and not map['dxcc-county']:has(dxcc) then
+ return "DX"
+ end
+
+ -- inside the 'states' but outside the 'counties' area
+ if not map['states-county']:has(state) then
+ return state
+ end
+
+ -- inside the 'counties' area
+ assert(county ~= nil)
+
+ return map['n2a'][state .. county]
+end
+
+local function abbrev2name(self, abbrev)
+ local map = self.data
+
+ local info = map['a2n'][abbrev]
+
+ -- inside 'counties' area: return state & county
+ if info ~= nil then
+ return info[2], info[4]
+ end
+
+ -- outside 'counties' area, but inside 'states' area: return state
+ if abbrev ~= nil and abbrev ~= "DX" then
+ return abbrev, nil
+ end
+
+ -- outside 'counties' and 'states' area
+ return nil, nil
+end
+
+local function dxcc_use_state(self, dxcc)
+ if dxcc == nil then
+ return false
+ end
+
+ return self.data['dxcc-state']:has(dxcc)
+end
+
+local function dxcc_use_county(self, dxcc)
+ if dxcc == nil then
+ return false
+ end
+
+ return self.data['dxcc-county']:has(dxcc)
+end
+
+local function state_use_county(self, dxcc, state)
+ local map = self.data
+
+ if dxcc == nil or state == nil then
+ return false
+ end
+
+ return map['dxcc-county']:has(dxcc) and map['states-county']:has(state)
+end
+
+local function qso_done(self, qso, tx, rx)
+ local map = self.data
+
+ tx = tx ~= nil and tx or qso.additional['contest-tx']
+ rx = rx ~= nil and rx or qso.additional['contest-rx']
+
+ map['dups']:add(self.duphash(qso, tx, rx))
+end
+
+-- is the passed in qso a duplicate with a previous qso?
+local function is_dup(self, qso, tx, rx)
+ local map = self.data
+
+ tx = tx ~= nil and tx or qso.additional['contest-tx']
+ rx = rx ~= nil and rx or qso.additional['contest-rx']
+
+ return map['dups']:has(self.duphash(qso, tx, rx))
+end
+
+local function export_prep(self, id, year, qpoints, qmult, qexchange)
+ local function do_add_entry(tx, rx, args)
+ local qp = args[1]
+ local all = args[2]
+ local ops = args[3]
+ local points = args[4]
+ local mults = args[5]
+ local qso = args[6]
+
+ local tx_exch, tx_exch_ok = qexchange(qso.tx, tx)
+ local rx_exch, rx_exch_ok = qexchange(qso.rx, rx)
+
+ -- accumulate points
+ if not self:is_dup(qso, tx, rx) and tx_exch_ok and rx_exch_ok then
+ points[1] = points[1] + qpoints(qso, tx, rx)
+ end
+
+ -- add the mult
+ local m = qmult(qso, tx, rx)
+ if m ~= nil then
+ mults:add(m)
+ end
+
+ -- add to list of all QSOs
+ table.insert(all, { qso, tx_exch, rx_exch, tx_exch_ok and rx_exch_ok })
+
+ -- stash for future dup detection
+ self:qso_done(qso, tx, rx)
+
+ -- keep track of the operators
+ ops:add(qso.tx.operator_call)
+ end
+
+ local function add_entry(id, year, station, all, ops, points, mults, qso)
+ if qso.additional['contest-id'] ~= id or
+ qso.additional['contest-year'] ~= year then
+ return
+ end
+
+ if qso.tx.band == nil then
+ print(string.format("Error: %s QSO lacks band info", qso.uuid))
+ return
+ end
+
+ assert(qso.tx.station_call ~= nil)
+ station:add(qso.tx.station_call)
+
+ hlog.utils.each_with_each(qso.additional['contest-tx'],
+ qso.additional['contest-rx'],
+ "+", do_add_entry,
+ { self, all, ops, points, mults, qso })
+ end
+
+ local station = hlog.set.new()
+ local all = {}
+ local ops = hlog.set.new()
+ local points = { 0 }
+ local mults = hlog.set.new()
+
+ for qso in hlog.index.history(true) do
+ add_entry(id, year, station, all, ops, points, mults, qso)
+ end
+
+ if station:count_items() ~= 1 then
+ error(string.format("Error: more than one station callsign used: %s",
+ station:to_string()))
+ end
+
+ station = hlog.utils.join_ipairs(station:as_array(), ",") -- should be only 1 element
+ points = points[1]
+
+ return all, station, ops, points, mults
+end
+
+local function tostring(self)
+ return "County{" .. self.data:tostring() .. "}"
+end
+
+mt.__index = {
+ name2abbrev = name2abbrev,
+ abbrev2name = abbrev2name,
+ dxcc_use_state = dxcc_use_state,
+ dxcc_use_county = dxcc_use_county,
+ state_use_county = state_use_county,
+ qso_done = qso_done,
+ is_dup = is_dup,
+ export_prep = export_prep,
+ tostring = tostring,
+}
+
+return {
+ county = county,
+}