from base import *\r
+import re\r
from tag import Tag\r
\r
import markdown\r
\r
\r
class NodeQuerySet(CachedQuerySet):\r
+ def obj_from_datadict(self, datadict):\r
+ cls = NodeMetaClass.types.get(datadict.get("node_type", ""), None)\r
+ if cls:\r
+ obj = cls()\r
+ obj.__dict__.update(datadict)\r
+ return obj\r
+ else:\r
+ return super(NodeQuerySet, self).obj_from_datadict(datadict)\r
+\r
def get(self, *args, **kwargs):\r
- node = super(NodeQuerySet, self).get(*args, **kwargs)\r
- cls = NodeMetaClass.types.get(node.node_type, None)\r
+ return super(NodeQuerySet, self).get(*args, **kwargs).leaf\r
\r
- if cls and (node.__class__ is not cls):\r
- return node.leaf\r
- return node\r
+ def filter_state(self, **kwargs):\r
+ apply_bool = lambda q, b: b and q or ~q\r
+ return self.filter(*[apply_bool(models.Q(state_string__contains="(%s)" % s), b) for s, b in kwargs.items()])\r
\r
\r
class NodeManager(CachedManager):\r
kwargs['node_type__in'] = [t.get_type() for t in types]\r
return self.get(*args, **kwargs)\r
\r
+ def filter_state(self, **kwargs):\r
+ return self.all().filter_state(**kwargs)\r
+\r
+\r
+class NodeStateDict(object):\r
+ def __init__(self, node):\r
+ self.__dict__['_node'] = node\r
+\r
+ def __getattr__(self, name):\r
+ if self.__dict__.get(name, None):\r
+ return self.__dict__[name]\r
+\r
+ try:\r
+ node = self.__dict__['_node']\r
+ action = NodeState.objects.get(node=node, state_type=name).action\r
+ self.__dict__[name] = action\r
+ return action\r
+ except:\r
+ return None\r
+\r
+ def __setattr__(self, name, value):\r
+ current = self.__getattr__(name)\r
+\r
+ if value:\r
+ if current:\r
+ current.action = value\r
+ current.save()\r
+ else:\r
+ node = self.__dict__['_node']\r
+ state = NodeState(node=node, action=value, state_type=name)\r
+ state.save()\r
+ self.__dict__[name] = value\r
+\r
+ if not "(%s)" % name in node.state_string:\r
+ node.state_string = "%s(%s)" % (node.state_string, name)\r
+ node.save()\r
+ else:\r
+ if current:\r
+ node = self.__dict__['_node']\r
+ node.state_string = "".join("(%s)" % s for s in re.findall('\w+', node.state_string) if s != name)\r
+ node.save()\r
+ current.node_state.delete()\r
+ del self.__dict__[name]\r
+\r
+\r
+class NodeStateQuery(object):\r
+ def __init__(self, node):\r
+ self.__dict__['_node'] = node\r
+\r
+ def __getattr__(self, name):\r
+ node = self.__dict__['_node']\r
+ return "(%s)" % name in node.state_string\r
+\r
+\r
\r
class Node(BaseModel, NodeContent):\r
__metaclass__ = NodeMetaClass\r
added_at = models.DateTimeField(default=datetime.datetime.now)\r
score = models.IntegerField(default=0)\r
\r
- deleted = models.ForeignKey('Action', null=True, unique=True, related_name="deleted_node")\r
- in_moderation = models.ForeignKey('Action', null=True, unique=True, related_name="moderated_node")\r
+ state_string = models.TextField(default='')\r
last_edited = models.ForeignKey('Action', null=True, unique=True, related_name="edited_node")\r
\r
last_activity_by = models.ForeignKey(User, null=True)\r
\r
extra_ref = models.ForeignKey('Node', null=True)\r
extra_count = models.IntegerField(default=0)\r
- extra_action = models.ForeignKey('Action', null=True, related_name="extra_node")\r
- \r
+\r
marked = models.BooleanField(default=False)\r
- wiki = models.BooleanField(default=False)\r
\r
comment_count = DenormalizedField("children", node_type="comment", canceled=False)\r
flag_count = DenormalizedField("flags")\r
\r
@classmethod\r
def cache_key(cls, pk):\r
- return '%s.node:%s' % (settings.APP_URL, pk)\r
+ return '%s:node:%s' % (settings.APP_URL, pk)\r
\r
@classmethod\r
def get_type(cls):\r
leaf.__dict__ = self.__dict__\r
return leaf\r
\r
+ @property\r
+ def nstate(self):\r
+ state = self.__dict__.get('_nstate', None)\r
+\r
+ if state is None:\r
+ state = NodeStateDict(self)\r
+ self._nstate = state\r
+\r
+ return state\r
+\r
+ @property\r
+ def nis(self):\r
+ nis = self.__dict__.get('_nis', None)\r
+\r
+ if nis is None:\r
+ nis = NodeStateQuery(self)\r
+ self._nis = nis\r
+\r
+ return nis\r
+\r
+ @property\r
+ def deleted(self):\r
+ return self.nis.deleted\r
+\r
@property \r
def absolute_parent(self):\r
if not self.abs_parent_id:\r
- return self.leaf\r
+ return self\r
\r
- return self.abs_parent.leaf\r
+ return self.abs_parent\r
\r
@property\r
def summary(self):\r
if not 'tagnames' in dirty:\r
return None\r
else:\r
- if dirty['tagnames']:\r
- old_tags = set(name for name in dirty['tagnames'].split(u' '))\r
+ if self._original_state['tagnames']:\r
+ old_tags = set(name for name in self._original_state['tagnames'].split(u' '))\r
else:\r
old_tags = set()\r
new_tags = set(name for name in self.tagnames.split(u' ') if name)\r
except:\r
tag = Tag.objects.create(name=name, created_by=self._last_active_user())\r
\r
- if not self.deleted:\r
+ if not self.nis.deleted:\r
tag.used_count = models.F('used_count') + 1\r
tag.save()\r
\r
- if not self.deleted:\r
+ if not self.nis.deleted:\r
for name in tag_changes['removed']:\r
try:\r
tag = Tag.objects.get(name=name)\r
tag.used_count = models.F('used_count') - 1\r
tag.save()\r
- if tag.used_count == 0:\r
- tag.mark_deleted(self._last_active_user())\r
except:\r
pass\r
\r
return False\r
\r
def mark_deleted(self, action):\r
- self.deleted = action\r
+ self.nstate.deleted = action\r
self.save()\r
\r
if action:\r
for tag in self.tags.all():\r
tag.used_count = models.F('used_count') - 1\r
tag.save()\r
- if tag.used_count == 0:\r
- tag.mark_deleted(self._last_active_user())\r
else:\r
for tag in Tag.objects.filter(name__in=self.tagname_list()):\r
tag.used_count = models.F('used_count') + 1\r
tag.save()\r
\r
def save(self, *args, **kwargs):\r
+ tags_changed = self._process_changes_in_tags()\r
+ \r
if not self.id:\r
self.node_type = self.get_type()\r
super(BaseModel, self).save(*args, **kwargs)\r
if self.parent_id and not self.abs_parent_id:\r
self.abs_parent = self.parent.absolute_parent\r
\r
- tags_changed = self._process_changes_in_tags()\r
-\r
super(Node, self).save(*args, **kwargs)\r
if tags_changed: self.tags = list(Tag.objects.filter(name__in=self.tagname_list()))\r
\r
app_label = 'forum'\r
\r
\r
+class NodeState(models.Model):\r
+ node = models.ForeignKey(Node, related_name='states')\r
+ state_type = models.CharField(max_length=16)\r
+ action = models.OneToOneField('Action', related_name="node_state")\r
+\r
+ class Meta:\r
+ unique_together = ('node', 'state_type')\r
+ app_label = 'forum'\r
+\r
+\r