More cleanup.
authorScott Gasch <[email protected]>
Thu, 10 Feb 2022 22:52:14 +0000 (14:52 -0800)
committerScott Gasch <[email protected]>
Thu, 10 Feb 2022 22:52:14 +0000 (14:52 -0800)
ml/quick_label.py
type/centcount.py
type/locations.py
type/money.py
type/people.py
type/rate.py

index 7e0a6bf64921533e00d719223d2657fb21ebbccf..da9a1d2e00d15e32cce697e3782a5cfce87ea584 100644 (file)
@@ -1,10 +1,13 @@
 #!/usr/bin/env python3
 
+"""A helper to facilitate quick manual labeling of ML training data."""
+
 import glob
 import logging
 import os
 import warnings
-from typing import Callable, List, NamedTuple, Optional, Set
+from dataclasses import dataclass
+from typing import Callable, List, Optional, Set
 
 import argparse_utils
 import config
@@ -35,14 +38,18 @@ parser.add_argument(
 )
 
 
-class InputSpec(NamedTuple):
-    image_file_glob: Optional[str]
-    image_file_prepopulated_list: Optional[List[str]]
-    image_file_to_features_file: Callable[[str], str]
-    label: str
-    valid_keystrokes: List[str]
-    prompt: str
-    keystroke_to_label: Callable[[str], str]
+@dataclass
+class InputSpec:
+    """A wrapper around the input data we need to operate; should be
+    populated by the caller."""
+
+    image_file_glob: Optional[str] = None
+    image_file_prepopulated_list: Optional[List[str]] = None
+    image_file_to_features_file: Optional[Callable[[str], str]] = None
+    label: str = ''
+    valid_keystrokes: List[str] = []
+    prompt: str = ''
+    keystroke_to_label: Optional[Callable[[str], str]] = None
 
 
 def read_skip_list() -> Set[str]:
@@ -56,7 +63,7 @@ def read_skip_list() -> Set[str]:
                 line = line[:-1]
                 line.strip()
                 ret.add(line)
-        logger.debug(f'Read {quick_skip_file} and found {len(ret)} entries.')
+        logger.debug('Read %s and found %d entries.', quick_skip_file, len(ret))
     return ret
 
 
@@ -68,7 +75,7 @@ def write_skip_list(skip_list) -> None:
                 filename = filename.strip()
                 if len(filename) > 0:
                     f.write(f'{filename}\n')
-        logger.debug(f'Updated {quick_skip_file}')
+        logger.debug('Updated %s', quick_skip_file)
 
 
 def label(in_spec: InputSpec) -> None:
@@ -85,8 +92,9 @@ def label(in_spec: InputSpec) -> None:
     skip_list = read_skip_list()
     for image in images:
         if image in skip_list:
-            logger.debug(f'Skipping {image} because of the skip list')
+            logger.debug('Skipping %s because of the skip list', image)
             continue
+        assert in_spec.image_file_to_features_file
         features = in_spec.image_file_to_features_file(image)
         if features is None or not os.path.exists(features):
             msg = f'File {image} yielded file {features} which does not exist, SKIPPING.'
@@ -114,9 +122,10 @@ def label(in_spec: InputSpec) -> None:
                 prompt=in_spec.prompt,
             )
             os.system('killall xv')
+            assert in_spec.keystroke_to_label
             label_value = in_spec.keystroke_to_label(keystroke)
             filtered_lines.append(f"{in_spec.label}: {label_value}\n")
             with open(features, 'w') as f:
-                f.writelines("%s\n" % line for line in filtered_lines)
+                f.writelines(line + '\n' for line in filtered_lines)
             skip_list.add(image)
     write_skip_list(skip_list)
index 2cb99e029ede3a72f3ccb83e79fbfaf62b6cbb15..4529695cd92174fe7e6aaff60df63dcad999d296 100644 (file)
@@ -1,7 +1,10 @@
 #!/usr/bin/env python3
 
+"""An amount of money (USD) represented as an integral count of
+cents."""
+
 import re
