From cb87462ba489630e3db51d6513a4478b619e4102 Mon Sep 17 00:00:00 2001 From: Tiziano Zito Date: Sat, 12 Apr 2025 17:18:30 +0200 Subject: [PATCH 1/5] add a ini file for testing --- tests/data/year99/applications.ini | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 tests/data/year99/applications.ini diff --git a/tests/data/year99/applications.ini b/tests/data/year99/applications.ini new file mode 100644 index 0000000..7d05b86 --- /dev/null +++ b/tests/data/year99/applications.ini @@ -0,0 +1,3 @@ +[identities] +0 = bob +1 = alice From be23c05a7ee9690217d1f1a3ec00d84c42769c1b Mon Sep 17 00:00:00 2001 From: Tiziano Zito Date: Sat, 12 Apr 2025 17:18:58 +0200 Subject: [PATCH 2/5] add a temporary new script to gradually move to grader2 --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 2b25f69..8fe2314 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,6 +20,7 @@ content-type = "text/markdown" [project.scripts] grader = "grader.grader:main" +grader2 = "grader.grader2:main" [tool.aliases] test = "pytest" From 1a4f1cc6a961545a890e8ff4a2c4223682bef6dd Mon Sep 17 00:00:00 2001 From: Tiziano Zito Date: Sat, 12 Apr 2025 17:19:10 +0200 Subject: [PATCH 3/5] implement do_identity with tests --- grader/grader2.py | 80 +++++++++++++++++++++++++++++++++++++++++++ tests/test_grader2.py | 23 +++++++++++++ 2 files changed, 103 insertions(+) create mode 100644 grader/grader2.py create mode 100644 tests/test_grader2.py diff --git a/grader/grader2.py b/grader/grader2.py new file mode 100644 index 0000000..7e05ae8 --- /dev/null +++ b/grader/grader2.py @@ -0,0 +1,80 @@ +import pathlib +import sys + +import rich +from rich import print + +from . import cmd_completer +from .applications import Applications + +class Grader(cmd_completer.Cmd_Completer): + #prompt = "[green]grader[/green][yellow]>[/yellow] " + prompt = "grader2> " + set_completions = cmd_completer.Cmd_Completer.set_completions + + def __init__(self, identity, csv_file, history_file=None): + super().__init__(histfile=history_file) + self.applications = Applications(csv_file=csv_file) + self.identity = None + if identity is not None: + self.do_identity(identity) + self.archive = [] + + identity_options = ( + cmd_completer.PagedArgumentParser('identity') + .add_argument('identity', type=str, help='become this identity') + ) + + def do_identity(self, args): + "Switch identity" + opts = self.identity_options.parse_args(args.split()) + self.identity = opts.identity + numerical_identities = list(self.applications.ini['identities'].keys()) + nominal_identities = list(self.applications.ini['identities'].values()) + if self.identity not in numerical_identities+nominal_identities: + raise ValueError(f'"{self.identity}" is not a registered identity. Add it ' + f'to {self.applications.ini.filename}') + +grader_options = ( + cmd_completer.ModArgumentParser('grader') + .add_argument('-i', '--identity', type=str, + help='Name of person grading applications') + .add_argument('--history-file', type=pathlib.Path, + default=pathlib.Path('~/.grader_history').expanduser(), + help='File to record typed in commands') + ) + +def main(): + opts = grader_options.parse_args() + + cmd = Grader( + identity=opts.identity, + csv_file=pathlib.Path('applications.csv'), + history_file=opts.history_file, + ) + + if sys.stdin.isatty(): + while True: + try: + cmd.cmdloop() + break + except KeyboardInterrupt: + print() + except SyntaxError as e: + printff('bad command: {}', e) + except ValueError as e: + printff('bad value: {}', e) + traceback.print_exc() + except Exception as e: + printff('programming error: {}', e) + traceback.print_exc() + else: + input = cmd_completer.InputFile(sys.stdin) + for line in input: + cmd.onecmd(line) + + if cmd.applications.ini.has_modifications(): + cmd.applications.ini.save() + +if __name__ == '__main__': + sys.exit(main()) diff --git a/tests/test_grader2.py b/tests/test_grader2.py new file mode 100644 index 0000000..8c664cc --- /dev/null +++ b/tests/test_grader2.py @@ -0,0 +1,23 @@ +import pathlib + +import pytest + +from grader import grader2 + +TESTCSV = pathlib.Path('tests/data/year99/applications.csv') + + +def test_identity(): + id = 'alice' + gr = grader2.Grader(id, TESTCSV) + assert gr.identity == id + gr.do_identity('bob') + assert gr.identity == 'bob' + +def test_no_identity(): + gr = grader2.Grader(None, TESTCSV) + assert gr.identity is None + +def test_unknown_identity(): + with pytest.raises(ValueError, match='charlie'): + gr = grader2.Grader('charlie', TESTCSV) From 2d47c4d4466df069a365641a01c9c5c768d589e8 Mon Sep 17 00:00:00 2001 From: Tiziano Zito Date: Sat, 12 Apr 2025 17:44:32 +0200 Subject: [PATCH 4/5] test loading of archives --- grader/grader2.py | 10 +++++++++- tests/data/year99/2009-location/applications.csv | 3 +++ tests/data/year99/2019-location/applications.csv | 3 +++ tests/test_grader2.py | 9 +++++++++ 4 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 tests/data/year99/2009-location/applications.csv create mode 100644 tests/data/year99/2019-location/applications.csv diff --git a/grader/grader2.py b/grader/grader2.py index 7e05ae8..2637b7a 100644 --- a/grader/grader2.py +++ b/grader/grader2.py @@ -19,12 +19,20 @@ def __init__(self, identity, csv_file, history_file=None): if identity is not None: self.do_identity(identity) self.archive = [] + for path in sorted(csv_file.parent.glob('*/applications.csv'), reverse=True): + # years before 2012 are to be treated less strictly + relaxed = any(f'{year}-' in str(path) for year in range(2009,2012)) + old = Applications(csv_file=path, relaxed=relaxed) + self.archive.append(old) + + for person in self.applications: + person.set_n_applied(self.archive) + identity_options = ( cmd_completer.PagedArgumentParser('identity') .add_argument('identity', type=str, help='become this identity') ) - def do_identity(self, args): "Switch identity" opts = self.identity_options.parse_args(args.split()) diff --git a/tests/data/year99/2009-location/applications.csv b/tests/data/year99/2009-location/applications.csv new file mode 100644 index 0000000..1a553e1 --- /dev/null +++ b/tests/data/year99/2009-location/applications.csv @@ -0,0 +1,3 @@ +SURNAME,EMAIL,NAME,GENDER,NAT,POSITION,INSTIT LOC,INSTITUTION,NEUROSCIENCE,PROGRAMMING,MOTIVATION,PYTHON,INTERNATIONAL,OPENSOURCE,GROUP,POSITION[OTHER],PROGRAMMING DESCRIPTION,OPEN_SOURCE_DESCRIPTION,CV,BORN,APPLIED +Smith,,Maggie,M,FR,PhD student,FR Paris,Computational Neuroscience,1,4,5,4,2,0,,,,,,1900,No +Schmidt,,Margarete,F,DE,PhD student,IT Milano,Machine Learning,1,3,5,4,1,1,,,,,,1900,No diff --git a/tests/data/year99/2019-location/applications.csv b/tests/data/year99/2019-location/applications.csv new file mode 100644 index 0000000..8d0cb9a --- /dev/null +++ b/tests/data/year99/2019-location/applications.csv @@ -0,0 +1,3 @@ +"Response ID";"Date submitted";"Last page";"Start language";"Seed";"Token";"Date started";"Date last action";"IP address";"Your affiliation (the place where you are currently working/studying): [University/Institute/Company:]";"Your affiliation (the place where you are currently working/studying): [Group/Division/Department:]";"Country of Affiliation:";"Position:";"Position: [Other]";"How do you estimate your programming skills in general? Please be honest! We want to have students with diverse skill levels, we don't plan to accept only the best programmers! ";"How do you estimate your Python skills?";"You estimated to be a Competent, Proficient or Expert programmer. Write a short description of your programming experience";"How do you estimate your exposure to open-source software?";"You contribute to open-source projects. Write a short description of your contributions.";"Do you habitually use a Version Control System for your softwareprojects? If yes, which one?";"Motivation letter: Why do you think Advanced Scientific Programming in Python is an appropriate course for your skill profile? Do not insert identifying information here (for example, do not sign the letter): we do blind reviews and do not want to be biased by your name! ";"Did you already apply to one of the previous instances of the school?";"Nationality:";"Year of birth:";"Gender:";"Please type in a short curriculum vitae (no links to external files/no list of publications), just enough for us to get an idea of what you have done until now...";"First name";"Last name";"Email address" +"1508";"2029-02-28 17:14:05";"15";"en";"1786416015";"gskDkewd7sBUJJu";"2019-06-28 16:34:41";"2019-02-28 17:14:05";"55.55.555.555";"Gruppo MarioRossi";"AI - Data Science";"Italy";"Employee";"";"Novice/Advanced beginner";"Novice/Advanced beginner";"";"Minor contributions (bug reports, mailing lists, ...)";"";"git";"blah";"Yes";"Italy";"1939";"female";"CVCV cv";"Alice";"Smith";"alice@example.com" +"1509";"2019-02-28 17:14:05";"15";"en";"1782416015";"gskD22wd7sBUJJu";"2029-06-28 16:32:41";"2111-02-28 17:14:05";"55.55.525.555";"Gruppo MarioGossi";"AI - XXXX Science";"Germany";"Post-Doctorate";"";"Competent/Proficient";"Novice/Advanced beginner";"";"Minor contributions (bug reports, mailing lists, ...)";"";"git";"blah";"Yes";"France";"1949";"male";"another CVCV cv";"Bob";"Travolta";"bob@example.com" diff --git a/tests/test_grader2.py b/tests/test_grader2.py index 8c664cc..e008e19 100644 --- a/tests/test_grader2.py +++ b/tests/test_grader2.py @@ -21,3 +21,12 @@ def test_no_identity(): def test_unknown_identity(): with pytest.raises(ValueError, match='charlie'): gr = grader2.Grader('charlie', TESTCSV) + +def test_archive(): + gr = grader2.Grader(None, TESTCSV) + # we have year 2009 and year 2019 + assert len(gr.archive) == 2 + for apps in gr.archive: + assert len(apps.people) == 2 + + From 06ffd0c9672589706fccb120e8b11579c8be2652 Mon Sep 17 00:00:00 2001 From: Tiziano Zito Date: Sat, 12 Apr 2025 17:50:22 +0200 Subject: [PATCH 5/5] bob travolta applied twice and test that we really set it --- tests/data/year99/applications.csv | 2 +- tests/test_grader2.py | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/tests/data/year99/applications.csv b/tests/data/year99/applications.csv index adba3aa..f1945eb 100644 --- a/tests/data/year99/applications.csv +++ b/tests/data/year99/applications.csv @@ -1,3 +1,3 @@ "id. Response ID","submitdate. Date submitted","lastpage. Last page","startlanguage. Start language","seed. Seed","token. Access code","startdate. Date started","datestamp. Date last action","ipaddr. IP address","AFFILIATION[UNI]. Your affiliation (the place where you are currently working/studying): [University/Institute/Company:]","AFFILIATION[GRP]. Your affiliation (the place where you are currently working/studying): [Group/Division/Department:]","AFFILIATION-NATION. Country of Affiliation:","POSITION. Position:","POSITION[other]. Position: [Other]","PROGEXPERIENCE. How do you estimate your programming skills in general?Please be honest! We want to have students with diverse skill levels, we don't plan to accept only the best programmers!","PYTEXPERIENCE. How do you estimate your Python skills?","PROG-TEXT. Write a short description of your programming experience","OPENSOURCE. How do you estimate your exposure to open-source software?","OPEN-SOURCE-TEXT. You contribute to open-source projects. Write a short description of your contributions.","VCS. Do you habitually use a Version Control System for your software projects? If yes, which one? ","MOTIVATION. Motivation letter: What brings you to ASPP's application form? How would you benefit from attending ASPP?  How would your work back in the lab change after attending ASPP? Do not insert identifying information here (for example, do not sign the letter): we do blind reviews and do not want to be biased by your name! ","PREV-APPLICATION. Did you already apply to one of the previous instances of the school?","NATIONALITY. Nationality:","BIRTH. Year of birth:","GENDER. Gender:","CURRICULUM. Please type in a short curriculum vitae (no links to external files/no list of publications), just enough for us to get an idea of what you have done until now...","firstname. First name","lastname. Last name","email. Email address" "218","2024-03-07 11:35:25","15","en","1200376851","pukt38jrePXmaxy","2024-02-22 17:36:33","2024-03-07 11:35:25","101.101.35.38","Charité - Universitätsmedizin Berlin","Einstein Center for Neurosciences","Germany","PhD student","","Competent/Proficient","Competent/Proficient","programming experience much","User","","git","motivation letter interesting","No","Germany","1990","female","cv cv cv cv cv","Anna","Lastname","email@example.com" -"219","2024-07-03 11:25:35","15","en","1200376851","pkt38jrePXmaxyu","2024-02-22 17:36:33","2024-03-07 11:35:25","101.101.35.38","Universita deglia Studia","Einstein Center for Neurosciences","Italy","PhD student","","Competent/Proficient","Competent/Proficient","programming experience much","User","","git","motivation letter interesting","No","Germany","1990","male","CV CV CV CV CV","Markus","Lastname a Spazia","email@example.com" +"219","2024-07-03 11:25:35","15","en","1200376851","pkt38jrePXmaxyu","2024-02-22 17:36:33","2024-03-07 11:35:25","101.101.35.38","Universita deglia Studia","Einstein Center for Neurosciences","Italy","PhD student","","Competent/Proficient","Competent/Proficient","programming experience much","User","","git","motivation letter interesting","No","Germany","1990","male","CV CV CV CV CV","Bob","Travolta","email@example.com" diff --git a/tests/test_grader2.py b/tests/test_grader2.py index e008e19..fbe28fd 100644 --- a/tests/test_grader2.py +++ b/tests/test_grader2.py @@ -29,4 +29,11 @@ def test_archive(): for apps in gr.archive: assert len(apps.people) == 2 - +def test_setting_n_applied(): + gr = grader2.Grader(None, TESTCSV) + # Bob Travolta applied twice in our dataset + bobs = gr.applications.filter(name='Bob') + assert len(bobs) == 1 + bob = bobs[0] + # n_applied is the number of times applied before + assert bob.n_applied == 1