kedder/openvario-shell

View on GitHub
src/ovshell_xcsoar/ext.py

Summary

Maintainability
A
25 mins
Test Coverage
import os
import subprocess
from typing import Sequence

import urwid

from ovshell import api


class XCSoarExtension(api.Extension):
    title = "XCSoar"

    def __init__(self, id: str, shell: api.OpenVarioShell):
        self.id = id
        self.shell = shell

    def list_apps(self) -> Sequence[api.App]:
        return [XCSoarApp(self.shell)]


class XCSoarApp(api.App):
    name = "xcsoar"
    title = "XCSoar"
    description = "Tactical glide computer"
    priority = 90

    def __init__(self, shell: api.OpenVarioShell) -> None:
        self.shell = shell

    def install(self, appinfo: api.AppInfo) -> None:
        self.shell.apps.pin(appinfo)

    def launch(self) -> None:
        env = self._prep_environment()
        cmdline = self._make_commandline()
        modal_opts = api.ModalOptions(
            align="center",
            width=("relative", 90),
            valign="middle",
            height="pack",
        )
        try:
            message = urwid.Text("Running XCSoar...")
            self.shell.screen.push_dialog("XCSoar", message).no_buttons()
            self.shell.screen.draw()
            try:
                completed = subprocess.run(cmdline, capture_output=True, env=env)
            finally:
                message.set_text("Finishing XCSoar...")
                self.shell.screen.draw()
                self.shell.os.sync()
                self.shell.screen.pop_activity()
        except FileNotFoundError as e:
            self.shell.screen.push_modal(
                AppOutputActivity(self.shell, str(e)), modal_opts
            )
            return

        if completed.returncode != 0:
            self.shell.screen.push_modal(
                AppOutputActivity(self.shell, completed.stderr.decode("utf-8")),
                modal_opts,
            )

    def _prep_environment(self) -> dict[str, str]:
        env = os.environ.copy()
        lang = self.shell.settings.get("core.language", str)
        if lang is None:
            return env

        env["LANG"] = lang
        return env

    def _make_commandline(self) -> Sequence[str]:
        binary = os.environ.get("XCSOAR_BIN", "/usr/bin/xcsoar")
        return [binary, "-fly"]


class AppOutputActivity(api.Activity):
    def __init__(self, shell: api.OpenVarioShell, message: str) -> None:
        self.shell = shell
        self.message = message

    def create(self) -> urwid.Widget:
        term = urwid.Text(self.message)
        view = urwid.LineBox(term, "Error")
        return view


class XCSoarProfile:
    def __init__(self, filename: str) -> None:
        self.os = os
        self.filename = filename
        self._dirty = False

        with open(filename) as f:
            self.lines = f.readlines()

    def save(self) -> None:
        if not self._dirty:
            return
        content = "".join(self.lines)
        with open(self.filename, "w") as f:
            f.write(content)

    def set_orientation(self, orientation: str) -> None:
        self._set_option("DisplayOrientation", orientation)

    def _set_option(self, key: str, value: str) -> None:
        modified_line = f'{key}="{value}"\n'
        for n, line in enumerate(self.lines):
            if "=" not in line:
                continue
            k, v = line.split("=", maxsplit=1)
            v = v.strip('\n"')
            if k == key:
                self.lines[n] = modified_line
                self._dirty = v != value
                break
        else:
            self.lines.append(modified_line)
            self._dirty = True