ygrader 2.6.12__tar.gz → 2.6.14__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {ygrader-2.6.12/ygrader.egg-info → ygrader-2.6.14}/PKG-INFO +1 -1
- {ygrader-2.6.12 → ygrader-2.6.14}/setup.py +1 -1
- {ygrader-2.6.12 → ygrader-2.6.14}/ygrader/grader.py +20 -5
- {ygrader-2.6.12 → ygrader-2.6.14}/ygrader/utils.py +46 -0
- {ygrader-2.6.12 → ygrader-2.6.14/ygrader.egg-info}/PKG-INFO +1 -1
- {ygrader-2.6.12 → ygrader-2.6.14}/LICENSE +0 -0
- {ygrader-2.6.12 → ygrader-2.6.14}/setup.cfg +0 -0
- {ygrader-2.6.12 → ygrader-2.6.14}/test/test_interactive.py +0 -0
- {ygrader-2.6.12 → ygrader-2.6.14}/test/test_unittest.py +0 -0
- {ygrader-2.6.12 → ygrader-2.6.14}/ygrader/__init__.py +0 -0
- {ygrader-2.6.12 → ygrader-2.6.14}/ygrader/deductions.py +0 -0
- {ygrader-2.6.12 → ygrader-2.6.14}/ygrader/feedback.py +0 -0
- {ygrader-2.6.12 → ygrader-2.6.14}/ygrader/grades_csv.py +0 -0
- {ygrader-2.6.12 → ygrader-2.6.14}/ygrader/grading_item.py +0 -0
- {ygrader-2.6.12 → ygrader-2.6.14}/ygrader/grading_item_config.py +0 -0
- {ygrader-2.6.12 → ygrader-2.6.14}/ygrader/remote.py +0 -0
- {ygrader-2.6.12 → ygrader-2.6.14}/ygrader/score_input.py +0 -0
- {ygrader-2.6.12 → ygrader-2.6.14}/ygrader/send_ctrl_backtick.ahk +0 -0
- {ygrader-2.6.12 → ygrader-2.6.14}/ygrader/student_repos.py +0 -0
- {ygrader-2.6.12 → ygrader-2.6.14}/ygrader/upstream_merger.py +0 -0
- {ygrader-2.6.12 → ygrader-2.6.14}/ygrader.egg-info/SOURCES.txt +0 -0
- {ygrader-2.6.12 → ygrader-2.6.14}/ygrader.egg-info/dependency_links.txt +0 -0
- {ygrader-2.6.12 → ygrader-2.6.14}/ygrader.egg-info/requires.txt +0 -0
- {ygrader-2.6.12 → ygrader-2.6.14}/ygrader.egg-info/top_level.txt +0 -0
|
@@ -4,7 +4,7 @@ setup(
|
|
|
4
4
|
name="ygrader",
|
|
5
5
|
packages=["ygrader"],
|
|
6
6
|
package_data={"ygrader": ["*.ahk"]},
|
|
7
|
-
version="2.6.
|
|
7
|
+
version="2.6.14",
|
|
8
8
|
description="Grading scripts used in BYU's Electrical and Computer Engineering Department",
|
|
9
9
|
author="Jeff Goeders",
|
|
10
10
|
author_email="jeff.goeders@gmail.com",
|
|
@@ -537,8 +537,8 @@ class Grader:
|
|
|
537
537
|
continue
|
|
538
538
|
|
|
539
539
|
# Print name(s) of who we are grading
|
|
540
|
-
student_work_path = self.
|
|
541
|
-
first_names, last_names, net_ids
|
|
540
|
+
student_work_path = self._get_student_work_path(
|
|
541
|
+
row, first_names, last_names, net_ids
|
|
542
542
|
)
|
|
543
543
|
print_color(
|
|
544
544
|
TermColors.PURPLE,
|
|
@@ -567,7 +567,9 @@ class Grader:
|
|
|
567
567
|
callback_args["first_names"] = first_names
|
|
568
568
|
callback_args["last_names"] = last_names
|
|
569
569
|
callback_args["net_ids"] = net_ids
|
|
570
|
-
callback_args["output"] =
|
|
570
|
+
callback_args["output"] = (
|
|
571
|
+
sys.stdout
|
|
572
|
+
) # Default to stdout in sequential mode
|
|
571
573
|
if self.code_source == CodeSource.GITHUB:
|
|
572
574
|
callback_args["repo_url"] = row["github_url"]
|
|
573
575
|
callback_args["tag"] = self.github_tag
|
|
@@ -663,8 +665,8 @@ class Grader:
|
|
|
663
665
|
)
|
|
664
666
|
log_path = log_file.name
|
|
665
667
|
|
|
666
|
-
student_work_path = self.
|
|
667
|
-
first_names, last_names, net_ids
|
|
668
|
+
student_work_path = self._get_student_work_path(
|
|
669
|
+
row, first_names, last_names, net_ids
|
|
668
670
|
)
|
|
669
671
|
|
|
670
672
|
# Build student info dict to pass to helper
|
|
@@ -868,6 +870,19 @@ class Grader:
|
|
|
868
870
|
df["submitted_zip_path"] = df["submitted_zip_path"].fillna(value="")
|
|
869
871
|
return df
|
|
870
872
|
|
|
873
|
+
def _get_student_work_path(self, row, first_names, last_names, net_ids):
|
|
874
|
+
"""Get the work path for a student/group.
|
|
875
|
+
|
|
876
|
+
For GitHub submissions, the folder is named after the repository name.
|
|
877
|
+
For Learning Suite submissions, it uses the first student's name.
|
|
878
|
+
"""
|
|
879
|
+
if self.code_source == CodeSource.GITHUB:
|
|
880
|
+
# Use the repository name from the GitHub URL
|
|
881
|
+
repo_name = utils.github_url_to_repo_name(row["github_url"])
|
|
882
|
+
return self.work_path / repo_name
|
|
883
|
+
# Learning Suite: use first student's name
|
|
884
|
+
return self.work_path / utils.names_to_dir(first_names, last_names, net_ids)
|
|
885
|
+
|
|
871
886
|
def _group_students(self, df):
|
|
872
887
|
if self.code_source == CodeSource.GITHUB:
|
|
873
888
|
# For Github source, group name is simply github URL
|
|
@@ -8,6 +8,7 @@ import subprocess
|
|
|
8
8
|
import hashlib
|
|
9
9
|
import time
|
|
10
10
|
import os
|
|
11
|
+
import shlex
|
|
11
12
|
|
|
12
13
|
|
|
13
14
|
class TermColors:
|
|
@@ -85,6 +86,31 @@ def names_to_dir(first_names, last_names, net_ids):
|
|
|
85
86
|
)
|
|
86
87
|
|
|
87
88
|
|
|
89
|
+
def github_url_to_repo_name(url):
|
|
90
|
+
"""Extract the repository name from a GitHub URL.
|
|
91
|
+
|
|
92
|
+
Works with both SSH and HTTPS formats:
|
|
93
|
+
>>> github_url_to_repo_name("git@github.com:byu-ecen123-classroom/123-labs-team01.git")
|
|
94
|
+
'123-labs-team01'
|
|
95
|
+
>>> github_url_to_repo_name("https://github.com/byu-ecen123-classroom/123-labs-team01")
|
|
96
|
+
'123-labs-team01'
|
|
97
|
+
>>> github_url_to_repo_name("https://github.com/byu-ecen123-classroom/123-labs-team01.git")
|
|
98
|
+
'123-labs-team01'
|
|
99
|
+
"""
|
|
100
|
+
# Try SSH format: git@github.com:org/repo.git
|
|
101
|
+
match = re.search(r"git@github\.com:.*?/(.*?)(?:\.git)?$", url)
|
|
102
|
+
if match:
|
|
103
|
+
return match.group(1)
|
|
104
|
+
|
|
105
|
+
# Try HTTPS format: https://github.com/org/repo or https://github.com/org/repo.git
|
|
106
|
+
match = re.search(r"github\.com/.*?/(.*?)(?:\.git)?$", url)
|
|
107
|
+
if match:
|
|
108
|
+
return match.group(1)
|
|
109
|
+
|
|
110
|
+
# Fallback: return the URL as-is (shouldn't happen with valid GitHub URLs)
|
|
111
|
+
return url
|
|
112
|
+
|
|
113
|
+
|
|
88
114
|
def hash_file(file_path):
|
|
89
115
|
"""Returns a hash of a file"""
|
|
90
116
|
|
|
@@ -163,6 +189,26 @@ def is_wsl():
|
|
|
163
189
|
_FOCUS_WARNING_PRINTED = False
|
|
164
190
|
|
|
165
191
|
|
|
192
|
+
def opener(file_path, sleep_time=1.0):
|
|
193
|
+
"""Open a file using VS Code or a custom opener defined in the OPENER environment variable.
|
|
194
|
+
|
|
195
|
+
Parameters
|
|
196
|
+
----------
|
|
197
|
+
file_path: pathlib.Path or str
|
|
198
|
+
Path to the file to open
|
|
199
|
+
sleep_time: float, optional
|
|
200
|
+
Time in seconds to wait for the editor to open (default: 1.0)
|
|
201
|
+
"""
|
|
202
|
+
if "OPENER" not in os.environ:
|
|
203
|
+
open_file_in_vscode(file_path, sleep_time)
|
|
204
|
+
else:
|
|
205
|
+
subprocess.run(
|
|
206
|
+
f'{os.environ["OPENER"]} {shlex.quote(str(file_path))}',
|
|
207
|
+
shell=True,
|
|
208
|
+
check=False,
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
|
|
166
212
|
def open_file_in_vscode(file_path, sleep_time=1.0):
|
|
167
213
|
"""Open a file in VS Code and return focus to terminal.
|
|
168
214
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|