#!/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
)
-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]:
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
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:
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.'
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)
#!/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
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)
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]$")
#!/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
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)
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]$")
#!/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,
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)