-from typing import Optional, Tuple, TypeVar
+from typing import Optional, Tuple
 
 import math_utils
 
@@ -36,9 +39,9 @@ class CentCount(object):
         a = round(a, 2)
         s = f'{a:,.2f}'
         if self.currency is not None:
-            return '%s %s' % (s, self.currency)
+            return f'{s} {self.currency}'
         else:
-            return '$%s' % s
+            return f'${s}'
 
     def __pos__(self):
         return CentCount(centcount=self.centcount, currency=self.currency)
@@ -181,8 +184,8 @@ class CentCount(object):
     def __ge__(self, other):
         return self > other or self == other
 
-    def __hash__(self):
-        return self.__repr__
+    def __hash__(self) -> int:
+        return hash(self.__repr__)
 
     CENTCOUNT_RE = re.compile(r"^([+|-]?)(\d+)(\.\d+)$")
     CURRENCY_RE = re.compile(r"^[A-Z][A-Z][A-Z]$")
index 24ab063a722d5b159be380ffb459cabcd5d35d23..718a5f1edd5d277e71ff3204a7d273617d581741 100644 (file)
@@ -1,10 +1,14 @@
 #!/usr/bin/env python3
 
+"""An enum to represent locations."""
+
 import enum
 
 
 @enum.unique
-class Location(enum.Enum):
+class Location(enum.IntEnum):
+    """An enum to represent locations."""
+
     UNKNOWN = 0
     HOUSE = 1
     CABIN = 2
index 39557aacdf79dc31ca6e48d27f367305325e80b8..b84652b75b1f594c98566ff54fe0105a5b1fa810 100644 (file)
@@ -1,8 +1,10 @@
 #!/usr/bin/env python3
 
+"""A class to represent money.  See also centcount.py"""
+
 import re
 from decimal import Decimal
-from typing import Optional, Tuple, TypeVar
+from typing import Optional, Tuple
 
 import math_utils
 
@@ -39,9 +41,9 @@ class Money(object):
         a = round(a, 2)
         s = f'{a:,.2f}'
         if self.currency is not None:
-            return '%s %s' % (s, self.currency)
+            return f'{s} {self.currency}'
         else:
-            return '$%s' % s
+            return f'${s}'
 
     def __pos__(self):
         return Money(amount=self.amount, currency=self.currency)
@@ -178,8 +180,8 @@ class Money(object):
     def __ge__(self, other):
         return self > other or self == other
 
-    def __hash__(self):
-        return self.__repr__
+    def __hash__(self) -> int:
+        return hash(self.__repr__)
 
     AMOUNT_RE = re.compile(r"^([+|-]?)(\d+)(\.\d+)$")
     CURRENCY_RE = re.compile(r"^[A-Z][A-Z][A-Z]$")
index 1230db22f90a6d467a6b94e66daa5674e7963163..3a6f743342f0d0c84f3eb139e5a5ebf16bab1553 100644 (file)
@@ -1,9 +1,13 @@
 #!/usr/bin/env python3
 
+"""An enum to represent people."""
+
 import enum
 
 
-class Person(enum.Enum):
+class Person(enum.IntEnum):
+    """An enum to represent people."""
+
     UNKNOWN = 0
     SCOTT = 1
     LYNN = 2
index 64a472650242f863e24a02cd03cb44344db10a76..c365848fa0bd2eb1a34916ec5e8071348e0dffdc 100644 (file)
@@ -1,9 +1,13 @@
 #!/usr/bin/env python3
 
+"""A class to represent a rate of change."""
+
 from typing import Optional
 
 
 class Rate(object):
+    """A class to represent a rate of change."""
+
     def __init__(
         self,
         multiplier: Optional[float] = None,
@@ -28,9 +32,7 @@ class Rate(object):
             self.multiplier = 1.0 + percent_change / 100
             count += 1
         if count != 1:
-            raise Exception(
-                'Exactly one of percentage, percent_change or multiplier is required.'
-            )
+            raise Exception('Exactly one of percentage, percent_change or multiplier is required.')
 
     def apply_to(self, other):
         return self.__mul__(other)