Skip to content

Contest Exchange Enhancement - Complete Phases 4-6 #48

@ny4i

Description

@ny4i

Background

This is a continuation of the 6-phase Exchange Enhancement plan (wild-scribbling-shore.md). Phases 1-3 have been completed in v3.31.0+:

✅ Completed (Phases 1-3)

  • Phase 1: Validation and parsing connected to logging flow (v3.31.0)
    • Exchange validation called before database save
    • parsedExchange field populated
    • RST auto-detection and auto-prepending
  • Phase 2: Auto-population infrastructure (v3.31.0+)
    • Basic exchange auto-population from contest rules
    • Context-aware field enabling in Edit QSO Dialog
  • Phase 3: Database schema for exchange fields (v3.31.1+)
    • Exchange field columns added to database schema
    • Exchange received formatting across all contests

⚠️ Remaining Work (Phases 4-6)

The following phases need implementation to achieve TR4W-level exchange sophistication:


Phase 4: Smart Navigation & Field Management

Goal: TR4W-style keyboard workflow - minimal mouse, auto-focus, smart spacing

Features to Implement

1. Auto-Focus Logic

  • Auto-focus to exchange field after 3+ chars in callsign (configurable threshold)
  • Located in MainWindow::onCallsignChanged() (~line 1377)
void MainWindow::onCallsignChanged(const QString& callsign) {
    autoPopulateExchange(callsign);

    // NEW: Auto-focus to exchange after threshold
    AppSettings& settings = AppSettings::instance();
    int threshold = settings.getAutoFocusThreshold();  // Default: 3

    if (callsign.length() >= threshold && m_exchangeEntry->text().isEmpty()) {
        m_exchangeEntry->setFocus();
    }
}

2. Exchange Overwrite Mode

  • When exchange is auto-populated, select all text so first keystroke replaces
  • Reduces keystrokes when auto-fill is wrong
// In autoPopulateExchange() after setting text
if (!prediction.isEmpty()) {
    m_exchangeEntry->setText(prediction);
    m_exchangeEntry->selectAll();  // Next keystroke replaces entire field
}

3. Smart Space Insertion (Optional)

  • Auto-insert space between fields (e.g., "5991" → "599 1" for CQWW)
  • New class: SmartExchangeParser::autoSpace()

4. Tab Completion for Sections

  • QCompleter for section autocomplete (e.g., "WM" + Tab → "WMA")

5. Settings Integration

  • Add to AppSettings.h/cpp:
    • setAutoFocusThreshold(int chars) - Default: 3
    • setAutoSpaceEnabled(bool) - Default: false
    • setExchangeOverwriteMode(bool) - Default: true

Testing Checklist

  • Type "W1A" in callsign → cursor moves to exchange
  • Exchange auto-fills "1O MA" → entire text selected
  • Type "5" → replaces with "5", not "5O MA"
  • Auto-focus disabled in settings → cursor stays in callsign
  • Type "WM" → Tab → completes to "WMA"

Deliverable: Hands stay on keyboard; <20 keystrokes per QSO (down from ~30)


Phase 5: Exchange Memory Database & Persistence

Goal: Exchange memory persists across sessions and improves over time

Features to Implement

1. Database Persistence Verification

  • Ensure exchange_memory table survives app restarts
  • Test schema loading in Database::loadSchemaSql()
  • Add migration for existing databases

2. Memory Manager UI (New Dialog)

New files:

  • /src/ui/dialogs/ExchangeMemoryDialog.h
  • /src/ui/dialogs/ExchangeMemoryDialog.cpp
class ExchangeMemoryDialog : public QDialog {
    Q_OBJECT
public:
    explicit ExchangeMemoryDialog(QWidget* parent = nullptr);

private:
    QTableView* m_memoryTable;
    ExchangeMemoryTableModel* m_model;
    QPushButton* m_deleteButton;
    QPushButton* m_importButton;
    QPushButton* m_exportButton;
    QPushButton* m_clearOldButton;
    QLabel* m_statsLabel;  // Shows: "782 entries, 63% hit rate"
};

Menu Integration: Tools → Exchange Memory Manager

3. Export to CSV

For backup and sharing:

callsign,exchange,contest_type,mode,timestamp,hit_count
W1AW,1O MA,WFD,CW,1703462400,5
K6XX,599 3,CQWW,CW,1703548800,2

4. Statistics & Metrics

Track memory performance:

  • Hit rate: successful lookups / total lookups
  • Coverage: unique callsigns in memory
  • Freshness: average age of entries
  • Top sources: most frequently looked up

Display in ExchangeMemoryDialog status bar

