
""" calculate_grade.py -- calculate introduction to Python grade during 
                          semester based on total credit assignments, 
                          completions, extra credit completions
                          and flashcard completions

    David Blaikie (dbb212@nyu.edu)

    Last Revised:  2025-08-21
"""


def get_grade(x: int) -> str:

    return ('A' if x >= 93 else 'A-' if x >= 90 else 'B+' if x >= 87 else
            'B' if x >= 83 else 'B-' if x >= 80 else 'C+' if x >  77 else
            'C' if x >= 73 else 'C-' if x >= 70 else 'D+' if x >= 67 else
            'D' if x >= 63 else 'D-' if x >= 60 else 'F' )


def minutes_to_hours_minutes(total_minutes: int) -> tuple[int, int]:
    hours = total_minutes // 60
    minutes = total_minutes % 60
    return hours, minutes


# this count of credit assignments for each session 
# will need to be updated if credit assignments are added or removed
week_credit_assignments = { 1:3, 2:3, 3:5, 4:7, 5:5, 6:4, 7:5, 8:3, 9:6 }

print()
print('* Grade Calculator *')
print()
print('This program calculates your grade based on 3 factors:')
print('  - whether flashcard time spent is up-to-date (20% of grade)')
print('  - number of completed for-credit solutions   (80% of grade)')
print('    - number of completed extra credit solutions (1/4 credit added to completions)')
print()
print('Please note that this is not an official grade and is not used \n'
      'to compute your official grade.  It is simply a grade calculator \n'
      'offered for your convenience.')
print()

try:
    current_week = int(input('enter current week #: '))
    assert 1 <= current_week <= 10
except (ValueError, AssertionError):
    exit('error: current week number must be a number from 1-10')

ui = input('have you done your homework this week?  Do you want to include '
           "this week's homework in your calculation? ")

if not ui.startswith('y') and not ui.startswith('Y'):
    current_week -= 1

if not current_week:
    exit('error: no weeks to count')

print(f'Calculating up through week {current_week}')
print()

total_credit_assignments = sum([ items[1] for items in week_credit_assignments.items()
                                 if items[0] <= current_week ])


try:
    credit_completions = int(input('enter number of completed regular credit homeworks: '))
    extra_credit_completions = int(input('enter number of completed extra credit homeworks: '))
    assert credit_completions >= 0
    assert extra_credit_completions >= 0
except ValueError:
    exit('error completions must be all digits')
except AssertionError:
    exit('error: completions must be > 0')


if credit_completions > total_credit_assignments:
    exit(f'error: there are only {total_credit_assignments} possible credit assignments '
         f'through session {current_week}')


# flashcards credit must be 0 or 100 only, depending on 4.5 hours completion
flashcard_weeks = current_week if current_week < 10 else current_week - 1

hours, minutes = minutes_to_hours_minutes(flashcard_weeks * 30)

question = 'have you completed '
hours_str = ''
joiner = ', '
minutes_str = ''
ending = ' in the flashcards? (y/n) '


if hours:
    hours_str = f'{hours} hour{"s" if hours > 1 else ""}'
if minutes:
    minutes_str = f'{minutes} minutes'
if not hours or not minutes:
    joiner = ''


msg = question + hours_str + joiner + minutes_str + ending
fcq = input(msg)


if fcq.startswith('y') or fcq.startswith('Y'):
    flashcards_score = 100       
elif fcq.startswith('n') or fcq.startswith('N'):
    flashcards_score = 0
else:
    exit('error: must answer with y or n')


hw_score = ( ((extra_credit_completions * 0.25) + 
               credit_completions) / 
             total_credit_assignments ) * 100

final_score = (hw_score * .8) + (flashcards_score * .2)

print()
print(f'As of Session {current_week}:')
print(f'  your for-credit homework count:     {credit_completions}/{total_credit_assignments}')
print(f'  your extra credit homework count:   {extra_credit_completions}')

print(f'  your total homework score:          {round(hw_score, 2)}')

print(f'  your flashcards score:              {flashcards_score}')
print()
print()

current = ""
as_of = ""
if current_week < 10:
    current = "current "
    as_of = f" (as of Session {current_week})"

print(f'Your {current}score{as_of}: {round(final_score)}')

print(f'Your {current}grade{as_of}: {get_grade(final_score)}')
print()

if current_week < 10:
    print('Note: since we are not at the end of the semester, this is \n'
          '      an interim grade and will be improved or reduced by \n'
          '      your performance in the remaining weeks.')
    print()

print(f'Note: if you are behind on the flashcards, please get in touch \n'
      '       with the instructor.')

print()


