Skip to content
This repository has been archived by the owner on Jun 18, 2024. It is now read-only.

Commit

Permalink
Fix PEP8 and pylint issues.
Browse files Browse the repository at this point in the history
  • Loading branch information
itsjeyd committed Nov 2, 2015
1 parent a838256 commit 273232b
Show file tree
Hide file tree
Showing 3 changed files with 186 additions and 33 deletions.
5 changes: 5 additions & 0 deletions vectordraw/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,6 @@
"""
Top-level package for Vector Drawing XBlock.
See vectordraw.vectordraw for more information.
"""
from .vectordraw import VectorDrawXBlock
154 changes: 137 additions & 17 deletions vectordraw/grader.py
Original file line number Diff line number Diff line change
@@ -1,111 +1,187 @@
"""
This module contains grading logic for Vector Drawing exercises.
"""

# pylint: disable=invalid-name

import inspect
import json
import logging
import math


log = logging.getLogger(__name__)
log = logging.getLogger(__name__) # pylint: disable=invalid-name


## Built-in check functions
# Built-in check functions

def _errmsg(default_message, check, vectors):
"""
Return error message for `check` targeting a vector from `vectors`.
If `check` does not define a custom error message, fall back on `default_message`.
"""
template = check.get('errmsg', default_message)
vec = vectors[check['vector']]
return template.format(name=vec.name,
tail_x=vec.tail.x,
tail_y=vec.tail.y,
tip_x=vec.tip.x,
tip_y=vec.tip.y,
length=vec.length,
angle=vec.angle)
return template.format(
name=vec.name,
tail_x=vec.tail.x,
tail_y=vec.tail.y,
tip_x=vec.tip.x,
tip_y=vec.tip.y,
length=vec.length,
angle=vec.angle
)


def _errmsg_point(default_message, check, point):
"""
Return error message for `check` targeting `point`.
If `check` does not define a custom error message, fall back on `default_message`.
"""
template = check.get('errmsg', default_message)
return template.format(name=check['point'], x=point.x, y=point.y)


def check_presence(check, vectors):
"""
Check if `vectors` contains vector targeted by `check`.
"""
if check['vector'] not in vectors:
errmsg = check.get('errmsg', 'You need to use the {name} vector.')
return errmsg.format(name=check['vector'])


def check_tail(check, vectors):
"""
Check if tail of vector targeted by `check` is in correct position.
"""
vec = vectors[check['vector']]
tolerance = check.get('tolerance', 1.0)
expected = check['expected']
dist = math.hypot(expected[0] - vec.tail.x, expected[1] - vec.tail.y)
if dist > tolerance:
return _errmsg('Vector {name} does not start at correct point.', check, vectors)


def check_tip(check, vectors):
"""
Check if tip of vector targeted by `check` is in correct position.
"""
vec = vectors[check['vector']]
tolerance = check.get('tolerance', 1.0)
expected = check['expected']
dist = math.hypot(expected[0] - vec.tip.x, expected[1] - vec.tip.y)
if dist > tolerance:
return _errmsg('Vector {name} does not end at correct point.', check, vectors)


def _check_coordinate(check, coord):
"""
Check `coord` against expected value.
"""
tolerance = check.get('tolerance', 1.0)
expected = check['expected']
return abs(expected - coord) > tolerance


def check_tail_x(check, vectors):
"""
Check if x position of tail of vector targeted by `check` is correct.
"""
vec = vectors[check['vector']]
if _check_coordinate(check, vec.tail.x):
return _errmsg('Vector {name} does not start at correct point.', check, vectors)


def check_tail_y(check, vectors):
"""
Check if y position of tail of vector targeted by `check` is correct.
"""
vec = vectors[check['vector']]
if _check_coordinate(check, vec.tail.y):
return _errmsg('Vector {name} does not start at correct point.', check, vectors)


def check_tip_x(check, vectors):
"""
Check if x position of tip of vector targeted by `check` is correct.
"""
vec = vectors[check['vector']]
if _check_coordinate(check, vec.tip.x):
return _errmsg('Vector {name} does not end at correct point.', check, vectors)


def check_tip_y(check, vectors):
"""
Check if y position of tip of vector targeted by `check` is correct.
"""
vec = vectors[check['vector']]
if _check_coordinate(check, vec.tip.y):
return _errmsg('Vector {name} does not end at correct point.', check, vectors)


def _coord_delta(expected, actual):
"""
Return distance between `expected` and `actual` coordinates.
"""
if expected == '_':
return 0
else:
return expected - actual


def _coords_within_tolerance(vec, expected, tolerance):
"""
Check if distance between coordinates of `vec` and `expected` coordinates is within `tolerance`.
"""
for expected_coords, vec_coords in ([expected[0], vec.tail], [expected[1], vec.tip]):
delta_x = _coord_delta(expected_coords[0], vec_coords.x)
delta_y = _coord_delta(expected_coords[1], vec_coords.y)
if math.hypot(delta_x, delta_y) > tolerance:
return False
return True


def check_coords(check, vectors):
"""
Check if coordinates of vector targeted by `check` are in correct position.
"""
vec = vectors[check['vector']]
expected = check['expected']
tolerance = check.get('tolerance', 1.0)
if not _coords_within_tolerance(vec, expected, tolerance):
return _errmsg('Vector {name} coordinates are not correct.', check, vectors)


