Skip to content

Commit

Permalink
Merge pull request #4 from ftl/export_cabrillo_dialog
Browse files Browse the repository at this point in the history
add an dialog to enter Cabrillo data before exporting
  • Loading branch information
ftl authored Dec 24, 2024
2 parents 3420f98 + 23f8bc0 commit c442bff
Show file tree
Hide file tree
Showing 9 changed files with 657 additions and 68 deletions.
55 changes: 31 additions & 24 deletions core/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,22 +70,23 @@ type Controller struct {
callHistoryFinder *callhistory.Finder
hamDXMap *hamdxmap.HamDXMap

VFO *vfo.VFO
Logbook *logbook.Logbook
QSOList *logbook.QSOList
Entry *entry.Controller
Workmode *workmode.Controller
Radio *radio.Controller
Keyer *keyer.Keyer
Callinfo *callinfo.Callinfo
Score *score.Counter
Rate *rate.Counter
ServiceStatus *ServiceStatus
NewContest *newcontest.Controller
Settings *settings.Settings
Bandmap *bandmap.Bandmap
Clusters *cluster.Clusters
Parrot *parrot.Parrot
VFO *vfo.VFO
Logbook *logbook.Logbook
QSOList *logbook.QSOList
Entry *entry.Controller
Workmode *workmode.Controller
Radio *radio.Controller
Keyer *keyer.Keyer
Callinfo *callinfo.Callinfo
Score *score.Counter
Rate *rate.Counter
ServiceStatus *ServiceStatus
NewContestController *newcontest.Controller
ExportCabrilloController *cabrillo.Controller
Settings *settings.Settings
Bandmap *bandmap.Bandmap
Clusters *cluster.Clusters
Parrot *parrot.Parrot
}

// View defines the visual functionality of the main application window.
Expand Down Expand Up @@ -146,7 +147,8 @@ func (c *Controller) Startup() {
c.configuration.Contest(),
)
c.callHistoryFinder.Notify(c.Settings)
c.NewContest = newcontest.NewController(c.Settings, c.configuration.LogDirectory())
c.NewContestController = newcontest.NewController(c.Settings, c.configuration.LogDirectory())
c.ExportCabrilloController = cabrillo.NewController()

