From 346e2c5b31230b22a2f1f1e5eb442d0d95b2f649 Mon Sep 17 00:00:00 2001 From: shiying Date: Mon, 13 Jan 2025 14:58:15 +0000 Subject: [PATCH 01/11] Add to-dos --- notebooks/to-do-list | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 notebooks/to-do-list diff --git a/notebooks/to-do-list b/notebooks/to-do-list new file mode 100644 index 0000000..301cd0f --- /dev/null +++ b/notebooks/to-do-list @@ -0,0 +1,6 @@ +cancel sort +save comparison data for ranked file +alter comparison data + + +change where pickle,xlsx files are saved \ No newline at end of file From fc28f0a78dc86817f1474b51af7f517a7297e68a Mon Sep 17 00:00:00 2001 From: shiying Date: Tue, 14 Jan 2025 16:30:01 +0000 Subject: [PATCH 02/11] Rename sample directory --- .../Emma_Jones_16743_Candidate_Pack.pdf | Bin .../Michael_Davis_89763_Candidate_Pack.pdf | Bin .../Patrick_Campbell_89763_Candidate_Pack (1).pdf | Bin .../Sam_Harrington_16743_Candidate_Pack.pdf | Bin .../Sarah_Thompson_82376_Candidate_Pack.pdf | Bin {test_role => test-role_0001}/criteria.csv | 0 6 files changed, 0 insertions(+), 0 deletions(-) rename {test_role => test-role_0001}/Emma_Jones_16743_Candidate_Pack.pdf (100%) rename {test_role => test-role_0001}/Michael_Davis_89763_Candidate_Pack.pdf (100%) rename {test_role => test-role_0001}/Patrick_Campbell_89763_Candidate_Pack (1).pdf (100%) rename {test_role => test-role_0001}/Sam_Harrington_16743_Candidate_Pack.pdf (100%) rename {test_role => test-role_0001}/Sarah_Thompson_82376_Candidate_Pack.pdf (100%) rename {test_role => test-role_0001}/criteria.csv (100%) diff --git a/test_role/Emma_Jones_16743_Candidate_Pack.pdf b/test-role_0001/Emma_Jones_16743_Candidate_Pack.pdf similarity index 100% rename from test_role/Emma_Jones_16743_Candidate_Pack.pdf rename to test-role_0001/Emma_Jones_16743_Candidate_Pack.pdf diff --git a/test_role/Michael_Davis_89763_Candidate_Pack.pdf b/test-role_0001/Michael_Davis_89763_Candidate_Pack.pdf similarity index 100% rename from test_role/Michael_Davis_89763_Candidate_Pack.pdf rename to test-role_0001/Michael_Davis_89763_Candidate_Pack.pdf diff --git a/test_role/Patrick_Campbell_89763_Candidate_Pack (1).pdf b/test-role_0001/Patrick_Campbell_89763_Candidate_Pack (1).pdf similarity index 100% rename from test_role/Patrick_Campbell_89763_Candidate_Pack (1).pdf rename to test-role_0001/Patrick_Campbell_89763_Candidate_Pack (1).pdf diff --git a/test_role/Sam_Harrington_16743_Candidate_Pack.pdf b/test-role_0001/Sam_Harrington_16743_Candidate_Pack.pdf similarity index 100% rename from test_role/Sam_Harrington_16743_Candidate_Pack.pdf rename to test-role_0001/Sam_Harrington_16743_Candidate_Pack.pdf diff --git a/test_role/Sarah_Thompson_82376_Candidate_Pack.pdf b/test-role_0001/Sarah_Thompson_82376_Candidate_Pack.pdf similarity index 100% rename from test_role/Sarah_Thompson_82376_Candidate_Pack.pdf rename to test-role_0001/Sarah_Thompson_82376_Candidate_Pack.pdf diff --git a/test_role/criteria.csv b/test-role_0001/criteria.csv similarity index 100% rename from test_role/criteria.csv rename to test-role_0001/criteria.csv From 11fb215d5f2edc96e2949c85533361fdbe8307ef Mon Sep 17 00:00:00 2001 From: shiying Date: Tue, 14 Jan 2025 16:30:48 +0000 Subject: [PATCH 03/11] Ignore sample directory --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 5fa7032..621ce9f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,7 @@ .venv/ *pycache*/ .vscode/ -test_role/shortlist.pickle +test-role_0001/shortlist.pickle tests/shortlist.pickle .pytest_cache/ .ruff_cache/ From 2e2d0a320d13d43975fcf3c52776a38077bfef39 Mon Sep 17 00:00:00 2001 From: shiying Date: Tue, 14 Jan 2025 16:37:52 +0000 Subject: [PATCH 04/11] Get role ID from directory name --- shortlister/model.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/shortlister/model.py b/shortlister/model.py index 15cd19a..9515531 100644 --- a/shortlister/model.py +++ b/shortlister/model.py @@ -128,7 +128,8 @@ def load_shortlist(path: Path) -> (Shortlist, str): def load_role(path, criteria): """Generates role object instance.""" - role = Role(str(path), "0001", criteria) + name, id = str(path).rsplit("_",1) + role = Role(job_title=name, job_id=id, criteria=criteria) return role From 8de0de77d2f033190649155869a0281b563fa3fa Mon Sep 17 00:00:00 2001 From: shiying Date: Tue, 14 Jan 2025 16:38:09 +0000 Subject: [PATCH 05/11] Update test to reflect changes to Role class --- tests/test_controller.py | 2 +- tests/test_model.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_controller.py b/tests/test_controller.py index 73292cc..b9f9c58 100644 --- a/tests/test_controller.py +++ b/tests/test_controller.py @@ -1,7 +1,7 @@ from shortlister.controller import Controller from pathlib import Path -controller = Controller(path=Path("test_role")) +controller = Controller(path=Path("test-role_0001")) def test_create_controller(): diff --git a/tests/test_model.py b/tests/test_model.py index 3f2e966..fee23ec 100644 --- a/tests/test_model.py +++ b/tests/test_model.py @@ -9,7 +9,7 @@ "Excellent": 40, } SCORES = list(SCORES_VALUES.keys()) -path = Path("test_role") +path = Path("test-role_0001") pickle_file_name = Path("shortlist.pickle") csv_file = Path("criteria.csv") @@ -52,7 +52,7 @@ def test_load_role(): ] result = model.load_role(path, model.load_criteria(path / csv_file)) - expected = model.Role(job_title="test_role", job_id="0001", criteria=criteria) + expected = model.Role(job_title="test-role", job_id="0001", criteria=criteria) assert result == expected @@ -117,7 +117,7 @@ def test_save_load(): def test_load_applicant_from_pdf(): applicant: model.Applicant = model.load_applicants_from_pdf( - Path("test_role/Emma_Jones_16743_Candidate_Pack.pdf") + Path("test-role_0001/Emma_Jones_16743_Candidate_Pack.pdf") ) assert applicant.name == "Emma Jones" assert applicant.email == "emmaj@outlook.com" From 9240fc308357c807acabdaf0a351b9c318a1c98c Mon Sep 17 00:00:00 2001 From: shiying Date: Tue, 21 Jan 2025 10:48:32 +0000 Subject: [PATCH 06/11] Add option to quit sorting --- src/shortlister/controller.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/shortlister/controller.py b/src/shortlister/controller.py index 41a6881..10e201f 100644 --- a/src/shortlister/controller.py +++ b/src/shortlister/controller.py @@ -108,6 +108,7 @@ def __init__(self, path, wv_window=None): "s": (self.sort, "SCORE ASCENDING"), "d": (self.sort, "SCORE DESCENDING"), "c": (self.sort, "COMPARISON"), + "q":(self.sort,"QUIT SORT") } self.options_applicant_detail = { "e": (self.score_applicant_step_1, "SCORE"), @@ -321,6 +322,9 @@ def sort(self, k=None): self.ctx.applicants = result print() + elif k == "q": + self.option = self.options_applicant_table + self.show_applicants_table() def filter_applicants(self, k=None): From c66dcaa2f1f8c3ea591301ee4f94566c33b34c5e Mon Sep 17 00:00:00 2001 From: shiying Date: Tue, 21 Jan 2025 15:33:35 +0000 Subject: [PATCH 07/11] Set applicant class's eq method to compare object hash instead --- src/shortlister/model.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/shortlister/model.py b/src/shortlister/model.py index ca0980c..2a96e10 100644 --- a/src/shortlister/model.py +++ b/src/shortlister/model.py @@ -26,7 +26,7 @@ def __repr__(self) -> str: return self.name -@dataclass(eq=False) +@dataclass(frozen=True) class Applicant: """A property of Shortlist - contained within the attribute applicants(list of Applicant objects).""" @@ -47,6 +47,9 @@ def __repr__(self): def __hash__(self): return hash(self.cv) + + def __eq__(self, other): + return hash(self) == hash(other) @dataclass From e232a6305be41e5b6ee27c182b3351ac522b24fd Mon Sep 17 00:00:00 2001 From: shiying Date: Tue, 21 Jan 2025 16:32:54 +0000 Subject: [PATCH 08/11] More scrap pieces of work --- 01.ipynb | 422 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 422 insertions(+) create mode 100644 01.ipynb diff --git a/01.ipynb b/01.ipynb new file mode 100644 index 0000000..7d3e985 --- /dev/null +++ b/01.ipynb @@ -0,0 +1,422 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "ERROR READING: Michael_Davis_89763_Candidate_Pack.pdf\n", + "ERROR READING: Patrick_Campbell_89763_Candidate_Pack (1).pdf\n", + "ERROR READING: Sam_Harrington_16743_Candidate_Pack.pdf\n", + "frozenset({Applicant(name=Emma Jones, scores={}, notes=), Applicant(name=Michael Davis, scores={}, notes=)})\n", + "frozenset({Applicant(name=Emma Jones, scores={}, notes=), Applicant(name=Patrick Campbell, scores={}, notes=)})\n", + "frozenset({Applicant(name=Sam Harrington, scores={}, notes=), Applicant(name=Emma Jones, scores={}, notes=)})\n", + "frozenset({Applicant(name=Emma Jones, scores={}, notes=), Applicant(name=Sarah Thompson, scores={}, notes=)})\n", + "frozenset({Applicant(name=Patrick Campbell, scores={}, notes=), Applicant(name=Michael Davis, scores={}, notes=)})\n", + "frozenset({Applicant(name=Sam Harrington, scores={}, notes=), Applicant(name=Michael Davis, scores={}, notes=)})\n", + "frozenset({Applicant(name=Michael Davis, scores={}, notes=), Applicant(name=Sarah Thompson, scores={}, notes=)})\n", + "frozenset({Applicant(name=Sam Harrington, scores={}, notes=), Applicant(name=Patrick Campbell, scores={}, notes=)})\n", + "frozenset({Applicant(name=Patrick Campbell, scores={}, notes=), Applicant(name=Sarah Thompson, scores={}, notes=)})\n", + "frozenset({Applicant(name=Sam Harrington, scores={}, notes=), Applicant(name=Sarah Thompson, scores={}, notes=)})\n" + ] + }, + { + "ename": "TypeError", + "evalue": "Applicant.__init__() missing 8 required positional arguments: 'cv', 'email', 'phone', 'postcode', 'country_region', 'right_to_work', 'visa_requirement', and 'application_text'", + "output_type": "error", + "traceback": [ + "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[1;31mTypeError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[1;32mIn[6], line 26\u001b[0m\n\u001b[0;32m 22\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m pair \u001b[38;5;129;01min\u001b[39;00m unique_pairs:\n\u001b[0;32m 23\u001b[0m \u001b[38;5;28mprint\u001b[39m(pair)\n\u001b[1;32m---> 26\u001b[0m result \u001b[38;5;241m=\u001b[39m {\u001b[38;5;28mfrozenset\u001b[39m({\u001b[43mApplicant\u001b[49m\u001b[43m(\u001b[49m\u001b[43mname\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mEmma Jones\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mscores\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m{\u001b[49m\u001b[43m}\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mnotes\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m, Applicant(name\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mMichael Davis\u001b[39m\u001b[38;5;124m\"\u001b[39m, scores\u001b[38;5;241m=\u001b[39m{}, notes\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m\"\u001b[39m)}):\u001b[38;5;241m3\u001b[39m,\n\u001b[0;32m 27\u001b[0m \u001b[38;5;28mfrozenset\u001b[39m({Applicant(name\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mEmma Jones\u001b[39m\u001b[38;5;124m\"\u001b[39m, scores\u001b[38;5;241m=\u001b[39m{}, notes\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m\"\u001b[39m), Applicant(name\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mPatrick Campbell\u001b[39m\u001b[38;5;124m\"\u001b[39m, scores\u001b[38;5;241m=\u001b[39m{}, notes\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m\"\u001b[39m)}):\u001b[38;5;241m4\u001b[39m,\n\u001b[0;32m 28\u001b[0m \u001b[38;5;28mfrozenset\u001b[39m({Applicant(name\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mSam Harrington\u001b[39m\u001b[38;5;124m\"\u001b[39m, scores\u001b[38;5;241m=\u001b[39m{}, notes\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m\"\u001b[39m), Applicant(name\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mEmma Jones\u001b[39m\u001b[38;5;124m\"\u001b[39m, scores\u001b[38;5;241m=\u001b[39m{}, notes\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m\"\u001b[39m)}):\u001b[38;5;241m5\u001b[39m,\n\u001b[0;32m 29\u001b[0m \u001b[38;5;28mfrozenset\u001b[39m({Applicant(name\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mEmma Jones\u001b[39m\u001b[38;5;124m\"\u001b[39m, scores\u001b[38;5;241m=\u001b[39m{}, notes\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m\"\u001b[39m), Applicant(name\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mSarah Thompson\u001b[39m\u001b[38;5;124m\"\u001b[39m, scores\u001b[38;5;241m=\u001b[39m{}, notes\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m\"\u001b[39m)}):\u001b[38;5;241m6\u001b[39m}\n\u001b[0;32m 31\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m pair \u001b[38;5;129;01min\u001b[39;00m unique_pairs:\n\u001b[0;32m 32\u001b[0m \u001b[38;5;28mprint\u001b[39m(pair) \u001b[38;5;129;01min\u001b[39;00m pair \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;129;01min\u001b[39;00m result\n", + "\u001b[1;31mTypeError\u001b[0m: Applicant.__init__() missing 8 required positional arguments: 'cv', 'email', 'phone', 'postcode', 'country_region', 'right_to_work', 'visa_requirement', and 'application_text'" + ] + } + ], + "source": [ + "from itertools import combinations\n", + "import shortlister.tournament as t\n", + "import shortlister.model as mdl\n", + "from shortlister.model import Applicant\n", + "from pathlib import Path\n", + "\n", + "mylist = [1,2,3,4,5,6,7]\n", + "RESULT = {(1,3):1}\n", + "\n", + "#def get_pair(object_list):\n", + "# \"\"\"get every possible item pair in the list\"\"\"\n", + "# unique_pairs = list(combinations(object_list, 2))\n", + "# pairs_to_compare =[pair for pair in unique_pairs if pair not in RESULT]\n", + "#\n", + "# # then check against compared pairs in RESULTS\n", + "# # return not compared pairs\n", + "# return pairs_to_compare\n", + "\n", + "\n", + "shortlist, message = mdl.load_shortlist(Path(\"test-role_0001\"))\n", + "unique_pairs = [frozenset(pair) for pair in combinations(shortlist.applicants, 2)]\n", + "for pair in unique_pairs:\n", + " if pair in result \n", + " print(pair)\n", + "\n", + "\n", + "result = {frozenset({Applicant(name=\"Emma Jones\", scores={}, notes=\"\"), Applicant(name=\"Michael Davis\", scores={}, notes=\"\")}):3,\n", + "frozenset({Applicant(name=\"Emma Jones\", scores={}, notes=\"\"), Applicant(name=\"Patrick Campbell\", scores={}, notes=\"\")}):4,\n", + "frozenset({Applicant(name=\"Sam Harrington\", scores={}, notes=\"\"), Applicant(name=\"Emma Jones\", scores={}, notes=\"\")}):5,\n", + "frozenset({Applicant(name=\"Emma Jones\", scores={}, notes=\"\"), Applicant(name=\"Sarah Thompson\", scores={}, notes=\"\")}):6}\n", + "\n", + "for pair in unique_pairs:\n", + " print(pair) in pair not in result" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "frozenset({'bob', 'martin'})\n", + "frozenset({'anna', 'bob'})\n", + "frozenset({'bob', 'ivan'})\n", + "frozenset({'anna', 'martin'})\n", + "frozenset({'ivan', 'martin'})\n", + "frozenset({'anna', 'ivan'})\n" + ] + }, + { + "data": { + "text/plain": [ + "[frozenset({'bob', 'martin'}),\n", + " frozenset({'bob', 'ivan'}),\n", + " frozenset({'ivan', 'martin'})]" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from itertools import combinations\n", + "test_list = [\"bob\",\"martin\",\"anna\",\"ivan\"]\n", + "#test_result = {frozenset((\"bob\",\"anna\")):2,\n", + "# frozenset((\"ivan\",\"anna\")):3}\n", + "test_result = {}\n", + "unique_pairs = [frozenset(pair) for pair in combinations(test_list, 2)]\n", + "pairs_to_compare = [pair for pair in unique_pairs if pair not in test_result]\n", + "\n", + "for pair in pairs_to_compare:\n", + " if \"anna\" in pair:\n", + " print(pair)\n", + " test_result[pair] = \"a\"\n", + "\n", + "new_unique_pairs = [frozenset(pair) for pair in combinations(test_list, 2)]\n", + "new_pair = [pair for pair in new_unique_pairs if pair not in test_result]\n", + "\n", + "test_result\n", + "new_pair\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "score = {1:4,\n", + " 2:8,\n", + " 3:2,\n", + " 4:9}\n", + "list = [1,2,3,4]\n", + "list.sort(key=lambda x : score[x],reverse=True) #highest score first \n", + "ranked = sorted(list,key=lambda item:score[item],reverse=True)\n", + "\n", + "ranked\n", + "\n", + "\n", + "dict1 = {frozenset({\"y\",\"t\",\"j\"}):2}\n", + "frozenset({\"t\",\"y\",\"j\"}) in dict1" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "sample_result = {\n", + " (\"apple\", \"banana\"): \"apple\",\n", + " (\"car\", \"bike\"): \"car\",\n", + " (\"cat\", \"dog\"): \"dog\",\n", + " (\"sun\", \"moon\"): \"moon\",\n", + " (\"table\", \"chair\"): \"chair\",\n", + " (\"tree\", \"river\"): \"tree\",\n", + " (\"mountain\", \"book\"): \"book\",\n", + " (\"pen\", \"phone\"): \"pen\",\n", + " (\"laptop\", \"bottle\"): \"bottle\",\n", + " (\"window\", \"door\"): \"door\",\n", + " (\"apple\", \"car\"): \"apple\",\n", + " (\"banana\", \"dog\"): \"dog\",\n", + " (\"bike\", \"tree\"): \"bike\",\n", + " (\"cat\", \"moon\"): \"moon\",\n", + " (\"sun\", \"mountain\"): \"sun\",\n", + " (\"chair\", \"river\"): \"river\",\n", + " (\"table\", \"laptop\"): \"laptop\",\n", + " (\"bottle\", \"window\"): \"bottle\",\n", + " (\"book\", \"door\"): \"book\",\n", + " (\"pen\", \"phone\"): \"phone\",\n", + "}\n", + "\n", + "mylist = [\n", + " \"apple\", \"banana\", \"bike\", \"bottle\", \"book\", \"car\", \"cat\", \"chair\", \n", + " \"door\", \"dog\", \"laptop\", \"moon\", \"mountain\", \"pen\", \"phone\", \"river\", \n", + " \"sun\", \"table\", \"tree\", \"window\"\n", + "]\n", + "\n", + "wins = {}\n", + "for object in mylist:\n", + " #award 1 point for every win\n", + " outcome = list(sample_result.values())\n", + " score = outcome.count(object)\n", + " wins[object] = score\n", + "\n", + "ranked = sorted(mylist, key=lambda item: wins[item], reverse=True)\n", + "print(ranked)\n", + "\n", + "key = frozenset(combinations(mylist, 2))\n", + "key" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Sorting\n", + "\n", + "# \n", + "# ranking()\n", + "\n", + "combining current method and result from bubble sort\n", + "\n", + "iterate through the list:\n", + " check the item with the most \n", + "\n", + "\n", + "current method flaws: when there is ties, rankings might be not well sorted\n", + " " + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "ename": "ValueError", + "evalue": "Cycle detected! Sorting is not possible.", + "output_type": "error", + "traceback": [ + "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[1;31mValueError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[1;32mIn[3], line 44\u001b[0m\n\u001b[0;32m 38\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m sorted_list\n\u001b[0;32m 40\u001b[0m \u001b[38;5;66;03m# use this to sort based on results from bubble sort\u001b[39;00m\n\u001b[0;32m 41\u001b[0m \n\u001b[0;32m 42\u001b[0m \n\u001b[0;32m 43\u001b[0m \u001b[38;5;66;03m# Example usage\u001b[39;00m\n\u001b[1;32m---> 44\u001b[0m sorted_items \u001b[38;5;241m=\u001b[39m \u001b[43morder_items\u001b[49m\u001b[43m(\u001b[49m\u001b[43mrelationships\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mitems\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 45\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mSorted items:\u001b[39m\u001b[38;5;124m\"\u001b[39m, sorted_items)\n", + "Cell \u001b[1;32mIn[3], line 36\u001b[0m, in \u001b[0;36morder_items\u001b[1;34m(relationships, items)\u001b[0m\n\u001b[0;32m 34\u001b[0m \u001b[38;5;66;03m# Check for cycles\u001b[39;00m\n\u001b[0;32m 35\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mlen\u001b[39m(sorted_list) \u001b[38;5;241m!=\u001b[39m \u001b[38;5;28mlen\u001b[39m(items):\n\u001b[1;32m---> 36\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mCycle detected! Sorting is not possible.\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[0;32m 38\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m sorted_list\n", + "\u001b[1;31mValueError\u001b[0m: Cycle detected! Sorting is not possible." + ] + } + ], + "source": [ + "from collections import defaultdict, deque\n", + "\n", + "\n", + "relationships = {\n", + " ('A', 'B'): 'B', # A wins over B\n", + " ('B', 'C'): 'C', # B wins over C\n", + " ('A', 'C'): 'A' # A wins over C\n", + "}\n", + "\n", + "items = ['A', 'B', 'C']\n", + "\n", + "def order_items(relationships, items):\n", + " # Build the graph\n", + " graph = defaultdict(list)\n", + " in_degree = {item: 0 for item in items}\n", + "\n", + " for (obj1, obj2), winner in relationships.items():\n", + " loser = obj1 if winner == obj2 else obj2\n", + " graph[winner].append(loser)\n", + " in_degree[loser] += 1\n", + "\n", + " # Topological sort\n", + " queue = deque([item for item in items if in_degree[item] == 0])\n", + " sorted_list = []\n", + "\n", + " while queue:\n", + " current = queue.popleft()\n", + " sorted_list.append(current)\n", + " for neighbor in graph[current]:\n", + " in_degree[neighbor] -= 1\n", + " if in_degree[neighbor] == 0:\n", + " queue.append(neighbor)\n", + "\n", + " # Check for cycles\n", + " if len(sorted_list) != len(items):\n", + " raise ValueError(\"Cycle detected! Sorting is not possible.\")\n", + "\n", + " return sorted_list\n", + "\n", + "# use this to sort based on results from bubble sort\n", + "\n", + "\n", + "# Example usage\n", + "sorted_items = order_items(relationships, items)\n", + "print(\"Sorted items:\", sorted_items)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{1: 'one', 2: 'two'}\n" + ] + }, + { + "data": { + "text/plain": [ + "(3, 'three')" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dic = {1: \"one\", 2: \"two\", 3: \"three\"}\n", + "removed = dic.popitem()\n", + "print(dic)\n", + "removed" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def comparison(list_to_rank, result):\n", + " \"\"\"Starts the comparison process.(To be changed)\"\"\"\n", + " pairs = get_pair(\n", + " list_to_rank, result\n", + " ) # returns a list of pairs that can be used to compare\n", + " for pair in pairs:\n", + " print(f\"{pair[0].name}[l] : {pair[1].name}[r]\")\n", + " print(f\"{[(criterion.name,score) for criterion,score in pair[0].scores]} {[(criterion.name,score) for criterion,score in pair[1].scores]}\")\n", + " print(f\"{pair[0].notes} {pair[1].notes}\")\n", + "\n", + " choice = readkey()\n", + " if choice == \"r\" or \"l\":\n", + " winner = choose(key,pair)\n", + " if choice == \"u\":\n", + " # if choice is \"u\"(undo), choose function removes the last item from dic and returns it as a pair to be added back to the queue\n", + " removed_pair = choose(key)\n", + " pairs.append(removed_pair)\n", + " save_results(frozenset(pair), winner, result)\n", + "\n", + "def choose(key,candidates: tuple,result):\n", + " \"\"\"Get user choice of which object out of the pair they prefer.\"\"\"\n", + " while True:\n", + " try:\n", + " if key == \"r\":\n", + " winner = candidates[1]\n", + " elif key == \"l\":\n", + " winner = candidates[0]\n", + " elif key == \"u\":\n", + " candidates = result.popitem()\n", + " continue\n", + " return winner\n", + " except Exception as e:\n", + " print(f\"{e}:selection must be r or l\")\n", + "\n", + "\n", + "\n", + "#pairs = list if pair\n", + "#iterating thru the pair\n", + "# l or r save the pair to dic and continue the loop until last\n", + "# u " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Datetime" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'20250113104356'" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import datetime\n", + "\n", + "datetime.datetime.now().strftime(\"%Y%m%d%H%M\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.5" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} From ed7a5e14d256ddb9ad60a8b3e9db3b9726a39728 Mon Sep 17 00:00:00 2001 From: shiying Date: Tue, 21 Jan 2025 17:26:50 +0000 Subject: [PATCH 09/11] Save result on "q" --- src/shortlister/tournament.py | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/shortlister/tournament.py b/src/shortlister/tournament.py index 6b96cae..398dfa4 100644 --- a/src/shortlister/tournament.py +++ b/src/shortlister/tournament.py @@ -13,13 +13,7 @@ def comparison(list_to_rank, result: Dict = None): print("TOURNAMENT COMPARISON") print() - # start with an empty dict if we don't have existing results - if result is None: - result = {} - - # Queue is a list of pairs that have not been compared yet - queue = get_pair(list_to_rank, result) - + queue = get_pair(list_to_rank,result) # Loop while there are still pairs to be compared in the queue while len(queue) > 0: # Remove the first pair in the queue and start the comparison @@ -42,7 +36,7 @@ def comparison(list_to_rank, result: Dict = None): if choice == "q": print("EXITING TOURNAMENT COMPARISON") print() - return + return result # if undo last choice elif choice == "u": # take the key and requeue the pair @@ -109,11 +103,10 @@ def save_rank(match_result, file: Path): def get_existing_result(path: Path): - # Checks if there is existing result data + """Checks if there is existing result data, or create a empty dict when there is no past results""" if path.exists(): with open(path, "rb") as pickle_file: result = pickle.load(pickle_file) - # If not, start a fresh comparison record else: result = {} return result From 154940c684aa45f8f821432439366236e4f75368 Mon Sep 17 00:00:00 2001 From: shiying Date: Tue, 21 Jan 2025 17:53:19 +0000 Subject: [PATCH 10/11] Only show rank when every pair is compared --- src/shortlister/controller.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/shortlister/controller.py b/src/shortlister/controller.py index 10e201f..e80b88b 100644 --- a/src/shortlister/controller.py +++ b/src/shortlister/controller.py @@ -377,13 +377,14 @@ def export_applicants_excel(self, k=None): print() def rank_selected_applicants(self, k=None): - result = tournament.get_existing_result( - Path(tournament.COMPARISON_RESULT_FILE_NAME) - ) - result = tournament.comparison(self.ctx.applicants, result) + ranked_pickle_path = self.path/Path(tournament.COMPARISON_RESULT_FILE_NAME) + result = tournament.get_existing_result(ranked_pickle_path) + new_result = tournament.comparison(self.ctx.applicants, result) + + tournament.save_rank(new_result,ranked_pickle_path) # Condition to avoid exception (caused by empty result when quitting comparison with "q") - if result: + if not tournament.get_pair(self.ctx.applicants,new_result): ranked_list = tournament.rank(self.ctx.applicants, result) print("RESULT:", [applicant.name for applicant in ranked_list]) print() From fa7a1b51be5a3cf21bf1f606c100bfc8c98346da Mon Sep 17 00:00:00 2001 From: shiying Date: Tue, 21 Jan 2025 18:51:53 +0000 Subject: [PATCH 11/11] Unfreeze applicant class --- src/shortlister/model.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/shortlister/model.py b/src/shortlister/model.py index 2a96e10..bbd8ff9 100644 --- a/src/shortlister/model.py +++ b/src/shortlister/model.py @@ -26,11 +26,11 @@ def __repr__(self) -> str: return self.name -@dataclass(frozen=True) +@dataclass class Applicant: """A property of Shortlist - contained within the attribute applicants(list of Applicant objects).""" - name: str = field(compare=True) + name: str cv: Path email: str phone: str