def check_segment_coords(check, vectors):
"""
Check if coordinates of segment targeted by `check` are in correct position.
"""
vec = vectors[check['vector']]
expected = check['expected']
tolerance = check.get('tolerance', 1.0)
if not (_coords_within_tolerance(vec, expected, tolerance) or
_coords_within_tolerance(vec.opposite(), expected, tolerance)):
return _errmsg('Segment {name} coordinates are not correct.', check, vectors)


def check_length(check, vectors):
"""
Check if length of vector targeted by `check` is correct.
"""
vec = vectors[check['vector']]
tolerance = check.get('tolerance', 1.0)
if abs(vec.length - check['expected']) > tolerance:
return _errmsg('The length of {name} is incorrect. Your length: {length:.1f}', check, vectors)
return _errmsg(
'The length of {name} is incorrect. Your length: {length:.1f}', check, vectors
)


def _angle_within_tolerance(vec, expected, tolerance):
"""
Check if difference between angle of `vec` and `expected` angle is within `tolerance`.
"""
# Calculate angle between vec and identity vector with expected angle
# using the formula:
# angle = acos((A . B) / len(A)*len(B))
Expand All @@ -115,14 +191,22 @@ def _angle_within_tolerance(vec, expected, tolerance):
angle = math.degrees(math.acos(dot_product / vec.length))
return abs(angle) <= tolerance


def check_angle(check, vectors):
"""
Check if angle of vector targeted by `check` is correct.
"""
vec = vectors[check['vector']]
tolerance = check.get('tolerance', 2.0)
expected = math.radians(check['expected'])
if not _angle_within_tolerance(vec, expected, tolerance):
return _errmsg('The angle of {name} is incorrect. Your angle: {angle:.1f}', check, vectors)


def check_segment_angle(check, vectors):
"""
Check if angle of segment targeted by `check` is correct.
"""
# Segments are not directed, so we must check the angle between the segment and
# the vector that represents it, as well as its opposite vector.
vec = vectors[check['vector']]
Expand All @@ -132,38 +216,56 @@ def check_segment_angle(check, vectors):
_angle_within_tolerance(vec.opposite(), expected, tolerance)):
return _errmsg('The angle of {name} is incorrect. Your angle: {angle:.1f}', check, vectors)


def _dist_line_point(line, point):
# Return the distance between the given line and point. The line is passed in as a Vector
# instance, the point as a Point instance.
"""
Return distance between `line` and `point`.
The line is passed in as a Vector instance, the point as a Point instance.
"""
direction_x = line.tip.x - line.tail.x
direction_y = line.tip.y - line.tail.y
determinant = (point.x - line.tail.x) * direction_y - (point.y - line.tail.y) * direction_x
return abs(determinant) / math.hypot(direction_x, direction_y)


def check_points_on_line(check, vectors):
"""
Check if line targeted by `check` passes through correct points.
"""
line = vectors[check['vector']]
tolerance = check.get('tolerance', 1.0)
points = check.get('expected')
for point in points:
point = Point(point[0], point[1])
if _dist_line_point(line, point) > tolerance:
return _errmsg('The line {name} does not pass through the correct points.', check, vectors)
return _errmsg(
'The line {name} does not pass through the correct points.', check, vectors
)


def check_point_coords(check, points):
"""
Check if coordinates of point targeted by `check` are correct.
"""
point = points[check['point']]
tolerance = check.get('tolerance', 1.0)
expected = check.get('expected')
dist = math.hypot(expected[0] - point.x, expected[1] - point.y)
if dist > tolerance:
return _errmsg_point('Point {name} is not at the correct location.', check, point)


class Point(object):
""" Represents a single point on the vector drawing board. """
def __init__(self, x, y):
self.x = x
self.y = y


class Vector(object):
def __init__(self, name, x1, y1, x2, y2):
""" Represents a single vector on the vector drawing board. """
def __init__(self, name, x1, y1, x2, y2): # pylint: disable=too-many-arguments
self.name = name
self.tail = Point(x1, y1)
self.tip = Point(x2, y2)
Expand All @@ -174,9 +276,16 @@ def __init__(self, name, x1, y1, x2, y2):
self.angle = angle

def opposite(self):
"""
Return new vector with tip and tail swapped.
"""
return Vector(self.name, self.tip.x, self.tip.y, self.tail.x, self.tail.y)


class Grader(object):
"""
Implements grading logic for student answers to Vector Drawing exercises.
"""
check_registry = {
'presence': check_presence,
'tail': check_tail,
Expand All @@ -200,6 +309,11 @@ def __init__(self, success_message='Test passed', custom_checks=None):
self.check_registry.update(custom_checks)

def grade(self, answer):
"""
Check correctness of `answer` by running checks defined for it one by one.
Short-circuit as soon as a single check fails.
"""
check_data = dict(
vectors=self._get_vectors(answer),
points=self._get_points(answer),
Expand All @@ -213,13 +327,19 @@ def grade(self, answer):
return {'ok': False, 'msg': result}
return {'ok': True, 'msg': self.success_message}

def _get_vectors(self, answer):
def _get_vectors(self, answer): # pylint: disable=no-self-use
"""
Turn vector info in `answer` into a dictionary of Vector objects.
"""
vectors = {}
for name, props in answer['vectors'].iteritems():
tail = props['tail']
tip = props['tip']
vectors[name] = Vector(name, tail[0], tail[1], tip[0], tip[1])
return vectors

def _get_points(self, answer):
def _get_points(self, answer): # pylint: disable=no-self-use
"""
Turn point info in `answer` into a dictionary of Point objects.
"""
return {name: Point(*coords) for name, coords in answer['points'].iteritems()}
Loading

0 comments on commit 273232b

Please sign in to comment.