<commit_before>#!/usr/bin/env python3

from argparse import ArgumentParser, ArgumentTypeError
import csv
import datetime
from enum import Enum, auto
import os
from distutils.util import strtobool
from math import isclose
import sys
import time

LOG_FILE_PATH = os.path.join(os.path.expanduser('~'), '.hour_logger', 'log.csv')

class ModeFailException(Exception):
    pass

def prompt_until_success(question, parser_fn):
    while True:
        print(question, end='')
        try:
            return parser_fn(input())
        except ValueError:
            print('Not a valid response.')

class LogEvent(Enum):
    WAGE_SET = auto()
    PAYMENT = auto()
    BEGIN = auto()
    END = auto()

def read_log():
    with open(LOG_FILE_PATH, 'r') as log_file:
        csv_reader = csv.reader(log_file)
        for log in csv_reader:
            event = next((e for e in LogEvent if e.name == log[0]), None)
            if event is None:
                raise ModeFailException(f'Log file at {LOG_FILE_PATH} is corrupted; found an unknown log event: {log}')
            value = float(log[1])
            yield event, value


def check_log_integrity(expected_in_shift=None, expected_in_shift_msg=None):
    if (expected_in_shift is None and expected_in_shift_msg is not None) or (expected_in_shift is not None and expected_in_shift_msg is None):
        raise ValueError('Either both, or neither of expected_in_shift and expected_in_shift_msg should be null.')
    has_wage = False
    in_shift = False

    for event, _ in read_log():
        if event == LogEvent.WAGE_SET:
            has_wage = True
        elif event == LogEvent.PAYMENT:
            continue
        elif event == LogEvent.BEGIN and in_shift:
            raise ModeFailException(f'Log file at {LOG_FILE_PATH} is corrupted; found two successive {LogEvent.BEGIN.name}s without a {LogEvent.END.name} in between. Try fixing or deleting it.')
        elif event == LogEvent.END and not in_shift:
            raise ModeFailException(f'Log file at {LOG_FILE_PATH} is corrupted; found two successive {LogEvent.END.name}s without a {LogEvent.BEGIN.name} in between. Try fixing or deleting it.')
        else:
            assert event == LogEvent.BEGIN or event == LogEvent.END, f'Support for new LogEvent {event.name} not added.'
            in_shift = not in_shift

    if not has_wage:
        raise ModeFailException(f'Log file at {LOG_FILE_PATH} is corrupted; no {LogEvent.WAGE_SET} events found. Try fixing or deleting it.')

    if expected_in_shift is not None and in_shift != expected_in_shift:
        raise ModeFailException(expected_in_shift_msg)

def configure_as_new():
    should_configure = prompt_until_success(question=f'Looks like you have never configured {sys.argv[0]} before. Would you like to do so now? [y/n] ', parser_fn=lambda x: strtobool(x) == 1)
    if not should_configure:
        raise ModeFailException(f'{sys.argv[0]} cannot run without configuring.')

    wage = prompt_until_success(question='What is your hourly wage? ', parser_fn=float)

    if not os.path.exists(os.path.dirname(LOG_FILE_PATH)):
        os.makedirs(os.path.dirname(LOG_FILE_PATH))

    with open(LOG_FILE_PATH, 'w') as log_file:
        csv_writer = csv.writer(log_file)
        csv_writer.writerow([LogEvent.WAGE_SET.name, wage])

    print(f'Log log file created at: {LOG_FILE_PATH}.')

def ensure_integrity(expected_in_shift=None, msg=None):
    def _ensure_integrity(fn):
        def wrapper(*args, **kwargs):
            if os.path.isfile(LOG_FILE_PATH):
                check_log_integrity(expected_in_shift, msg)
            else:
                configure_as_new()
            fn(*args, **kwargs)

        return wrapper
    return _ensure_integrity

def write_log(event, value):
    with open(LOG_FILE_PATH, 'a') as log_file:
        csv_writer = csv.writer(log_file)
        csv_writer.writerow([event.name, value])

@ensure_integrity(expected_in_shift=False, msg='Cannot change the wage while a shift is ongoing.')
def change_wage(wage):
    write_log(LogEvent.WAGE_SET, wage)

@ensure_integrity()
def payment(amount):
    write_log(LogEvent.PAYMENT, amount)

@ensure_integrity(expected_in_shift=False, msg='Cannot begin a shift while one is ongoing.')
def begin():
    write_log(LogEvent.BEGIN, time.time())

@ensure_integrity(expected_in_shift=True, msg='Cannot end a shift when none is ongoing.')
def end():
    write_log(LogEvent.END, time.time())

@ensure_integrity()
def status():
    total_earned = 0
    total_paid = 0
    active_wage = None
    shift_started_at = None

    for event, value in read_log():
        if event == LogEvent.WAGE_SET:<commit_msg>Use write_log when configuring<commit_after>#!/usr/bin/env python3

