Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion TODO
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
- add tab-completion for label during motivation grading
- add common labels to criteria.txt
- tab-completion does not work out of the box in the Mac terminal: https://github.com/ASPP/grader/issues/26
- if two reviewers add the same label there is a git-conflict
- add a `o` key to the grade verb that allows to set overrides. Fields that can be overridden must be tab-auto-completed and possible values for the field must be also tab-auto-completed.
- verify that we can dynamically change the formula and this has an effect on the ranking
52 changes: 44 additions & 8 deletions grader/applications.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import collections
import configparser
import contextlib
import csv
from fnmatch import fnmatch
import functools
import io
import itertools
import keyword
import locale
import math
import pprint
import re
Expand All @@ -15,6 +17,17 @@
from . import (person, vector, util)
from .util import printff


@contextlib.contextmanager
def override_locale(category, locale_string):
# Context manager to set locale temporarily.
# Strangely, it seems that there is no builtin that does that.
prev_locale_string = locale.getlocale(category)
locale.setlocale(category, locale_string)
yield
locale.setlocale(category, prev_locale_string)


DEBUG_MAPPINGS = False

# CSV-file:
Expand Down Expand Up @@ -376,18 +389,41 @@ def get_ratings(self, field):

def save(self, filename=None):
filename = filename or self.filename
with open(filename, 'wt') as file:
self.save_to_file(file)
text = self.to_string()

def save_to_file(self, file):
filename.write_text(text)

printff(f'Saved changes to {filename}')

def to_string(self):
# save our data to the INI file
cp = configparser.ConfigParser(comment_prefixes='#', inline_comment_prefixes='#')
cp.read_dict(self.data)
cp.write(file)

name = getattr(file, 'name', '(tmp)')
printff(f'Saved changes to {name}')

with override_locale(locale.LC_COLLATE, 'en_US.UTF-8'):
# The order of sections is kept stable.
#
# Within a section, we sort the fields alphabetically,
# with the en_US.UTF-8 collation, which puts accented
# letters in the expected place, usually right after the
# unaccented version.
#
# As an exception, the [*_rating] sections are not sorted,
# so that the items remain from "lowest" to "highest".
sorted_data = {
name:{
k:values[k] for k in sorted(
values,
key=(lambda x:0) if name.endswith('_rating') else functools.cmp_to_key(locale.strcoll),
)
}
for name, values in self.data.items()
}

out = io.StringIO()

cp.read_dict(sorted_data)
cp.write(out)
return out.getvalue()

def __setitem__(self, key, value):
# allow to set items in the section of the INI using a dotted form, for ex:
Expand Down
45 changes: 39 additions & 6 deletions grader/test_applications.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import io
import pathlib
import os
import time
Expand Down Expand Up @@ -50,13 +49,48 @@ def test_all_years(path):
person two = 1
some son jr. = 1

[verify_rating_is_unsorted_rating]
novice = +10.0
competent = 0.0
expert = -10.0

[labels]
john doe = VEGAN, VIP
jędrzej marcin mirosławski piołun = UTF-8, VEGAN
person one = PALEO

"""

ini_sorted = """\
[extra]
key_num = 111.5
key_str = value

[motivation_score-zbyszek]
jędrzej marcin mirosławski piołun = 1
person one = 1
person three = 0
person two = -1
some son jr. = -1

[motivation_score-other]
person one = -1
person two = 1
some son jr. = 1

[verify_rating_is_unsorted_rating]
novice = 10.0
competent = 0.0
expert = -10.0

[labels]
jędrzej marcin mirosławski piołun = UTF-8, VEGAN
john doe = VEGAN, VIP
person one = PALEO

"""


def get_ini(tmp_path, *extra, ini_filename='ini1.ini'):
input = tmp_path / ini_filename
input.write_text(ini_string + '\n'.join(extra))
Expand Down Expand Up @@ -167,7 +201,7 @@ def test_applications_ini_save(tmp_path):
out = tmp_path / 'ini1.copy'
ini.save(out)

assert out.read_text() == ini_string
assert out.read_text() == ini_sorted

# Replace an exisiting entry.
# We use an int, but the type is converted to float internally
Expand Down Expand Up @@ -292,11 +326,10 @@ def test_applications_labels(app):
assert app['Person One'].remove_label('PALEO') is True
assert app['Person One'].labels == []

out = io.StringIO()
app.ini.save_to_file(file=out)
text = app.ini.to_string()

assert 'john doe = VEGAN, VIP' in out.getvalue()
assert 'jędrzej marcin mirosławski piołun = UTF-8, VEGAN' in out.getvalue()
assert 'john doe = VEGAN, VIP' in text
assert 'jędrzej marcin mirosławski piołun = UTF-8, VEGAN' in text

def test_applications_all_labels(app):
assert app.all_labels() == {'PALEO', 'UTF-8', 'VEGAN'}
Expand Down