Source code for camcops_server.cc_modules.cc_trackerhelpers

"""
camcops_server/cc_modules/cc_trackerhelpers.py

===============================================================================

    Copyright (C) 2012, University of Cambridge, Department of Psychiatry.
    Created by Rudolf Cardinal (rnc1001@cam.ac.uk).

    This file is part of CamCOPS.

    CamCOPS is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    CamCOPS is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with CamCOPS. If not, see <https://www.gnu.org/licenses/>.

===============================================================================

**Helper representations for trackers.**

"""

from enum import Enum
from typing import List, Optional

import numpy as np


DEFAULT_TRACKER_ASPECT_RATIO = 2.0  # width / height


[docs]class LabelAlignment(Enum): """ Enum representing figure label alignment. """ center = "center" top = "top" bottom = "bottom" baseline = "baseline"
[docs]class TrackerLabel(object): """ Representation of a label on a :class:`camcops_server.cc_modules.cc_tracker.Tracker` figure. """
[docs] def __init__( self, y: float, label: str, vertical_alignment: LabelAlignment = LabelAlignment.center, ): """ Args: y: Y axis (vertical) position label: text for label vertical_alignment: :class:`LabelAlignment` enum """ self.y = y self.label = label self.vertical_alignment = vertical_alignment
def __str__(self) -> str: return f"{self.y}: {self.label}"
[docs]class TrackerAxisTick(object): """ Representation of a Y-axis tick mark and associated label on a :class:`camcops_server.cc_modules.cc_tracker.Tracker` figure. """
[docs] def __init__(self, y: float, label: str): self.y = y self.label = label
[docs]class TrackerInfo(object): """ Tasks return one or more of these (one for each tracker to be shown), from which :class:`camcops_server.cc_modules.cc_tracker.Tracker` displays are created. """
[docs] def __init__( self, value: float, plot_label: str = None, axis_label: str = None, axis_min: float = None, axis_max: float = None, axis_ticks: Optional[List[TrackerAxisTick]] = None, horizontal_lines: Optional[List[float]] = None, horizontal_labels: Optional[List[TrackerLabel]] = None, aspect_ratio: Optional[float] = DEFAULT_TRACKER_ASPECT_RATIO, ): """ Args: value: numerical value plot_label: label for the whole plot axis_label: label for the Y axis axis_min: minimum value for the Y axis axis_max: maximum value for the Y axis axis_ticks: optional list of :class:`TrackerAxisTick` objects describing where to put tick marks/labels on the Y axis horizontal_lines: optional list of y values at which to draw horizontal (dotted) lines horizontal_labels: optional list of :class:`TrackerLabel` objects indicating which additional labels to place on the main plot (such as: to describe the meaning of the horizontal lines) aspect_ratio: optional aspect ratio (width / height) """ self.value = value self.plot_label = plot_label self.axis_label = axis_label self.axis_min = axis_min self.axis_max = axis_max self.axis_ticks = axis_ticks or [] self.horizontal_lines = horizontal_lines or [] self.horizontal_labels = horizontal_labels or [] self.aspect_ratio = aspect_ratio
[docs]def equally_spaced_ndarray( start: float, stop: float, num: int, endpoint: bool = True ) -> np.ndarray: """ Produces equally spaced numbers. See https://stackoverflow.com/questions/477486/how-to-use-a-decimal-range-step-value. Args: start: starting value stop: stopping value num: number of values to return endpoint: include the endpoint? Returns: list of floats """ return np.linspace(start, stop, num, endpoint=endpoint, dtype=float)
[docs]def equally_spaced_float( start: float, stop: float, num: int, endpoint: bool = True ) -> List[float]: """ Returns a float equivalent of :func:`equally_spaced_float` (q.v.). """ return list(equally_spaced_ndarray(start, stop, num, endpoint=endpoint))
[docs]def equally_spaced_int( start: int, stop: int, step: int, endpoint: bool = True ) -> List[int]: """ Almost a synonym for :func:`range`! Args: start: starting value stop: stopping value (INCLUSIVE if endpoint is True) step: step size endpoint: bool Returns: list of integers """ if endpoint: if start <= stop: # normal range_stop = stop + 1 else: # counting backwards: start > stop range_stop = stop - 1 else: range_stop = stop return list(range(start, range_stop, step))
[docs]def regular_tracker_axis_ticks_float( start: float, stop: float, num: int, endpoint: bool = True ) -> List[TrackerAxisTick]: """ Args: start: starting value stop: stopping value num: number of values to return endpoint: include the endpoint? Returns: a list of simple numerical TrackerAxisTick objects """ ticks = [] # type: List[TrackerAxisTick] for val in equally_spaced_ndarray(start, stop, num, endpoint=endpoint): ticks.append(TrackerAxisTick(val, str(val))) return ticks
[docs]def regular_tracker_axis_ticks_int( start: int, stop: int, step: int, endpoint: bool = True ) -> List[TrackerAxisTick]: """ Args: start: starting value stop: stopping value step: step size endpoint: include the endpoint? Returns: a list of simple numerical TrackerAxisTick objects """ ticks = [] # type: List[TrackerAxisTick] for val in equally_spaced_int(start, stop, step, endpoint=endpoint): ticks.append(TrackerAxisTick(val, str(val))) return ticks