5. Import from TR4W TRMASTER.DTA (Stretch Goal)

  • Convert TR4W's binary master database to TR4QT exchange_memory
  • TR4W format: Binary file with callsign records (Name, QTH, Section, Zone, Check)

Testing Checklist

  • Log 50 QSOs → restart app → memory table has 50 entries
  • Type callsign from previous session → exchange auto-fills
  • Memory manager shows statistics (hit rate, entry count)
  • Export memory to CSV → reimport → entries restored
  • Delete old entries (>1 year) → memory cleaned up
  • Import TR4W TRMASTER.DTA → entries converted (stretch goal)

Deliverable: Exchange memory persists and improves; >80% hit rate after 500 QSOs


Phase 6: Advanced Features & DOM Files

Goal: Contest-specific resources, smart parsing, TR4W-level sophistication

Features to Implement

1. JSON Resource Files for Contest Data

Replace hardcoded sections/zones with JSON resources:

New files:

  • /resources/contests/WFD_sections.json
  • /resources/contests/ARRL_sections.json
  • /resources/contests/RAC_sections.json
  • /resources/contests/CQ_zones.json

Example (WFD_sections.json):

{
  "contest": "WFD",
  "version": "2024",
  "sections": {
    "US": [
      {"code": "CT", "name": "Connecticut", "aliases": ["CONN"]},
      {"code": "WMA", "name": "Western Massachusetts"},
      {"code": "EMA", "name": "Eastern Massachusetts"}
    ],
    "Canada": [
      {"code": "MAR", "name": "Maritime"},
      {"code": "QC", "name": "Quebec"}
    ],
    "Other": [
      {"code": "DX", "name": "DX (non-US/VE)"}
    ]
  },
  "classes": ["1O", "1I", "2O", "2I", "3O", "HOME"]
}

2. Contest Resource Loader

New files:

  • /src/contests/ContestResourceLoader.h
  • /src/contests/ContestResourceLoader.cpp
class ContestResourceLoader {
public:
    static QStringList loadValidSections(const QString& contestId);
    static QStringList loadValidClasses(const QString& contestId);
    static QMap<QString, QString> loadSectionAliases(const QString& contestId);

private:
    static QJsonDocument loadResource(const QString& contestId, const QString& resourceType);
    static QCache<QString, QJsonDocument> s_cache;
};

3. Update Contests to Use Resources

Modify: /src/contests/WinterFieldDayContest.cpp (lines 226-251)

Replace hardcoded sections with:

bool WinterFieldDayContest::isValidSection(const QString& section) const {
    static QStringList validSections = ContestResourceLoader::loadValidSections("WFD");
    return validSections.contains(section.toUpper());
}

4. Smart Multi-Field Parser

New files:

  • /src/exchanges/SmartExchangeParser.h
  • /src/exchanges/SmartExchangeParser.cpp

TR4W's ParseArray() equivalent - detect field types and reorder:

class SmartExchangeParser {
public:
    enum class FieldType { Numeric, Alpha, Mixed, RST };

    struct ParsedField {
        FieldType type;
        QString value;
        int position;
    };

    // Analyze field types
    static QList<ParsedField> analyzeFields(const QString& exchange);

    // Reorder to match expected format
    // E.g., "WTX 2A" → "2A WTX" for WFD
    static QString normalizeOrder(const QString& exchange,
                                 const QList<ExchangeField>& expectedFields);
};

5. Context-Sensitive Help (F1 Key)

Add F1 key handler to exchange field:

bool MainWindow::eventFilter(QObject* obj, QEvent* event) {
    if (obj == m_exchangeEntry && event->type() == QEvent::KeyPress) {
        QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
        if (keyEvent->key() == Qt::Key_F1) {
            showExchangeHelp();
            return true;
        }
    }
    return QMainWindow::eventFilter(obj, event);
}

void MainWindow::showExchangeHelp() {
    if (!m_activeContest) return;

    QString help = "Exchange format: ";
    QList<ExchangeField> fields = m_activeContest->getReceivedExchangeFields();

    for (const ExchangeField& field : fields) {
        help += field.hint + " ";
    }

    // Show valid values for current contest
    if (m_activeContest->getContestId() == "WFD") {
        QStringList sections = ContestResourceLoader::loadValidSections("WFD");
        help += "\n\nValid sections: " + sections.join(", ");
    }

    DialogHelper::information(this, "Exchange Help", help);
}

6. Exchange Statistics

Add Tools menu item: "Exchange Statistics"

Show:

  • Zone/section distribution (bar chart using QCustomPlot)
  • Most common exchanges
  • Unusual exchanges (outliers)
  • Missing multipliers

