from collections import MutableMapping, namedtuple

from .model import ModuleScope, ModelType


class KeyValueStore(object):
    """The abstract interface for Key Value Stores."""

    # Keys are structured to retain information about the scope of the data.
    # Stores can use this information however they like to store and retrieve
    # data.
    Key = namedtuple("Key", "scope, student_id, module_scope_id, field_name")

    def get(self, key):
        pass

    def set(self, key, value):
        pass

    def delete(self, key):
        pass


class DbModel(MutableMapping):
    """A dictionary-like interface to the fields on a module."""

    def __init__(self, kvs, module_cls, student_id, usage):
        self._kvs = kvs
        self._student_id = student_id
        self._module_cls = module_cls
        self._usage = usage

    def __repr__(self):
        return "<{0.__class__.__name__} {0._module_cls!r}>".format(self)

    def _getfield(self, name):
        # First, get the field from the class, if defined
        module_field = getattr(self._module_cls, name, None)
        if module_field is not None and isinstance(module_field, ModelType):
            return module_field

        # If the class doesn't have the field, and it also
        # doesn't have any namespaces, then the the name isn't a field
        # so KeyError
        if not hasattr(self._module_cls, 'namespaces'):
            return KeyError(name)

        # Resolve the field name in the first namespace where it's
        # available
        for namespace_name in self._module_cls.namespaces:
            namespace = getattr(self._module_cls, namespace_name)
            namespace_field = getattr(type(namespace), name, None)
            if namespace_field is not None and isinstance(namespace_field, ModelType):
                return namespace_field

        # Not in the class or in any of the namespaces, so name
        # really doesn't name a field
        raise KeyError(name)

    def _key(self, name):
        field = self._getfield(name)
        module = field.scope.module

        if module == ModuleScope.ALL:
            module_id = None
        elif module == ModuleScope.USAGE:
            module_id = self._usage.id
        elif module == ModuleScope.DEFINITION:
            module_id = self._usage.def_id
        elif module == ModuleScope.TYPE:
            module_id = self._module_cls.__name__

        if field.scope.student:
            student_id = self._student_id
        else:
            student_id = None

        key = KeyValueStore.Key(
            scope=field.scope,
            student_id=student_id,
            module_scope_id=module_id,
            field_name=name
            )
        return key

    def __getitem__(self, name):
        return self._kvs.get(self._key(name))

    def __setitem__(self, name, value):
        self._kvs.set(self._key(name), value)

    def __delitem__(self, name):
        self._kvs.delete(self._key(name))

    def __iter__(self):
        return iter(self.keys())

    def __len__(self):
        return len(self.keys())

    def keys(self):
        fields = [field.name for field in self._module_cls.fields]
        for namespace_name in self._module_cls.namespaces:
            fields.extend(field.name for field in getattr(self._module_cls, namespace_name).fields)
        return fields

@@ -2029,24 +2029,133 @@
 field(name)%0A
+        if field.scope is None:%0A            module_id = None%0A            student_id = None%0A        else:%0A    
         modu
@@ -2179,32 +2179,36 @@
 module%0A%0A        
+    
 if module == Mod
@@ -2225,32 +2225,36 @@
 LL:%0A            
+    
 module_id = None
@@ -2254,32 +2254,36 @@
  = None%0A        
+ 
+   
 elif module == M
@@ -2304,32 +2304,36 @@
 GE:%0A            
+    
 module_id = self
@@ -2343,32 +2343,36 @@
 sage.id%0A        
+   
+ 
 elif module == M
@@ -2394,16 +2394,20 @@
 NITION:%0A
+    
         
@@ -2441,32 +2441,36 @@
 .def_id%0A        
+    
 elif module == M
@@ -2490,32 +2490,36 @@
 PE:%0A            
+ 
+   
 module_id = self
@@ -2541,32 +2541,36 @@
 name__%0A%0A        
+    
 if field.scope.s
@@ -2581,32 +2581,36 @@
 nt:%0A            
+   
+ 
 student_id = sel
@@ -2623,38 +2623,46 @@
 dent_id%0A        
+    
 else:%0A
+    
             stud

Allow field scope of None