from argparse import ArgumentParser, ArgumentTypeError
import csv
import datetime
from enum import Enum, auto
import os
from distutils.util import strtobool
from math import isclose
import sys
import time

LOG_FILE_PATH = os.path.join(os.path.expanduser('~'), '.hour_logger', 'log.csv')

class ModeFailException(Exception):
    pass

def prompt_until_success(question, parser_fn):
    while True:
        print(question, end='')
        try:
            return parser_fn(input())
        except ValueError:
            print('Not a valid response.')

class LogEvent(Enum):
    WAGE_SET = auto()
    PAYMENT = auto()
    BEGIN = auto()
    END = auto()

def read_log():
    with open(LOG_FILE_PATH, 'r') as log_file:
        csv_reader = csv.reader(log_file)
        for log in csv_reader:
            event = next((e for e in LogEvent if e.name == log[0]), None)
            if event is None:
                raise ModeFailException(f'Log file at {LOG_FILE_PATH} is corrupted; found an unknown log event: {log}')
            value = float(log[1])
            yield event, value

def write_log(event, value):
    with open(LOG_FILE_PATH, 'a') as log_file:
        csv_writer = csv.writer(log_file)
        csv_writer.writerow([event.name, value])

def check_log_integrity(expected_in_shift=None, expected_in_shift_msg=None):
    if (expected_in_shift is None and expected_in_shift_msg is not None) or (expected_in_shift is not None and expected_in_shift_msg is None):
        raise ValueError('Either both, or neither of expected_in_shift and expected_in_shift_msg should be null.')
    has_wage = False
    in_shift = False

    for event, _ in read_log():
        if event == LogEvent.WAGE_SET:
            has_wage = True
        elif event == LogEvent.PAYMENT:
            continue
        elif event == LogEvent.BEGIN and in_shift:
            raise ModeFailException(f'Log file at {LOG_FILE_PATH} is corrupted; found two successive {LogEvent.BEGIN.name}s without a {LogEvent.END.name} in between. Try fixing or deleting it.')
        elif event == LogEvent.END and not in_shift:
            raise ModeFailException(f'Log file at {LOG_FILE_PATH} is corrupted; found two successive {LogEvent.END.name}s without a {LogEvent.BEGIN.name} in between. Try fixing or deleting it.')
        else:
            assert event == LogEvent.BEGIN or event == LogEvent.END, f'Support for new LogEvent {event.name} not added.'
            in_shift = not in_shift

    if not has_wage:
        raise ModeFailException(f'Log file at {LOG_FILE_PATH} is corrupted; no {LogEvent.WAGE_SET} events found. Try fixing or deleting it.')

    if expected_in_shift is not None and in_shift != expected_in_shift:
        raise ModeFailException(expected_in_shift_msg)

def configure_as_new():
    should_configure = prompt_until_success(question=f'Looks like you have never configured {sys.argv[0]} before. Would you like to do so now? [y/n] ', parser_fn=lambda x: strtobool(x) == 1)
    if not should_configure:
        raise ModeFailException(f'{sys.argv[0]} cannot run without configuring.')

    wage = prompt_until_success(question='What is your hourly wage? ', parser_fn=float)

    if not os.path.exists(os.path.dirname(LOG_FILE_PATH)):
        os.makedirs(os.path.dirname(LOG_FILE_PATH))

    write_log(LogEvent.WAGE_SET, wage)

    print(f'Log log file created at: {LOG_FILE_PATH}.')

def ensure_integrity(expected_in_shift=None, msg=None):
    def _ensure_integrity(fn):
        def wrapper(*args, **kwargs):
            if os.path.isfile(LOG_FILE_PATH):
                check_log_integrity(expected_in_shift, msg)
            else:
                configure_as_new()
            fn(*args, **kwargs)

        return wrapper
    return _ensure_integrity

@ensure_integrity(expected_in_shift=False, msg='Cannot change the wage while a shift is ongoing.')
def change_wage(wage):
    write_log(LogEvent.WAGE_SET, wage)

@ensure_integrity()
def payment(amount):
    write_log(LogEvent.PAYMENT, amount)

@ensure_integrity(expected_in_shift=False, msg='Cannot begin a shift while one is ongoing.')
def begin():
    write_log(LogEvent.BEGIN, time.time())

@ensure_integrity(expected_in_shift=True, msg='Cannot end a shift when none is ongoing.')
def end():
    write_log(LogEvent.END, time.time())

@ensure_integrity()
def status():
    total_earned = 0
    total_paid = 0
    active_wage = None
    shift_started_at = None

    for event, value in read_log():
        if event == LogEvent.WAGE_SET:
{'fraction_non_alphanumeric': 0.09520697167755991, 'fraction_numerical': 0.0017429193899782135, 'mean_word_length': 3.359449192782526, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '543522', 'n_tokens_mistral': 2899, 'n_tokens_neox': 2822, 'n_words': 819}