Skip to content

Commit

Permalink
Feature: Global comments that do not apply to a specific task
Browse files Browse the repository at this point in the history
  • Loading branch information
lkuchenb committed May 19, 2020
1 parent fa8aef7 commit b92fc73
Show file tree
Hide file tree
Showing 6 changed files with 81 additions and 11 deletions.
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ student assignments.

You can install `assignment-tool` by running

pip install git+https://github.com/KohlbacherLab/assignment-tool.git@1.1.1
pip install git+https://github.com/KohlbacherLab/assignment-tool.git@1.2.0

## Usage

Expand Down Expand Up @@ -151,6 +151,10 @@ on the feedback PDF file in numerical order. If multiple comments are added for
the same subtask, the order in which they are specified in the Excel sheet is
maintained on the feedback PDF file.

Optionally, *global* comments can be specified by leaving the *Task* and
*Subtask* fields empty. Such comments can then be rendered separately from the
per-task comments.

### The *Summary* Sheet

<p align="center">
Expand Down
Binary file modified examples/ExampleSheet.xlsx
Binary file not shown.
31 changes: 27 additions & 4 deletions examples/template.tex
Original file line number Diff line number Diff line change
Expand Up @@ -8,41 +8,64 @@

\setlength\parindent{0mm}

% Command is executed before global comments and only if global comments are present
\newcommand\beforeGlobalComments{
\subsection*{General Remarks}
\begin{itemize}\setlength\itemsep{0mm}
}

% Command is executed after global comments and only if global comments are present
\newcommand\afterGlobalComments{
\end{itemize}
}

% Command is executed for every global comment. Arguments: Comment.
\newcommand\globalComment[1]{
\item #1
}

% Command is executed before per-task comments and only if per-task comments are present
\newcommand\beforeComments{
\begin{itemize}\setlength\itemsep{0mm}
}

% Command is executed after per-task comments and only if per-task comments are present
\newcommand\afterComments{
\end{itemize}
}

% Command is executed for every per-task comment. Arguments: Comment.
\newcommand\comment[1]{
\item #1
}

% Command is executed for every scored task, passing the task number as an argument
\newcommand\newtask[1]{
\subsection*{Exercise #1}
}

% Command is executed for every scored subtask. Arguments: sheet number, task number, subtask number, score for the subtask, maximum attainable score for the subtask
\newcommand\scoreTask[5]{% <sheet> <task> <subtask> <score> <total>
#2.#3\dotfill #4 von #5
#2.#3\dotfill #4 of #5

}

\begin{document}
\hrule
\smallskip

{\large Bewertung Übungsblatt §§sheetnr§§\hfill Gesamt: §§total§§ von §§maxtotal§§}\smallskip
{\large Scores for Exercise Sheet §§sheetnr§§\hfill Total: §§total§§ of §§maxtotal§§}\smallskip

{\large §§fullname§§}\smallskip

{Tutor: §§tutorname§§}
{Tutor: §§tutorname§§\hfill PHY9911: Practical String Theory}

\smallskip
\hrule
\vspace{0.2cm}

§§body§§
§§global§§

§§tasks§§

\end{document}
Binary file modified img/header.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,13 @@
from setuptools import setup, find_packages

requires = [
'pandas',
'pandas>=1.0.0',
'xlrd',
]

setup(
name = 'assignmenttool',
version = '1.1.1',
version = '1.2.0',
package_dir={'':'src'},
packages=find_packages('./src'),
author = 'Leon Kuchenbecker',
Expand Down
51 changes: 47 additions & 4 deletions src/assignmenttool/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,23 @@ def mail_feedback(config, participants, pdfs):

####################################################################################################

def read_scores(infile):
"""Reads the scores from the Excel sheet while being as relaxed about the
data type of the task / subtask columns as possible"""
scores = pd.read_excel(infile, sheet_name = 'Grading', dtype={
'Username' : str,
'Sheet' : 'Int64',
'Task' : 'Int64',
'Subtask' : 'Int64',
'Type' : str,
'Value' : object,
})

return scores

def process(config):
# Read Scores and comments
scores = pd.read_excel(config.infile, sheet_name = 'Grading')
scores = read_scores(config.infile)

# Read participants
participants = pd.read_excel(config.infile, sheet_name = 'Participants').set_index('Username')
Expand All @@ -114,20 +128,38 @@ def process(config):

# Build dictionaries
d = defaultdict(lambda : defaultdict( lambda : { 'score' : None, 'comments' : []} ) )
sheet_comments = defaultdict(lambda : [])


for _,row in scores.iterrows():
task = (row.Sheet, row.Task, row.Subtask)
record = d[row.Username][task]
if pd.isna(row.Sheet):
raise AToolError('Failed to parse provided Excel file, "Grading" sheet contains empty value for "Sheet" column.')
if row.Type.upper() == 'SCORE':
# Check if the row checks out
if pd.isna(row.Task) or pd.isna(row.Subtask) or pd.isna(row.Value):
raise AToolError(f'Failed to parse provided Excel file, "Grading" sheet contains empty value for "Task", "Subtask" or "Value" column for sheet {row.Sheet}.')
task = (row.Sheet, row.Task, row.Subtask)
record = d[row.Username][task]
# Check if a score occurs redundantly for the same task
if record['score'] is not None:
raise AToolError(f'Duplicate score for identical task found (User: {row.Username}, Sheet: {row.Sheet}, Task: {row.Task}, Subtask: {row.Subtask}')
# Obtain maximum attainable score
try:
record['max_score'] = max_scores[task]
except KeyError:
raise AToolError(f'Could not find maximum score for task {task}')
record['score'] = row.Value
elif row.Type.upper() == 'COMMENT':
record['comments'].append(row.Value)
# Check if this is a comment that applies to the entire sheet
if pd.isna(row.Task) and pd.isna(row.Subtask):
sheet_comments[row.Username].append(row.Value)
elif pd.isna(row.Task) or pd.isna(row.Subtask):
raise AToolError(f'Failed to parse provided Excel file, "Grading" sheet contains empty value for "Task" or "Subtask" but not for both.')
# ... or if it's a comment that applies to a specific task
else:
task = (row.Sheet, row.Task, row.Subtask)
record = d[row.Username][task]
record['comments'].append(row.Value)
else:
raise AToolError(f'Invalid value type "{row.Type}".')

Expand Down Expand Up @@ -166,8 +198,19 @@ def process(config):
body.append(f'\\comment{{{comment}}}')
body.append(r'\afterComments')

# Global comments
global_ = []
if user in sheet_comments:
global_.append('\\beforeGlobalComments')
global_comments = sheet_comments[user]
for comment in global_comments:
global_.append(f'\\globalComment{{{comment}}}')
global_.append('\\afterGlobalComments')

# Write out and compile tex
tex = tex.replace('§§global§§', '\n'.join(global_))
tex = tex.replace('§§body§§', '\n'.join(body))
tex = tex.replace('§§tasks§§', '\n'.join(body))
pdf = compileLaTeX(tex, config.pdflatex)

# Move output file in place
Expand Down

0 comments on commit b92fc73

Please sign in to comment.