Commit 67b19d6d9484b0dce4183e406f7c4ce04119e5b8

Authored by Rohan Rangray
1 parent 2f1be78a16
Exists in master

Subclasses set to create a FlashcardMask class. Moved Mask validation logic from…

… model MaskField to the FlashcardMask class

Showing 4 changed files with 46 additions and 16 deletions Side-by-side Diff

flashcards/fields.py View file @ 67b19d6
1 1 from django.db import models
  2 +from validators import FlashcardMask, OverlapIntervalException
2 3  
3 4  
4 5 class MaskField(models.Field):
5 6  
6 7  
... ... @@ -37,16 +38,14 @@
37 38 return ','.join(['-'.join(map(str, i)) for i in value])
38 39  
39 40 def to_python(self, value):
40   - if value is None or isinstance(value, set):
  41 + if value is None:
41 42 return value
42   - return MaskField._parse_mask(value)
  43 + return sorted(list(FlashcardMask(value)))
43 44  
44 45 def get_prep_value(self, value):
45 46 if value is None:
46 47 return value
47   - if not isinstance(value, set) or not all([isinstance(interval, tuple) for interval in value]):
48   - raise ValueError("Invalid value for MaskField attribute")
49   - return self.__class__._parse_mask(sorted(value))
  48 + return sorted(list(FlashcardMask(value)))
50 49  
51 50 def get_prep_lookup(self, lookup_type, value):
52 51 raise TypeError("Lookup not supported for MaskField")
53 52  
... ... @@ -65,16 +64,10 @@
65 64  
66 65 @classmethod
67 66 def _varchar_parse_mask(cls, value):
68   - intervals = []
69   - ranges = value.split(',')
70   - for interval in ranges:
71   - _range = interval.split('-')
72   - if len(_range) != 2 or not all(map(unicode.isdigit, _range)):
73   - raise ValueError("Invalid range format.")
74   - intervals.append(tuple(_range))
75   - return set([tuple(i) for i in cls._parse_mask(sorted(intervals))])
  67 + mask = [tuple(map(int, i.split('-'))) for i in value.split(',')]
  68 + return FlashcardMask(mask)
76 69  
77 70 @classmethod
78 71 def _psql_parse_mask(cls, value):
79   - return set([tuple(i) for i in cls._parse_mask(sorted(value))])
  72 + return FlashcardMask(value)
flashcards/tests/test_models.py View file @ 67b19d6
... ... @@ -2,6 +2,7 @@
2 2  
3 3 from django.test import TestCase
4 4 from flashcards.models import User, Section, Flashcard
  5 +from flashcards.validators import OverlapIntervalException
5 6  
6 7  
7 8 class RegistrationTests(TestCase):
... ... @@ -68,6 +69,6 @@
68 69 previous=None,
69 70 mask={(10,34), (0, 14)})
70 71 self.fail()
71   - except ValueError:
  72 + except OverlapIntervalException:
72 73 self.assertTrue(True)
flashcards/validators.py View file @ 67b19d6
  1 +__author__ = 'rray'
  2 +
  3 +from collections import Iterable
  4 +
  5 +
  6 +class FlashcardMask(set):
  7 + def __init__(self, *args, **kwargs):
  8 + super(FlashcardMask, self).__init__(*args, **kwargs)
  9 + self._iterable_check()
  10 + self._interval_check()
  11 + self._overlap_check()
  12 +
  13 + def _iterable_check(self):
  14 + if not all([isinstance(i, Iterable) for i in self]):
  15 + raise TypeError("Interval not a valid iterable")
  16 +
  17 + def _interval_check(self):
  18 + if not all([len(i) == 2 for i in self]):
  19 + raise TypeError("Intervals must have exactly 2 elements, begin and end")
  20 +
  21 + def _overlap_check(self):
  22 + p_beg, p_end = -1, -1
  23 + for interval in sorted(self):
  24 + beg, end = map(int, interval)
  25 + if not (0 <= beg <= 255) or not (0 <= end <= 255) or not (beg <= end) or not (beg > p_end):
  26 + raise OverlapIntervalException((beg, end), "Invalid interval offsets in the mask")
  27 + p_beg, p_end = beg, end
  28 +
  29 +
  30 +class OverlapIntervalException(Exception):
  31 + def __init__(self, interval, reason):
  32 + self.interval = interval
  33 + self.reason = reason
  34 +
  35 + def __str__(self):
  36 + return repr(self.reason) + ': ' + repr(self.interval)
flashcards/views.py View file @ 67b19d6
... ... @@ -273,7 +273,7 @@
273 273 :return: A 204 response upon success.
274 274 """
275 275 user = request.user
276   - flashcard = Flashcard.objects.get(pk=pk)
  276 + flashcard = self.get_object()
277 277 user_card, created = UserFlashcard.objects.get_or_create(user=user, flashcard=flashcard)
278 278 user_card.save()
279 279 return Response(status=HTTP_204_NO_CONTENT)