Testing Checklist

  • WFD validates against section list from JSON
  • Type "WTX 2A" → smart parser reorders to "2A WTX"
  • Press F1 on exchange → shows valid sections/classes
  • Invalid section "ZZZ" → rejected
  • Section alias "CONN" → accepted as "CT"
  • Statistics window shows zone distribution chart
  • Contest resources embedded in binary (Qt resources)

Deliverable: TR4W-equivalent sophistication; forgiving parser; comprehensive validation


Files to Create

Phase 4

  • None (modifications only)

Phase 5

  1. /src/ui/dialogs/ExchangeMemoryDialog.h
  2. /src/ui/dialogs/ExchangeMemoryDialog.cpp

Phase 6

  1. /src/contests/ContestResourceLoader.h
  2. /src/contests/ContestResourceLoader.cpp
  3. /src/exchanges/SmartExchangeParser.h
  4. /src/exchanges/SmartExchangeParser.cpp
  5. /resources/contests/WFD_sections.json
  6. /resources/contests/ARRL_sections.json
  7. /resources/contests/RAC_sections.json
  8. /resources/contests/CQ_zones.json

Files to Modify

Phase 4

  1. /src/ui/MainWindow.cpp
    • Line 1377: Add auto-focus logic in onCallsignChanged()
    • Lines 1741-1771: Add overwrite mode to autoPopulateExchange()
  2. /src/ui/MainWindow.h
    • Add event filter declaration
  3. /src/utils/AppSettings.h
    • Add: getAutoFocusThreshold(), getAutoSpaceEnabled(), getExchangeOverwriteMode()
  4. /src/utils/AppSettings.cpp
    • Implement settings persistence

Phase 5

  1. /src/CMakeLists.txt
    • Add ExchangeMemoryDialog to build
  2. /src/ui/MainWindow.cpp
    • Add Tools menu item for Exchange Memory Manager

Phase 6

  1. /src/contests/WinterFieldDayContest.cpp
    • Lines 226-251: Replace hardcoded sections with JSON loader
  2. /src/CMakeLists.txt
    • Add Qt resource file for JSON resources
    • Add SmartExchangeParser and ContestResourceLoader to build
  3. /src/ui/MainWindow.cpp
    • Add F1 event filter for exchange help
    • Add Tools menu item for Exchange Statistics

Success Metrics

Phase 4

  • Goal: Reduced keystrokes per QSO
  • Metric: <20 keystrokes per QSO (down from ~30)
  • Measurement: Log 50 QSOs and count keystrokes

Phase 5

  • Goal: Persistent exchange knowledge
  • Metric: Memory DB size grows linearly with unique calls
  • Measurement: >80% hit rate after 500 QSOs

Phase 6

  • Goal: TR4W-level sophistication
  • Metric: Log 100 WFD QSOs with <5% manual corrections
  • Measurement: Parser accepts "WTX 2A" and reorders automatically

References

Original Plan: ~/.claude/plans/wild-scribbling-shore.md

Completed Work:

  • v3.31.0: Complete exchange received formatting across all contests
  • v3.31.1: Fix database schema bug - Add missing exchange field columns
  • v3.31.3: Context-aware field enabling in Edit QSO Dialog
  • v3.31.5: Add Operator Name field to Edit QSO Dialog
  • v3.31.6: Document exchange field database schema architecture

TR4W Reference (for feature parity):

  • /Users/toms/projects/TR4W/tr4w/src/trdos/LOGEDIT.PAS (line 2207 - InitialExchangeEntry)
  • /Users/toms/projects/TR4W/tr4w/src/trdos/LOGSTUFF.PAS (line 8516 - ProcessExchange)
  • /Users/toms/projects/TR4W/tr4w/src/MainUnit.pas (exchange UI handling)

TR4QT Codebase:

  • /src/ui/MainWindow.cpp (lines 1258-1372, 1710-1771)
  • /src/contests/ContestBase.h (exchange API)
  • /src/contests/WinterFieldDayContest.cpp (complex validation example)

Implementation Strategy

Recommended order:

  1. Phase 4 (1-2 weeks) - Immediate UX improvement, low risk
  2. Phase 6 (2-3 weeks) - Foundation for Phase 5 (resource loader needed)
  3. Phase 5 (1-2 weeks) - UI polish, depends on Phase 6 resources

Total: 4-7 weeks for complete TR4W-equivalent exchange system

Milestones:

  • Phase 4 complete: Auto-focus and overwrite mode working
  • Phase 6 complete: JSON resources loaded, smart parser functional
  • Phase 5 complete: Exchange memory UI and persistence verified

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions