Skip to content

Commit

Permalink
Merge pull request #236 from mbridak/Real_Time_Scoring
Browse files Browse the repository at this point in the history
Real time scoring
  • Loading branch information
mbridak authored Dec 5, 2024
2 parents 7e62fd9 + 74c0b89 commit c1d2ee0
Show file tree
Hide file tree
Showing 9 changed files with 322 additions and 10 deletions.
45 changes: 40 additions & 5 deletions not1mm/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
from not1mm.radio import Radio
from not1mm.voice_keying import Voice
from not1mm.lookupservice import LookupService
from not1mm.rtc_service import RTCService

poll_time = datetime.datetime.now()

Expand All @@ -102,6 +103,11 @@ class MainWindow(QtWidgets.QMainWindow):
"multicast_group": "239.1.1.1",
"multicast_port": 2239,
"interface_ip": "0.0.0.0",
"send_rtc_scores": False,
"rtc_url": "",
"rtc_user": "",
"rtc_pass": "",
"rtc_interval": 2,
"send_n1mm_packets": False,
"n1mm_station_name": "20M CW Tent",
"n1mm_operator": "Bernie",
Expand Down Expand Up @@ -156,7 +162,6 @@ class MainWindow(QtWidgets.QMainWindow):
opon_dialog = None
dbname = fsutils.USER_DATA_PATH, "/ham.db"
radio_state = {}
rig_control = None
worked_list = {}
cw_entry_visible = False
last_focus = None
Expand All @@ -170,6 +175,7 @@ class MainWindow(QtWidgets.QMainWindow):
radio_thread = QThread()
voice_thread = QThread()
fldigi_thread = QThread()
rtc_thread = QThread()

fldigi_watcher = None
rig_control = None
Expand All @@ -179,6 +185,7 @@ class MainWindow(QtWidgets.QMainWindow):
vfo_window = None
lookup_service = None
fldigi_util = None
rtc_service = None

current_widget = None

Expand Down Expand Up @@ -712,7 +719,7 @@ def __init__(self, splash):
)

def load_call_history(self) -> None:
""""""
"""Display filepicker and load chosen call history file."""
filename = self.filepicker("other")
if filename:
self.database.create_callhistory_table()
Expand Down Expand Up @@ -755,13 +762,13 @@ def load_call_history(self) -> None:
self.show_message_box(f"{err}")

def on_focus_changed(self, new):
""""""
"""Called when text entry focus has changed."""
if self.use_esm:
if hasattr(self.contest, "process_esm"):
self.contest.process_esm(self, new_focused_widget=new)

def make_button_green(self, the_button: QtWidgets.QPushButton) -> None:
"""Turn the_button green."""
"""Takes supplied QPushButton object and turns it green."""
if the_button is not None:
pal = QPalette()
pal.isCopyOf(self.current_palette)
Expand Down Expand Up @@ -2457,7 +2464,10 @@ def save_contact(self) -> None:
self.worked_list = self.database.get_calls_and_bands()
self.send_worked_list()
self.clearinputs()

if self.pref.get("send_rtc_scores", False):
if hasattr(self.contest, "online_score_xml"):
if self.rtc_service is not None:
self.rtc_service.xml = self.contest.online_score_xml(self)
cmd = {}
cmd["cmd"] = "UPDATELOG"
if self.log_window:
Expand Down Expand Up @@ -2898,6 +2908,25 @@ def readpreferences(self) -> None:
self.setDarkMode(False)
self.actionDark_Mode_2.setChecked(False)

try:
if self.rtc_thread.isRunning():
self.rtc_service.time_to_quit = True
self.rtc_thread.quit()
self.rtc_thread.wait(1000)

except (RuntimeError, AttributeError):
...

self.rtc_service = None

if self.pref.get("send_rtc_scores", False):
self.rtc_service = RTCService()
self.rtc_service.moveToThread(self.rtc_thread)
self.rtc_thread.started.connect(self.rtc_service.run)
self.rtc_thread.finished.connect(self.rtc_service.deleteLater)
# self.rtc_service.poll_callback.connect(self.rtc_result)
self.rtc_thread.start()

try:
if self.radio_thread.isRunning():
self.rig_control.time_to_quit = True
Expand Down Expand Up @@ -3046,6 +3075,12 @@ def readpreferences(self) -> None:
self.esm_dict["MYCALL"] = fkey_dict.get(self.pref.get("esm_mycall", "DISABLED"))
self.esm_dict["QSOB4"] = fkey_dict.get(self.pref.get("esm_qsob4", "DISABLED"))

self.send_rtc_scores = self.pref.get("send_rtc_scores", False)
self.rtc_url = self.pref.get("rtc_url", "")
self.rtc_user = self.pref.get("rtc_user", "")
self.rtc_pass = self.pref.get("rtc_pass", "")
self.rtc_interval = self.pref.get("rtc_interval", 2)

def dark_mode_state_changed(self) -> None:
"""Called when the Dark Mode menu state is changed."""
self.pref["darkmode"] = self.actionDark_Mode_2.isChecked()
Expand Down
70 changes: 70 additions & 0 deletions not1mm/data/configuration.ui
Original file line number Diff line number Diff line change
Expand Up @@ -2173,6 +2173,76 @@
</property>
</widget>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="send_rtc_scores">
<property name="text">
<string>Use RTC score reporting</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="rtc_url">
<item>
<property name="text">
<string>https://hamscore.com/postxml/</string>
</property>
</item>
<item>
<property name="text">
<string>https://contestonlinescore.com/post/</string>
</property>
</item>
<item>
<property name="text">
<string>http://contest.run</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QLineEdit" name="rtc_user">
<property name="placeholderText">
<string>username</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="rtc_pass">
<property name="echoMode">
<enum>QLineEdit::EchoMode::Password</enum>
</property>
<property name="placeholderText">
<string>password</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_28">
<property name="text">
<string>Score posting interval (minutes)</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="rtc_interval">
<property name="text">
<string>2</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_8">
<property name="orientation">
Expand Down
52 changes: 52 additions & 0 deletions not1mm/lib/plugin_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,58 @@
from decimal import Decimal
from pathlib import Path
from not1mm.lib.ham_utility import get_adif_band
from not1mm.lib.version import __version__


def online_score_xml(self):
"""generate online xml"""

mults = self.contest.get_mults(self)
the_mults = ""
for thing in mults:
the_mults += (
f'<mult band="total" mode="ALL" type="{thing}">{mults.get(thing,0)}</mult>'
)

the_points = self.contest.just_points(self)

the_date_time = datetime.datetime.now(datetime.timezone.utc).isoformat(" ")[:19]
assisted = self.contest_settings.get("AssistedCategory", "")
bands = self.contest_settings.get("BandCategory", "")
modes = self.contest_settings.get("ModeCategory", "")
xmiter = self.contest_settings.get("TransmitterCategory", "")
ops = self.contest_settings.get("OperatorCategory", "")
overlay = self.contest_settings.get("OverlayCategory", "")
power = self.contest_settings.get("PowerCategory", "")

the_xml = (
'<?xml version="1.0"?>'
"<dynamicresults>"
f"<contest>{self.contest.cabrillo_name}</contest>"
f'<call>{self.station.get("Call", "")}</call>'
# <ops>NR9Q</ops>
f'<class power="{power}" assisted = "{assisted}" transmitter="{xmiter}" ops="{ops}" bands="{bands}" mode="{modes}" overlay="{overlay}"></class>'
f"<club>{self.station.get('Club', '').upper()}</club>"
"<soft>Not1MM</soft>"
f"<version>{__version__}</version>"
"<qth>"
# <dxcccountry>K</dxcccountry>
f"<cqzone>{self.station.get('CQZone','')}</cqzone>"
f"<iaruzone>{self.station.get('IARUZone','')}</iaruzone>"
f"<arrlsection>{self.station.get('ARRLSection', '')}</arrlsection>"
f"<stprvoth>{self.station.get('State','')}</stprvoth>"
f"<grid6>{self.station.get('GridSquare','')}</grid6>"
"</qth>"
"<breakdown>"
f'<qso band="total" mode="ALL">{self.contest.show_qso(self)}</qso>'
f"{the_mults}"
f'<point band="total" mode="ALL">{the_points}</point>'
"</breakdown>"
f"<score>{self.contest.calc_score(self)}</score>"
f"<timestamp>{the_date_time}</timestamp>"
"</dynamicresults>"
)
return the_xml


def get_points(self):
Expand Down
22 changes: 22 additions & 0 deletions not1mm/lib/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,19 @@ def __init__(self, app_data_path, pref, parent=None):
def setup(self):
"""setup dialog"""

self.send_rtc_scores.setChecked(
bool(self.preference.get("send_rtc_scores", False))
)

value = self.preference.get("rtc_url", "")
index = self.rtc_url.findText(value)
if index != -1:
self.rtc_url.setCurrentIndex(index)

self.rtc_user.setText(str(self.preference.get("rtc_user", "")))
self.rtc_pass.setText(str(self.preference.get("rtc_pass", "")))
self.rtc_interval.setText(str(self.preference.get("rtc_interval", "2")))

self.use_call_history.setChecked(
bool(self.preference.get("use_call_history", False))
)
Expand Down Expand Up @@ -195,6 +208,15 @@ def save_changes(self):
"""
Write preferences to json file.
"""
self.preference["send_rtc_scores"] = self.send_rtc_scores.isChecked()
self.preference["rtc_url"] = self.rtc_url.currentText()
self.preference["rtc_user"] = self.rtc_user.text()
self.preference["rtc_pass"] = self.rtc_pass.text()
try:
self.preference["rtc_interval"] = int(self.rtc_interval.text())
except ValueError:
self.preference["rtc_interval"] = 2

self.preference["use_call_history"] = self.use_call_history.isChecked()
self.preference["use_esm"] = self.use_esm.isChecked()
self.preference["esm_cq"] = self.esm_cq.currentText()
Expand Down
4 changes: 2 additions & 2 deletions not1mm/lookupservice.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
not1mm Contest logger
Email: [email protected]
GPL V3
Class: BandMapWindow
Purpose: Onscreen widget to show realtime spots from an AR cluster.
Class: LookupService
Purpose: Lookup callsigns with online services.
"""

# pylint: disable=unused-import, c-extension-no-member, no-member, invalid-name, too-many-lines
Expand Down
15 changes: 14 additions & 1 deletion not1mm/plugins/cq_ww_cw.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@

from PyQt6 import QtWidgets

from not1mm.lib.plugin_common import gen_adif, get_points
from not1mm.lib.plugin_common import gen_adif, get_points, online_score_xml
from not1mm.lib.version import __version__
from not1mm.lib.ham_utility import get_logged_band

Expand Down Expand Up @@ -177,6 +177,19 @@ def points(self):
return 0


def get_mults(self):
""""""
mults = {}
mults["zone"] = self.database.fetch_zn_band_count().get("zb_count", 0)
mults["country"] = self.database.fetch_country_band_count().get("cb_count", 0)
return mults


def just_points(self):
""""""
return self.database.fetch_points().get("Points", "0")


def show_mults(self):
"""Return display string for mults"""
result1 = self.database.fetch_zn_band_count()
Expand Down
22 changes: 21 additions & 1 deletion not1mm/plugins/cwt.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@

from PyQt6 import QtWidgets

from not1mm.lib.plugin_common import gen_adif, get_points
from not1mm.lib.plugin_common import gen_adif, get_points, online_score_xml
from not1mm.lib.version import __version__

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -519,3 +519,23 @@ def check_call_history(self):
self.other_1.setText(f"{result.get('Name', '')}")
if self.other_2.text() == "":
self.other_2.setText(f"{result.get('Exch1', '')}")


# --------RTC Stuff-----------
def get_mults(self):
""""""

mults = {}
mults["state"] = show_mults(self)
return mults


def just_points(self):
""""""
result = self.database.fetch_points()
if result is not None:
score = result.get("Points", "0")
if score is None:
score = "0"
return int(score)
return 0
22 changes: 21 additions & 1 deletion not1mm/plugins/icwc_mst.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@

from PyQt6 import QtWidgets

from not1mm.lib.plugin_common import gen_adif, get_points
from not1mm.lib.plugin_common import gen_adif, get_points, online_score_xml
from not1mm.lib.version import __version__

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -493,3 +493,23 @@ def check_call_history(self):
self.history_info.setText(f"{result.get('UserText','')}")
if self.other_2.text() == "":
self.other_2.setText(f"{result.get('Name', '')}")


# --------RTC Stuff-----------
def get_mults(self):
""""""

mults = {}
mults["state"] = show_mults(self)
return mults


def just_points(self):
""""""
result = self.database.fetch_points()
if result is not None:
score = result.get("Points", "0")
if score is None:
score = "0"
return int(score)
return 0
Loading

0 comments on commit c1d2ee0

Please sign in to comment.