c.bandplan = bandplan.IARURegion1 // TODO: make the bandplan configurable
c.dxccFinder = dxcc.New()
Expand Down Expand Up @@ -396,7 +398,7 @@ func proposeFilename(contestName, callsign string) string {

func (c *Controller) New() {
var err error
newContest, ok := c.NewContest.Run()
newContest, ok := c.NewContestController.Run()
if !ok {
return
}
Expand Down Expand Up @@ -538,6 +540,12 @@ func (c *Controller) SaveAs() {
}

func (c *Controller) ExportCabrillo() {
var err error
export, openCabrilloFile, ok := c.ExportCabrilloController.Run(c.Settings, c.Score.Result(), c.QSOList.All())
if !ok {
return
}

proposedName := c.proposeFilename() + ".cabrillo"
filename, ok, err := c.view.SelectSaveFile("Export Cabrillo File", c.configuration.LogDirectory(), proposedName, "*.cabrillo")
if !ok {
Expand All @@ -555,16 +563,15 @@ func (c *Controller) ExportCabrillo() {
}
defer file.Close()

err = cabrillo.Export(
file,
c.Settings,
c.Score.Result(),
c.QSOList.All()...)
err = cabrillo.Export(file, export)
if err != nil {
c.view.ShowErrorDialog("Cannot export Cabrillo to %s: %v", filename, err)
return
}
c.openWithExternalApplication(filename)

if openCabrilloFile {
c.openWithExternalApplication(filename)
}
}

func (c *Controller) ExportADIF() {
Expand Down
204 changes: 195 additions & 9 deletions core/export/cabrillo/cabrillo.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,209 @@ package cabrillo
import (
"fmt"
"io"
"log"
"strings"
"text/template"
"time"

"github.com/ftl/cabrillo"
"github.com/ftl/conval"
"github.com/ftl/hamradio/callsign"
"github.com/ftl/hellocontest/core"
)

// Export writes the given QSOs to the given writer in the Cabrillo format.
// The header is very limited and needs to be completed manually after the log was written.
func Export(w io.Writer, settings core.Settings, claimedScore int, qsos ...core.QSO) error {
type View interface {
Show() bool

SetCategoryAssisted(bool)
SetCategoryBand(string)
SetCategoryMode(string)
SetCategoryOperator(string)
SetCategoryPower(string)
SetName(string)
SetEmail(string)
SetOpenAfterExport(bool)
}

type Controller struct {
view View

definition *conval.Definition
category cabrillo.Category
name string
email string
openAfterExport bool
}

func NewController() *Controller {
result := &Controller{
openAfterExport: false,
}

return result
}

func (c *Controller) SetView(view View) {
if view == nil {
panic("cabrillo.Controller.SetView must not be called with nil")
}
if c.view != nil {
panic("cabrillo.Controller.SetView was already called")
}

c.view = view
}

func (c *Controller) Run(settings core.Settings, claimedScore int, qsos []core.QSO) (*cabrillo.Log, bool, bool) {
c.definition = settings.Contest().Definition

c.updateCategorySettings()
c.view.SetName(c.name)
c.view.SetEmail(c.email)
c.view.SetOpenAfterExport(c.openAfterExport)
accepted := c.view.Show()
if !accepted {
return nil, false, false
}

export := createCabrilloLog(settings, claimedScore, qsos)
export.Category = c.category
export.Name = c.name
export.Email = c.email

return export, c.openAfterExport, true
}

func (c *Controller) Categories() []string {
if c.definition == nil {
return nil
}
result := make([]string, len(c.definition.Categories))
for i, category := range c.definition.Categories {
result[i] = category.Name
}
return result
}

func (c *Controller) SetCategory(name string) {
category, found := c.findCategory(name)
if !found {
log.Printf("no category with name %q found", name)
return
}

c.category.Assisted = convalToCabrilloAssisted(category)
c.category.Band = convalToCabrilloBand(category, c.definition.Bands)
c.category.Mode = convalToCabrilloMode(category, c.definition.Modes)
c.category.Operator = convalToCabrilloOperator(category)
c.category.Power = convalToCabrilloPower(category)
c.updateCategorySettings()
}

func (c *Controller) updateCategorySettings() {
log.Printf("new category settings: %+v", c.category)
c.view.SetCategoryAssisted(c.category.Assisted == cabrillo.Assisted)
c.view.SetCategoryBand(string(c.category.Band))
c.view.SetCategoryMode(string(c.category.Mode))
c.view.SetCategoryOperator(string(c.category.Operator))
c.view.SetCategoryPower(string(c.category.Power))
}

func (c *Controller) findCategory(name string) (conval.Category, bool) {
if c.definition == nil {
return conval.Category{}, false
}
for _, category := range c.definition.Categories {
if category.Name == name {
return category, true
}
}
return conval.Category{}, false
}

func (c *Controller) SetCategoryAssisted(assisted bool) {
if assisted {
c.category.Assisted = cabrillo.Assisted
} else {
c.category.Assisted = cabrillo.NonAssisted
}
}

func (c *Controller) CategoryBands() []string {
if c.definition.Bands == nil {
return []string{
"ALL", "160M", "80M", "40M", "20M", "15M", "10M", "6M", "4M", "2M", "222", "432",
"902", "1.2G", "2.3G", "3.4G", "5.7G", "10G", "24G", "47G", "75G", "122G", "134G",
"241G", "LIGHT", "VHF-3-BAND", "VHF-FM-ONLY",
}
}
result := make([]string, len(c.definition.Bands)+1)
result[0] = "ALL"
for i, band := range c.definition.Bands {
result[i+1] = string(convertBand(band))
}
return result
}

func (c *Controller) SetCategoryBand(band string) {
c.category.Band = cabrillo.CategoryBand(strings.ToUpper(band))
}

func (c *Controller) CategoryModes() []string {
if c.definition.Modes == nil {
return []string{"CW", "PH", "RY", "DG", "FM", "MIXED"}
}
result := make([]string, len(c.definition.Modes))
for i, mode := range c.definition.Modes {
result[i] = string(convertMode(mode))
}
if len(result) > 1 {
result = append(result, "MIXED")
}
return result
}

func (c *Controller) SetCategoryMode(mode string) {
c.category.Mode = cabrillo.CategoryMode(strings.ToUpper(mode))
}

func (c *Controller) CategoryOperators() []string {
return []string{"SINGLE-OP", "MULTI-OP", "CHECKLOG"}
}

func (c *Controller) SetCategoryOperator(operator string) {
c.category.Operator = cabrillo.CategoryOperator(strings.ToUpper(operator))
}

func (c *Controller) CategoryPowers() []string {
return []string{"QRP", "LOW", "HIGH"}
}

func (c *Controller) SetCategoryPower(power string) {
c.category.Power = cabrillo.CategoryPower(strings.ToUpper(power))
}

func (c *Controller) SetName(name string) {
c.name = name
}

func (c *Controller) SetEmail(email string) {
c.email = email
}

func (c *Controller) SetOpenAfterExport(open bool) {
c.openAfterExport = open
}

func createCabrilloLog(settings core.Settings, claimedScore int, qsos []core.QSO) *cabrillo.Log {
export := cabrillo.NewLog()
export.Callsign = settings.Station().Callsign
export.CreatedBy = "Hello Contest"
export.Contest = cabrillo.ContestIdentifier(settings.Contest().Name)
export.Contest = cabrillo.ContestIdentifier(settings.Contest().Definition.Identifier)
export.Operators = []callsign.Callsign{settings.Station().Operator}
export.GridLocator = settings.Station().Locator
export.ClaimedScore = claimedScore
export.Certificate = true

qsoData := make([]cabrillo.QSO, 0, len(qsos))
ignoredQSOs := make([]cabrillo.QSO, 0, len(qsos))
Expand All @@ -36,11 +220,13 @@ func Export(w io.Writer, settings core.Settings, claimedScore int, qsos ...core.
export.QSOData = qsoData
export.IgnoredQSOs = ignoredQSOs

return cabrillo.WriteWithTags(w, export, false, false, cabrillo.CreatedByTag, cabrillo.ContestTag,
cabrillo.CallsignTag, cabrillo.OperatorsTag, cabrillo.GridLocatorTag, cabrillo.ClaimedScoreTag,
cabrillo.Tag("SPECIFIC"), cabrillo.CategoryAssistedTag, cabrillo.CategoryBandTag, cabrillo.CategoryModeTag,
cabrillo.CategoryOperatorTag, cabrillo.CategoryPowerTag, cabrillo.ClubTag, cabrillo.NameTag,
cabrillo.EmailTag)
return export
}

// Export writes the given QSOs to the given writer in the Cabrillo format.
// The header is very limited and needs to be completed manually after the log was written.
func Export(w io.Writer, export *cabrillo.Log) error {
return cabrillo.Write(w, export, false)
}

var qrg = map[core.Band]string{
Expand Down
Loading

0 comments on commit c442bff

Please sign in to comment.