models.py 159 KB
Newer Older
1
# imports from python libraries
2
import os
3
import hashlib
4 5
import datetime
import json
6 7
import magic
import mimetypes
8

9 10 11 12
from itertools import chain     # Using from_iterable()
from hashfs import HashFS       # content-addressable file management system
from StringIO import StringIO
from PIL import Image
13
from django.utils import timezone
14

15
# imports from installed packages
16
from django.contrib.auth.models import User
17
from django.contrib.auth.models import Group as DjangoGroup
18
from django.contrib.sessions.models import Session
19
from django.db import models
20
from django.http import HttpRequest
21
from celery import task
22

Nagarjuna's avatar
Nagarjuna committed
23
from django_mongokit import connection
24
from django_mongokit import get_database
Nagarjuna's avatar
Nagarjuna committed
25
from django_mongokit.document import DjangoDocument
26
from django.core.files.images import get_image_dimensions
27

28
from mongokit import IS, OR
29
from mongokit import INDEX_ASCENDING, INDEX_DESCENDING
30

31 32 33 34
try:
    from bson import ObjectId
except ImportError:  # old pymongo
    from pymongo.objectid import ObjectId
35

Nagarjuna's avatar
Nagarjuna committed
36

37
# imports from application folders/files
38
from gnowsys_ndf.settings import RCS_REPO_DIR, MEDIA_ROOT
39
from gnowsys_ndf.settings import RCS_REPO_DIR_HASH_LEVEL
40 41
from gnowsys_ndf.settings import MARKUP_LANGUAGE
from gnowsys_ndf.settings import MARKDOWN_EXTENSIONS
42
from gnowsys_ndf.settings import GSTUDIO_GROUP_AGENCY_TYPES, GSTUDIO_AUTHOR_AGENCY_TYPES
43
from gnowsys_ndf.settings import GSTUDIO_DEFAULT_LICENSE
44
from gnowsys_ndf.settings import META_TYPE
45
from gnowsys_ndf.settings import GSTUDIO_BUDDY_LOGIN
46
from gnowsys_ndf.ndf.rcslib import RCS
47
from registration.signals import user_registered
48

49

50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
NODE_TYPE_CHOICES = (
    ('Nodes'),
    ('Attribute Types'),
    ('Attributes'),
    ('Relation Types'),
    ('Relations'),
    ('GSystem Types'),
    ('GSystems'),
    ('Node Specification'),
    ('Attribute Specification'),
    ('Relation Specification'),
    ('Intersection'),
    ('Complement'),
    ('Union'),
    ('Process Types'),
    ('Process')
66
)
67

68
TYPES_OF_GROUP = (
Anuja G's avatar
Anuja G committed
69
    ('PUBLIC'),
70 71
    ('PRIVATE'),
    ('ANONYMOUS')
72 73 74
)

EDIT_POLICY = (
75
    ('EDITABLE_NON_MODERATED'),
Anuja G's avatar
Anuja G committed
76
    ('EDITABLE_MODERATED'),
77
    ('NON_EDITABLE')
78 79 80
)

SUBSCRIPTION_POLICY = (
Anuja G's avatar
Anuja G committed
81 82 83
    ('OPEN'),
    ('BY_REQUEST'),
    ('BY_INVITATION'),
84 85 86
)

EXISTANCE_POLICY = (
Anuja G's avatar
Anuja G committed
87 88
    ('ANNOUNCED'),
    ('NOT_ANNOUNCED')
89 90 91
)

LIST_MEMBER_POLICY = (
Anuja G's avatar
Anuja G committed
92 93
    ('DISCLOSED_TO_MEM'),
    ('NOT_DISCLOSED_TO_MEM')
94 95
)

96
ENCRYPTION_POLICY = (
Anuja G's avatar
Anuja G committed
97 98
    ('ENCRYPTED'),
    ('NOT_ENCRYPTED')
99
)
100

101
DATA_TYPE_CHOICES = (
102 103 104 105 106 107 108 109 110 111 112 113
    "None",
    "bool",
    "basestring",
    "unicode",
    "int",
    "float",
    "long",
    "datetime.datetime",
    "list",
    "dict",
    "ObjectId",
    "IS()"
114 115
)

tulisiddhant's avatar
tulisiddhant committed
116 117 118 119 120
my_doc_requirement = u'storing_orignal_doc'
reduced_doc_requirement = u'storing_reduced_doc'
to_reduce_doc_requirement = u'storing_to_be_reduced_doc'
indexed_word_list_requirement = u'storing_indexed_words'

121
# CUSTOM DATA-TYPE DEFINITIONS
122
STATUS_CHOICES_TU = IS(u'DRAFT', u'HIDDEN', u'PUBLISHED', u'DELETED', u'MODERATION')
123 124 125
STATUS_CHOICES = tuple(str(qtc) for qtc in STATUS_CHOICES_TU)

QUIZ_TYPE_CHOICES_TU = IS(u'Short-Response', u'Single-Choice', u'Multiple-Choice')
126 127
QUIZ_TYPE_CHOICES = tuple(str(qtc) for qtc in QUIZ_TYPE_CHOICES_TU)

Kedar A.'s avatar
Kedar A. committed
128
# Designate a root folder for HashFS. If the folder does not exists already, it will be created.
129 130 131 132 133
# Set the `depth` to the number of subfolders the file's hash should be split when saving.
# Set the `width` to the desired width of each subfolder.
gfs = HashFS(MEDIA_ROOT, depth=3, width=1, algorithm='sha256')
# gfs: gstudio file system

134

Nagarjuna's avatar
Nagarjuna committed
135 136
@connection.register
class Node(DjangoDocument):
Kedar A.'s avatar
Kedar A. committed
137
    '''Everything is a Node.  Other classes should inherit this Node class.
138 139 140 141

    According to the specification of GNOWSYS, all nodes, including
    types, metatypes and members of types, edges of nodes, should all
    be Nodes.
Kedar A.'s avatar
Kedar A. committed
142 143

    Member of this class must belong to one of the NODE_TYPE_CHOICES.
144 145 146

    Some in-built Edge names (Relation types) are defined in this
    class: type_of, member_of, prior_node, post_node, collection_set,
Kedar A.'s avatar
Kedar A. committed
147
    group_set.
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166

    type_of is used to express generalization of Node. And member_of
    to express its type. This type_of should not be confused with
    _type.  The latter expresses the Python classes defined in this
    program that the object inherits.  The former (type_of) is about
    the data the application represents.

    _type is useful in seggregating the nodes from the mongodb
    collection, where all nodes are stored.

    prior_node is to express that the current node depends in some way
    to another node/s.  post_node is seldom used.  Currently we use it
    to define sub-Group, and to set replies to a post in the Forum App.

    Nodes are publisehed in one group or another, or in more than one
    group. The groups in which a node is publisehed is expressed in
    group_set.

    '''
167
    objects = models.Manager()
168

169
    collection_name = 'Nodes'
Nagarjuna's avatar
Nagarjuna committed
170
    structure = {
171 172 173
        '_type': unicode, # check required: required field, Possible
                          # values are to be taken only from the list
                          # NODE_TYPE_CHOICES
Nagarjuna's avatar
Nagarjuna committed
174 175 176
        'name': unicode,
        'altnames': unicode,
        'plural': unicode,
Kedar A.'s avatar
Kedar A. committed
177
        'prior_node': [ObjectId],
178
        'post_node': [ObjectId],
Kedar A.'s avatar
Kedar A. committed
179

180 181 182
        # 'language': unicode,  # previously it was unicode.
        'language': (basestring, basestring),  # Tuple are converted into a simple list
                                               # ref: https://github.com/namlook/mongokit/wiki/Structure#tuples
183

Kedar A.'s avatar
Kedar A. committed
184
        'type_of': [ObjectId], # check required: only ObjectIDs of GSystemType
185 186 187 188 189 190 191
        'member_of': [ObjectId], # check required: only ObjectIDs of
                                 # GSystemType for GSystems, or only
                                 # ObjectIDs of MetaTypes for
                                 # GSystemTypes
        'access_policy': unicode, # check required: only possible
                                  # values are Public or Private.  Why
                                  # is this unicode?
Kedar A.'s avatar
Kedar A. committed
192

Nagarjuna's avatar
Nagarjuna committed
193
      	'created_at': datetime.datetime,
194
        'created_by': int, # test required: only ids of Users
195 196

        'last_update': datetime.datetime,
197 198 199 200 201 202 203
        'modified_by': int, # test required: only ids of Users

        'contributors': [int], # test required: set of all ids of
                               # Users of created_by and modified_by
                               # fields
        'location': [dict], # check required: this dict should be a
                            # valid GeoJason format
Kedar A.'s avatar
Kedar A. committed
204 205
        'content': unicode,
        'content_org': unicode,
206 207 208 209 210 211 212 213 214 215 216

        'group_set': [ObjectId], # check required: should not be
                                 # empty. For type nodes it should be
                                 # set to a Factory Group called
                                 # Administration
        'collection_set': [ObjectId],  # check required: to exclude
                                       # parent nodes as children, use
                                       # MPTT logic
        'property_order': [],  # Determines the order & grouping in
                               # which attribute(s)/relation(s) are
                               # displayed in the form
217

218
        'start_publication': datetime.datetime,
Nagarjuna's avatar
Nagarjuna committed
219 220
        'tags': [unicode],
        'featured': bool,
221
        'url': unicode,
Nagarjuna's avatar
Nagarjuna committed
222
        'comment_enabled': bool,
Anuja G's avatar
Anuja G committed
223
      	'login_required': bool,
224
      	# 'password': basestring,
225

226 227 228
        'status': STATUS_CHOICES_TU,
        'rating':[{'score':int,
                  'user_id':int,
229
                  'ip_address':basestring}],
230
        'snapshot':dict
231
    }
232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280

    indexes = [
        {
            # 1: Compound index
            'fields': [
                ('_type', INDEX_ASCENDING), ('name', INDEX_ASCENDING)
            ]
        }, {
            # 2: Compound index
            'fields': [
                ('_type', INDEX_ASCENDING), ('created_by', INDEX_ASCENDING)
            ]
        }, {
            # 3: Single index
            'fields': [
                ('group_set', INDEX_ASCENDING)
            ]
        }, {
            # 4: Single index
            'fields': [
                ('member_of', INDEX_ASCENDING)
            ]
        }, {
            # 5: Single index
            'fields': [
                ('name', INDEX_ASCENDING)
            ]
        }, {
            # 6: Compound index
            'fields': [
                ('created_by', INDEX_ASCENDING), ('status', INDEX_ASCENDING), \
                ('access_policy', INDEX_ASCENDING), ('last_update' , INDEX_DESCENDING)
            ]
        }, {
            # 7: Compound index
            'fields': [
                ('created_by', INDEX_ASCENDING), ('status', INDEX_ASCENDING), \
                ('access_policy', INDEX_ASCENDING), ('created_at' , INDEX_DESCENDING)
            ]
        }, {
            # 8: Compound index
            'fields': [
                ('created_by', INDEX_ASCENDING), ('last_update' , INDEX_DESCENDING)
            ]
        }, {
            # 9: Compound index
            'fields': [
                ('status', INDEX_ASCENDING), ('last_update' , INDEX_DESCENDING)
            ]
Kedar A.'s avatar
Kedar A. committed
281
        },
282 283
    ]

284 285 286 287
    required_fields = ['name', '_type'] # 'group_set' to be included
                                        # here after the default
                                        # 'Administration' group is
                                        # ready.
288
    default_values = {
289
                        'created_at': datetime.datetime.now,
290
                        'status': u'DRAFT',
291
                        'language': ('en', 'English')
292
                    }
Nagarjuna's avatar
Nagarjuna committed
293
    use_dot_notation = True
294

Kedar A.'s avatar
Kedar A. committed
295

296 297 298 299 300 301 302 303 304 305 306 307
    def add_in_group_set(self, group_id):
        if group_id not in self.group_set:
            self.group_set.append(ObjectId(group_id))
        return self


    def remove_from_group_set(self, group_id):
        if group_id in self.group_set:
            self.group_set.remove(ObjectId(group_id))
        return self


308
    # custom methods provided for Node class
309 310 311 312 313 314 315 316 317 318 319
    def fill_node_values(self, request=HttpRequest(), **kwargs):

        # 'name': unicode,
        if kwargs.has_key('name'):
            name = kwargs.get('name', '')
        else:
            name = request.POST.get('name', '').strip()
        self.name = unicode(name)

        # 'altnames': unicode,
        if kwargs.has_key('altnames'):
Kedar A.'s avatar
Kedar A. committed
320
            altnames = kwargs.get('altnames', name)
321 322 323 324 325 326
        else:
            altnames = request.POST.get('altnames', name).strip()
        self.altnames = unicode(altnames)

        # 'plural': unicode,
        if kwargs.has_key('plural'):
Kedar A.'s avatar
Kedar A. committed
327
            plural = kwargs.get('plural', None)
328 329 330 331 332 333
        else:
            plural = request.POST.get('plural', None)
        self.plural = unicode(plural)

        # 'prior_node': [ObjectId],
        if kwargs.has_key('prior_node'):
Kedar A.'s avatar
Kedar A. committed
334
            prior_node = kwargs.get('prior_node', [])
335 336 337 338 339 340 341 342
        else:
            prior_node = request.POST.get('prior_node', [])
        self.prior_node = prior_node
        if prior_node and not isinstance(prior_node, list):
            self.prior_node = [ObjectId(each) for each in prior_node]

        # 'post_node': [ObjectId]
        if kwargs.has_key('post_node'):
Kedar A.'s avatar
Kedar A. committed
343
            post_node = kwargs.get('post_node', [])
344 345 346 347 348 349 350 351
        else:
            post_node = request.POST.get('post_node', [])
        self.post_node = post_node
        if post_node and not isinstance(post_node, list):
            self.post_node = [ObjectId(each) for each in post_node]

        # 'language': (basestring, basestring)
        if kwargs.has_key('language'):
Kedar A.'s avatar
Kedar A. committed
352
            language = kwargs.get('language', ('en', 'English'))
353 354 355 356 357 358
        else:
            language = request.POST.get('language', ('en', 'English'))
        self.language = language

        # 'type_of': [ObjectId]
        if kwargs.has_key('type_of'):
Kedar A.'s avatar
Kedar A. committed
359
            type_of = kwargs.get('type_of', [])
360 361 362 363 364 365 366 367
        else:
            type_of = request.POST.get('type_of', [])
        self.type_of = type_of
        if type_of and not isinstance(type_of, list):
            self.type_of = [ObjectId(each) for each in type_of]

        # 'member_of': [ObjectId]
        if kwargs.has_key('member_of'):
Kedar A.'s avatar
Kedar A. committed
368
            member_of = kwargs.get('member_of', [])
369 370
        else:
            member_of = request.POST.get('member_of', [])
Kedar A.'s avatar
Kedar A. committed
371 372 373
        self.member_of = [ObjectId(member_of)] if member_of else member_of
        # if member_of and not isinstance(member_of, list):
        #     self.member_of = [ObjectId(each) for each in member_of]
374 375 376

        # 'access_policy': unicode
        if kwargs.has_key('access_policy'):
Kedar A.'s avatar
Kedar A. committed
377
            access_policy = kwargs.get('access_policy', u'PUBLIC')
378 379 380
        else:
            access_policy = request.POST.get('access_policy', u'PUBLIC')
        self.access_policy = unicode(access_policy)
Kedar A.'s avatar
Kedar A. committed
381

382 383 384 385 386 387 388 389 390
        # 'created_at': datetime.datetime
        #   - this will be system generated (while instantiation time), always.

        # 'last_update': datetime.datetime,
        #   - this will be system generated (from save method), always.

        # 'created_by': int
        if not self.created_by:
            if kwargs.has_key('created_by'):
Kedar A.'s avatar
Kedar A. committed
391
                created_by = kwargs.get('created_by', '')
392 393 394 395 396 397
            elif request:
                created_by = request.user.id
            self.created_by = int(created_by) if created_by else 0

        # 'modified_by': int, # test required: only ids of Users
        if kwargs.has_key('modified_by'):
Kedar A.'s avatar
Kedar A. committed
398
            modified_by = kwargs.get('modified_by', None)
399
        elif request:
Kedar A.'s avatar
Kedar A. committed
400 401 402 403
            if hasattr(request, 'user'):
                modified_by = request.user.id
            elif kwargs.has_key('created_by'):
                modified_by = created_by
404
        self.modified_by = int(modified_by) if modified_by else self.created_by
405 406 407

        # 'contributors': [int]
        if kwargs.has_key('contributors'):
408
            contributors = kwargs.get('contributors', [self.created_by])
409
        else:
410
            contributors = request.POST.get('contributors', [self.created_by])
411 412
        self.contributors = contributors
        if contributors and not isinstance(contributors, list):
Kedar A.'s avatar
Kedar A. committed
413
            self.contributors = [int(each) for each in contributors]
414 415 416

        # 'location': [dict]
        if kwargs.has_key('location'):
Kedar A.'s avatar
Kedar A. committed
417
            location = kwargs.get('location', [])
418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437
        else:
            location = request.POST.get('location', [])
        self.location = list(location) if not isinstance(location, list) else location

        # 'content': unicode
        if kwargs.has_key('content'):
            content = kwargs.get('content', '')
        else:
            content = request.POST.get('content', '')
        self.content = unicode(content)

        # 'content_org': unicode
        if kwargs.has_key('content_org'):
            content_org = kwargs.get('content_org', '')
        else:
            content_org = request.POST.get('content_org', '')
        self.content_org = unicode(content_org)

        # 'group_set': [ObjectId]
        if kwargs.has_key('group_set'):
Kedar A.'s avatar
Kedar A. committed
438
            group_set = kwargs.get('group_set', [])
439 440 441 442 443 444 445 446
        else:
            group_set = request.POST.get('group_set', [])
        self.group_set = group_set
        if group_set and not isinstance(group_set, list):
            self.group_set = [ObjectId(each) for each in group_set]

        # 'collection_set': [ObjectId]
        if kwargs.has_key('collection_set'):
Kedar A.'s avatar
Kedar A. committed
447
            collection_set = kwargs.get('collection_set', [])
448 449 450 451 452 453 454 455
        else:
            collection_set = request.POST.get('collection_set', [])
        self.collection_set = collection_set
        if collection_set and not isinstance(collection_set, list):
            self.collection_set = [ObjectId(each) for each in collection_set]

        # 'property_order': []
        if kwargs.has_key('property_order'):
Kedar A.'s avatar
Kedar A. committed
456
            property_order = kwargs.get('property_order', [])
457 458 459 460 461 462 463 464 465 466 467 468 469 470
        else:
            property_order = request.POST.get('property_order', [])
        self.property_order = list(property_order) if not isinstance(property_order, list) else property_order

        # 'start_publication': datetime.datetime,
        if kwargs.has_key('start_publication'):
            start_publication = kwargs.get('start_publication', None)
        else:
            start_publication = request.POST.get('start_publication', None)
        self.start_publication = start_publication
        # self.start_publication = datetime.datetime(start_publication) if not isinstance(start_publication, datetime.datetime) else start_publication

        # 'tags': [unicode],
        if kwargs.has_key('tags'):
Kedar A.'s avatar
Kedar A. committed
471
            tags = kwargs.get('tags', [])
472 473
        else:
            tags = request.POST.get('tags', [])
474
        self.tags = tags if tags else []
475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520
        if tags and not isinstance(tags, list):
            self.tags = [unicode(each.strip()) for each in tags.split(',')]

        # 'featured': bool,
        if kwargs.has_key('featured'):
            featured = kwargs.get('featured', None)
        else:
            featured = request.POST.get('featured', None)
        self.featured = bool(featured)

        # 'url': unicode,
        if kwargs.has_key('url'):
            url = kwargs.get('url', None)
        else:
            url = request.POST.get('url', None)
        self.url = unicode(url)

        # 'comment_enabled': bool,
        if kwargs.has_key('comment_enabled'):
            comment_enabled = kwargs.get('comment_enabled', None)
        else:
            comment_enabled = request.POST.get('comment_enabled', None)
        self.comment_enabled = bool(comment_enabled)

        # 'login_required': bool,
        if kwargs.has_key('login_required'):
            login_required = kwargs.get('login_required', None)
        else:
            login_required = request.POST.get('login_required', None)
        self.login_required = bool(login_required)

        # 'status': STATUS_CHOICES_TU,
        if kwargs.has_key('status'):
            status = kwargs.get('status', u'DRAFT')
        else:
            status = request.POST.get('status', u'DRAFT')
        self.status = unicode(status)

        # 'rating':[{'score':int, 'user_id':int, 'ip_address':basestring}],
        #       - mostly, it's on detail view and by AJAX and not in/within forms.

        # 'snapshot':dict
        #       - needs to think on this.

        return self

Kedar A.'s avatar
Kedar A. committed
521

522 523 524 525 526
    @staticmethod
    def get_node_obj_from_id_or_obj(node_obj_or_id, expected_type):
        # confirming arg 'node_obj_or_id' is Object or oid and
        # setting node_obj accordingly.
        node_obj = None
527

528 529
        if isinstance(node_obj_or_id, expected_type):
            node_obj = node_obj_or_id
530
        elif isinstance(node_obj_or_id, ObjectId) or ObjectId.is_valid(node_obj_or_id):
531 532 533
            node_obj = node_collection.one({'_id': ObjectId(node_obj_or_id)})
        else:
            # error raised:
534
            raise RuntimeError('No Node class instance found with provided arg for get_node_obj_from_id_or_obj(' + str(node_obj_or_id) + ', expected_type=' + str(expected_type) + ')')
535

536
        return node_obj
537

538

539 540 541 542 543 544 545 546 547 548 549 550 551
    def type_of_names_list(self, smallcase=False):
        """Returns a list having names of each type_of (GSystemType, i.e Wiki page,
        Blog page, etc.), built from 'type_of' field (list of ObjectIds)
        """
        type_of_names = []
        if self.type_of:
            node_cur = node_collection.find({'_id': {'$in': self.type_of}})
            if smallcase:
                type_of_names = [node.name.lower() for node in node_cur]
            else:
                type_of_names = [node.name for node in node_cur]

        return type_of_names
552

553

554
    ########## Setter(@x.setter) & Getter(@property) ##########
555 556
    @property
    def member_of_names_list(self):
557 558
        """Returns a list having names of each member (GSystemType, i.e Page,
        File, etc.), built from 'member_of' field (list of ObjectIds)
Nagarjuna's avatar
Nagarjuna committed
559

560
        """
561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585
        # member_of_names = []

        # if self.member_of:
        #     for each_member_id in self.member_of:
        #         if type(each_member_id) == ObjectId:
        #             _id = each_member_id
        #         else:
        #             _id = each_member_id['$oid']
        #         if _id:
        #             mem = node_collection.one({'_id': ObjectId(_id)})
        #             if mem:
        #                 member_of_names.append(mem.name)
        # else:
        #     if "gsystem_type" in self:
        #         for each_member_id in self.gsystem_type:
        #             if type(each_member_id) == ObjectId:
        #                 _id = each_member_id
        #             else:
        #                 _id = each_member_id['$oid']
        #             if _id:
        #                 mem = node_collection.one({'_id': ObjectId(_id)})
        #                 if mem:
        #                     member_of_names.append(mem.name)
        # return member_of_names
        return [GSystemType.get_gst_name_id(gst_id)[0] for gst_id in self.member_of]
586

587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608

    @property
    def user_details_dict(self):
        """Retrieves names of created-by & modified-by users from the given
        node, and appends those to 'user_details' dict-variable

        """
        user_details = {}
        if self.created_by:
            user_details['created_by'] = User.objects.get(pk=self.created_by).username

        contributor_names = []
        for each_pk in self.contributors:
            contributor_names.append(User.objects.get(pk=each_pk).username)
        user_details['contributors'] = contributor_names

        if self.modified_by:
            user_details['modified_by'] = User.objects.get(pk=self.modified_by).username

        return user_details


609
    @property
610
    def prior_node_dict(self):
611 612 613
        """Returns a dictionary consisting of key-value pair as
        ObjectId-Document pair respectively for prior_node objects of
        the given node.
614

615
        """
616

617
        obj_dict = {}
618
        i = 0
619
        for each_id in self.prior_node:
620 621
            i = i + 1

622
            if each_id != self._id:
623
                node_collection_object = node_collection.one({"_id": ObjectId(each_id)})
624
                dict_key = i
625
                dict_value = node_collection_object
626

627 628
                obj_dict[dict_key] = dict_value

629
        return obj_dict
630 631 632

    @property
    def collection_dict(self):
633 634 635
        """Returns a dictionary consisting of key-value pair as
        ObjectId-Document pair respectively for collection_set objects
        of the given node.
636

637
        """
638

639 640
        obj_dict = {}

641
        i = 0;
642
        for each_id in self.collection_set:
643 644
            i = i + 1

645
            if each_id != self._id:
646
                node_collection_object = node_collection.one({"_id": ObjectId(each_id)})
647
                dict_key = i
648
                dict_value = node_collection_object
649

650 651 652
                obj_dict[dict_key] = dict_value

        return obj_dict
653

654 655
    @property
    def html_content(self):
656
        """Returns the content in proper html-format.
Nagarjuna's avatar
Nagarjuna committed
657

658
        """
659 660 661 662 663 664
        if MARKUP_LANGUAGE == 'markdown':
            return markdown(self.content, MARKDOWN_EXTENSIONS)
        elif MARKUP_LANGUAGE == 'textile':
            return textile(self.content)
        elif MARKUP_LANGUAGE == 'restructuredtext':
            return restructuredtext(self.content)
665
        return self.content
666

mukesh's avatar
mukesh committed
667 668
    @property
    def current_version(self):
669 670
        history_manager = HistoryManager()
        return history_manager.get_current_version(self)
671

672 673
    @property
    def version_dict(self):
674 675
        """Returns a dictionary containing list of revision numbers of the
        given node.
676

677 678 679 680 681 682
        Example:
        {
         "1": "1.1",
         "2": "1.2",
         "3": "1.3",
        }
683

684 685 686 687
        """
        history_manager = HistoryManager()
        return history_manager.get_version_dict(self)

688 689

    ########## Built-in Functions (Overridden) ##########
690

691 692
    def __unicode__(self):
        return self._id
693

694 695
    def identity(self):
        return self.__unicode__()
696

Nagarjuna's avatar
Nagarjuna committed
697
    def save(self, *args, **kwargs):
makfire's avatar
makfire committed
698
	if "is_changed" in kwargs:
699 700 701 702
            if not kwargs["is_changed"]:
                #print "\n ", self.name, "(", self._id, ") -- Nothing has changed !\n\n"
                return

703 704
        is_new = False

705
        if not "_id" in self:
706
            is_new = True               # It's a new document, hence yet no ID!"
707

708
            # On save, set "created_at" to current date
709 710 711 712
            self.created_at = datetime.datetime.today()

        self.last_update = datetime.datetime.today()

713 714 715 716 717
        # Check the fields which are not present in the class
        # structure, whether do they exists in their GSystemType's
        # "attribute_type_set"; If exists, add them to the document
        # Otherwise, throw an error -- " Illegal access: Invalid field
        # found!!! "
718 719 720

        try:

721 722 723 724
            invalid_struct_fields = list(set(self.structure.keys()) - set(self.keys()))
            # print '\n invalid_struct_fields: ',invalid_struct_fields
            if invalid_struct_fields:
                for each_invalid_field in invalid_struct_fields:
725
                    if each_invalid_field in self.structure:
726 727
                        print "=== removed from structure", each_invalid_field, ' : ', self.structure.pop(each_invalid_field)

728

729 730 731 732 733 734 735 736
            keys_list = self.structure.keys()
            keys_list.append('_id')
            invalid_struct_fields_list = list(set(self.keys()) - set(keys_list))
            # print '\n invalid_struct_fields_list: ',invalid_struct_fields_list
            if invalid_struct_fields_list:
                for each_invalid_field in invalid_struct_fields_list:
                    if each_invalid_field in self:
                        print "=== removed ", each_invalid_field, ' : ', self.pop(each_invalid_field)
737

root's avatar
root committed
738

739 740 741 742
        except Exception, e:
            print e
            pass

743 744
        invalid_fields = []

745 746 747 748
        for key, value in self.iteritems():
            if key == '_id':
                continue

749
            if not (key in self.structure):
750
                field_found = False
751
                for gst_id in self.member_of:
752 753
                    attribute_set_list = node_collection.one({'_id': gst_id}).attribute_type_set

754 755 756 757
                    for attribute in attribute_set_list:
                        if key == attribute['name']:
                            field_found = True

758 759 760 761 762
                            # TODO: Check whether type of "value"
                            # matches with that of
                            # "attribute['data_type']" Don't continue
                            # searching from list of remaining
                            # attributes
763 764 765
                            break

                    if field_found:
766 767
                        # Don't continue searching from list of
                        # remaining gsystem-types
768 769 770
                        break

                if not field_found:
771
                    invalid_fields.append(key)
772
                    print "\n Invalid field(", key, ") found!!!\n"
773 774
                    # Throw an error: " Illegal access: Invalid field
                    # found!!! "
775

776 777 778 779 780 781 782 783 784 785 786
        # print "== invalid_fields : ", invalid_fields
        try:
            self_keys = self.keys()
            if invalid_fields:
                for each_invalid_field in invalid_fields:
                    if each_invalid_field in self_keys:
                        self.pop(each_invalid_field)
        except Exception, e:
            print "\nError while processing invalid fields: ", e
            pass

787
        # if Add-Buddy feature is enabled:
788 789 790 791 792 793 794
        #   - Get all user id's of active buddies with currently logged in user.
        #   - Check if each of buddy-user-id does not exists in contributors of node object, add it.
        if GSTUDIO_BUDDY_LOGIN:
            buddy_contributors = Buddy.get_buddy_userids_list_within_datetime(
                                                    self.created_by,
                                                    self.last_update or self.created_at
                                                )
795
            # print 'buddy_contributors : ', buddy_contributors
796 797 798 799 800 801

            if buddy_contributors:
                for each_bcontrib in buddy_contributors:
                    if each_bcontrib not in self.contributors:
                        self.contributors.append(each_bcontrib)

802
        super(Node, self).save(*args, **kwargs)
803

804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834
        # This is the save method of the node class.It is still not
        # known on which objects is this save method applicable We
        # still do not know if this save method is called for the
        # classes which extend the Node Class or for every class There
        # is a very high probability that it is called for classes
        # which extend the Node Class only The classes which we have
        # i.e. the MyReduce() and ToReduce() class do not extend from
        # the node class Hence calling the save method on those objects
        # should not create a recursive function

        # If it is a new document then Make a new object of ToReduce
        # class and the id of this document to that object else Check
        # whether there is already an object of ToReduce() with the id
        # of this object.  If there is an object present pass else add
        # that object I have not applied the above algorithm

        # Instead what I have done is that I have searched the
        # ToReduce() collection class and searched whether the ID of
        # this document is present or not.  If the id is not present
        # then add that id.If it is present then do not add that id

        old_doc = node_collection.collection.ToReduceDocs.find_one({'required_for':to_reduce_doc_requirement,'doc_id':self._id})

        #print "~~~~~~~~~~~~~~~~~~~~It is not present in the ToReduce() class collection.Message Coming from save() method ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~",self._id
        if  not old_doc:
            z = node_collection.collection.ToReduceDocs()
            z.doc_id = self._id
            z.required_for = to_reduce_doc_requirement
            z.save()

        #If you create/edit anything then this code shall add it in the URL
835

836 837
        history_manager = HistoryManager()
        rcs_obj = RCS()
838 839

        if is_new:
840
            # Create history-version-file
841 842 843
            try:
                if history_manager.create_or_replace_json_file(self):
                    fp = history_manager.get_file_path(self)
844 845 846
                    user_list = User.objects.filter(pk=self.created_by)
                    user = user_list[0].username if user_list else 'user'
                    # user = User.objects.get(pk=self.created_by).username
847 848 849 850
                    message = "This document (" + self.name + ") is created by " + user + " on " + self.created_at.strftime("%d %B %Y")
                    rcs_obj.checkin(fp, 1, message.encode('utf-8'), "-i")
            except Exception as err:
                print "\n DocumentError: This document (", self._id, ":", self.name, ") can't be created!!!\n"
851
                node_collection.collection.remove({'_id': self._id})
852 853
                raise RuntimeError(err)

854 855 856
        else:
            # Update history-version-file
            fp = history_manager.get_file_path(self)
857 858 859 860 861 862 863

            try:
                rcs_obj.checkout(fp)
            except Exception as err:
                try:
                    if history_manager.create_or_replace_json_file(self):
                        fp = history_manager.get_file_path(self)
864 865 866
                        # user = User.objects.get(pk=self.created_by).username
                        user_list = User.objects.filter(pk=self.created_by)
                        user = user_list[0].username if user_list else 'user'
867 868 869 870 871
                        message = "This document (" + self.name + ") is re-created by " + user + " on " + self.created_at.strftime("%d %B %Y")
                        rcs_obj.checkin(fp, 1, message.encode('utf-8'), "-i")

                except Exception as err:
                    print "\n DocumentError: This document (", self._id, ":", self.name, ") can't be re-created!!!\n"
872
                    node_collection.collection.remove({'_id': self._id})
873
                    raise RuntimeError(err)
874

875 876
            try:
                if history_manager.create_or_replace_json_file(self):
877 878 879
                    # user = User.objects.get(pk=self.modified_by).username
                    user_list = User.objects.filter(pk=self.created_by)
                    user = user_list[0].username if user_list else 'user'
880
                    message = "This document (" + self.name + ") is lastly updated by " + user + " status:" + self.status + " on " + self.last_update.strftime("%d %B %Y")
881
                    rcs_obj.checkin(fp, 1, message.encode('utf-8'))
882

883 884 885
            except Exception as err:
                print "\n DocumentError: This document (", self._id, ":", self.name, ") can't be updated!!!\n"
                raise RuntimeError(err)
886 887 888 889 890 891 892

        #update the snapshot feild
        if kwargs.get('groupid'):
            # gets the last version no.
            rcsno = history_manager.get_current_version(self)
            node_collection.collection.update({'_id':self._id}, {'$set': {'snapshot'+"."+str(kwargs['groupid']):rcsno }}, upsert=False, multi=True)

Kedar A.'s avatar
Kedar A. committed
893

894
    # User-Defined Functions
895
    def get_possible_attributes(self, gsystem_type_id_or_list):
896 897
        """Returns user-defined attribute(s) of given node which belongs to
        either given single/list of GType(s).
898

899 900 901
        Keyword arguments: gsystem_type_id_or_list -- Single/List of
        ObjectId(s) of GSystemTypes' to which the given node (self)
        belongs
902

903 904 905 906 907 908 909 910 911
        If node (self) has '_id' -- Node is created; indicating
        possible attributes needs to be searched under GAttribute
        collection & return value of those attributes (previously
        existing) as part of the list along with attribute-data_type

        Else -- Node needs to be created; indicating possible
        attributes needs to be searched under AttributeType collection
        & return default value 'None' of those attributes as part of
        the list along with attribute-data_type
912

913 914
        Returns: Dictionary that holds follwoing details:- Key -- Name
        of the attribute Value, which inturn is a dictionary that
915
        holds key and values as shown below:
916 917 918 919 920 921

        { 'attribute-type-name': { 'altnames': Value of AttributeType
        node's altnames field, 'data_type': Value of AttributeType
        node's data_type field, 'object_value': Value of GAttribute
        node's object_value field } }

922 923 924 925 926
        """

        gsystem_type_list = []
        possible_attributes = {}

927
        # Converts to list, if passed parameter is only single ObjectId
928 929
        if not isinstance(gsystem_type_id_or_list, list):
            gsystem_type_list = [gsystem_type_id_or_list]
930 931
        else:
            gsystem_type_list = gsystem_type_id_or_list
932 933 934 935 936 937 938 939 940

        # Code for finding out attributes associated with each gsystem_type_id in the list
        for gsystem_type_id in gsystem_type_list:

            # Converts string representaion of ObjectId to it's corresponding ObjectId type, if found
            if not isinstance(gsystem_type_id, ObjectId):
                if ObjectId.is_valid(gsystem_type_id):
                    gsystem_type_id = ObjectId(gsystem_type_id)
                else:
941
                    error_message = "\n ObjectIdError: Invalid ObjectId (" + str(gsystem_type_id) + ") found while finding attributes !!!\n"
942
                    raise Exception(error_message)
943

944
            # Case [A]: While editing GSystem
945
            # Checking in Gattribute collection - to collect user-defined attributes' values, if already set!
946
            if "_id" in self:
947
                # If - node has key '_id'
948
                attributes = triple_collection.find({'_type': "GAttribute", 'subject': self._id})
949 950
                for attr_obj in attributes:
                    # attr_obj is of type - GAttribute [subject (node._id), attribute_type (AttributeType), object_value (value of attribute)]
951
                    # Must convert attr_obj.attribute_type [dictionary] to node_collection(attr_obj.attribute_type) [document-object]
952 953
                    # PREV: AttributeType.append_attribute(node_collection.collection.AttributeType(attr_obj.attribute_type), possible_attributes, attr_obj.object_value)
                    AttributeType.append_attribute(attr_obj.attribute_type, possible_attributes, attr_obj.object_value)
954

955
            # Case [B]: While creating GSystem / if new attributes get added
956
            # Again checking in AttributeType collection - because to collect newly added user-defined attributes, if any!
957
            attributes = node_collection.find({'_type': 'AttributeType', 'subject_type': gsystem_type_id})
958 959
            for attr_type in attributes:
                # Here attr_type is of type -- AttributeType
960
                # PREV: AttributeType.append_attribute(attr_type, possible_attributes)
961
                AttributeType.append_attribute(attr_type, possible_attributes)
962

963
            # type_of check for current GSystemType to which the node belongs to
964
            gsystem_type_node = node_collection.one({'_id': gsystem_type_id}, {'name': 1, 'type_of': 1})
965
            if gsystem_type_node.type_of:
966
                attributes = node_collection.find({'_type': 'AttributeType', 'subject_type': {'$in': gsystem_type_node.type_of}})
967 968 969
                for attr_type in attributes:
                    # Here attr_type is of type -- AttributeType
                    AttributeType.append_attribute(attr_type, possible_attributes)
970

971 972
        return possible_attributes

Anuja G's avatar
Anuja G committed
973

974
    def get_possible_relations(self, gsystem_type_id_or_list):
975 976
        """Returns relation(s) of given node which belongs to either given
        single/list of GType(s).
977

978 979
        Keyword arguments: gsystem_type_id_or_list -- Single/List of
        ObjectId(s) of GTypes' to which the given node (self) belongs
Kedar A.'s avatar
Kedar A. committed
980

981 982 983 984 985 986 987 988 989 990 991
        If node (self) has '_id' -- Node is created; indicating
        possible relations need to be searched under GRelation
        collection & return value of those relations (previously
        existing) as part of the dict along with relation-type details
        ('object_type' and 'inverse_name')

        Else -- Node needs to be created; indicating possible
        relations need to be searched under RelationType collection &
        return default value 'None' for those relations as part of the
        dict along with relation-type details ('object_type' and
        'inverse_name')
Kedar A.'s avatar
Kedar A. committed
992

993 994
        Returns: Dictionary that holds details as follows:- Key --
        Name of the relation Value -- It's again a dictionary that
Kedar A.'s avatar
Kedar A. committed
995
        holds key and values as shown below:
996 997 998 999 1000 1001 1002 1003

        { // If inverse_relation - False 'relation-type-name': {
        'altnames': Value of RelationType node's altnames field [0th
        index-element], 'subject_or_object_type': Value of
        RelationType node's object_type field, 'inverse_name': Value
        of RelationType node's inverse_name field,
        'subject_or_right_subject_list': List of Value(s) of GRelation
        node's right_subject field }
Kedar A.'s avatar
Kedar A. committed
1004

1005 1006 1007 1008 1009 1010 1011 1012
          // If inverse_relation - True 'relation-type-name': {
          'altnames': Value of RelationType node's altnames field [1st
          index-element], 'subject_or_object_type': Value of
          RelationType node's subject_type field, 'inverse_name':
          Value of RelationType node's name field,
          'subject_or_right_subject_list': List of Value(s) of
          GRelation node's subject field } }

1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031
        """
        gsystem_type_list = []
        possible_relations = {}
        # Converts to list, if passed parameter is only single ObjectId
        if not isinstance(gsystem_type_id_or_list, list):
            gsystem_type_list = [gsystem_type_id_or_list]
        else:
            gsystem_type_list = gsystem_type_id_or_list

        # Code for finding out relations associated with each gsystem_type_id in the list
        for gsystem_type_id in gsystem_type_list:

            # Converts string representaion of ObjectId to it's corresponding ObjectId type, if found
            if not isinstance(gsystem_type_id, ObjectId):
                if ObjectId.is_valid(gsystem_type_id):
                    gsystem_type_id = ObjectId(gsystem_type_id)
                else:
                    error_message = "\n ObjectIdError: Invalid ObjectId (" + gsystem_type_id + ") found while finding relations !!!\n"
                    raise Exception(error_message)
1032 1033

            # Relation
1034
            inverse_relation = False
1035 1036 1037
            # Case - While editing GSystem Checking in GRelation
            # collection - to collect relations' values, if already
            # set!
1038
            if "_id" in self:
1039
                # If - node has key '_id'
1040
                relations = triple_collection.find({'_type': "GRelation", 'subject': self._id, 'status': u"PUBLISHED"})
1041
                for rel_obj in relations:
1042 1043 1044 1045 1046 1047
                    # rel_obj is of type - GRelation
                    # [subject(node._id), relation_type(RelationType),
                    # right_subject(value of related object)] Must
                    # convert rel_obj.relation_type [dictionary] to
                    # collection.Node(rel_obj.relation_type)
                    # [document-object]
1048
                    RelationType.append_relation(
1049 1050
                        # PREV:  node_collection.collection.RelationType(rel_obj.relation_type),
                        rel_obj.relation_type,
1051 1052
                        possible_relations, inverse_relation, rel_obj.right_subject
                    )
1053

1054 1055 1056
            # Case - While creating GSystem / if new relations get
            # added Checking in RelationType collection - because to
            # collect newly added user-defined relations, if any!
1057
            relations = node_collection.find({'_type': 'RelationType', 'subject_type': gsystem_type_id})
1058 1059
            for rel_type in relations:
                # Here rel_type is of type -- RelationType
1060
                RelationType.append_relation(rel_type, possible_relations, inverse_relation)
1061

1062 1063
            # type_of check for current GSystemType to which the node
            # belongs to
1064
            gsystem_type_node = node_collection.one({'_id': gsystem_type_id}, {'name': 1, 'type_of': 1})
1065
            if gsystem_type_node.type_of:
1066
                relations = node_collection.find({'_type': 'RelationType', 'subject_type': {'$in': gsystem_type_node.type_of}})
1067 1068
                for rel_type in relations:
                    # Here rel_type is of type -- RelationType
1069
                    RelationType.append_relation(rel_type, possible_relations, inverse_relation)
1070

1071
            # Inverse-Relation
1072
            inverse_relation = True
1073 1074 1075
            # Case - While editing GSystem Checking in GRelation
            # collection - to collect inverse-relations' values, if
            # already set!
1076
            if "_id" in self:
1077
                # If - node has key '_id'
1078
                relations = triple_collection.find({'_type': "GRelation", 'right_subject': self._id, 'status': u"PUBLISHED"})
1079
                for rel_obj in relations:
1080 1081 1082 1083 1084 1085
                    # rel_obj is of type - GRelation
                    # [subject(node._id), relation_type(RelationType),
                    # right_subject(value of related object)] Must
                    # convert rel_obj.relation_type [dictionary] to
                    # collection.Node(rel_obj.relation_type)
                    # [document-object]
1086 1087
                    rel_type_node = node_collection.one({'_id': ObjectId(rel_obj.relation_type)})
                    if META_TYPE[4] in rel_type_node.member_of_names_list:
1088 1089 1090 1091
                        # We are not handling inverse relation processing for
                        # Triadic relationship(s)
                        continue

1092
                    RelationType.append_relation(
1093 1094
                        # node_collection.collection.RelationType(rel_obj.relation_type),
                        rel_obj.relation_type,
1095 1096
                        possible_relations, inverse_relation, rel_obj.subject
                    )
1097

1098 1099 1100
            # Case - While creating GSystem / if new relations get
            # added Checking in RelationType collection - because to
            # collect newly added user-defined relations, if any!
1101
            relations = node_collection.find({'_type': 'RelationType', 'object_type': gsystem_type_id})
1102 1103 1104 1105
            for rel_type in relations:
                # Here rel_type is of type -- RelationType
                RelationType.append_relation(rel_type, possible_relations, inverse_relation)

1106 1107
            # type_of check for current GSystemType to which the node
            # belongs to
1108
            gsystem_type_node = node_collection.one({'_id': gsystem_type_id}, {'name': 1, 'type_of': 1})
1109
            if gsystem_type_node.type_of:
1110
                relations = node_collection.find({'_type': 'RelationType', 'object_type': {'$in': gsystem_type_node.type_of}})
1111 1112 1113 1114 1115
                for rel_type in relations:
                    # Here rel_type is of type -- RelationType
                    RelationType.append_relation(rel_type, possible_relations, inverse_relation)

        return possible_relations
1116

1117 1118
    def get_neighbourhood(self, member_of):
        """Attaches attributes and relations of the node to itself;
Kedar A.'s avatar
Kedar A. committed
1119
        i.e. key's types to it's structure and key's values to itself
1120 1121 1122 1123 1124 1125 1126 1127 1128
        """

        attributes = self.get_possible_attributes(member_of)
        for key, value in attributes.iteritems():
            self.structure[key] = value['data_type']
            self[key] = value['object_value']

        relations = self.get_possible_relations(member_of)
        for key, value in relations.iteritems():
1129 1130
            self.structure[key] = value['subject_or_object_type']
            self[key] = value['subject_or_right_subject_list']
1131 1132


Nagarjuna's avatar
Nagarjuna committed
1133 1134
@connection.register
class AttributeType(Node):
1135 1136
    '''To define reusable properties that can be set as possible
    attributes to a GSystemType. A set of possible properties defines
Kedar A.'s avatar
Kedar A. committed
1137
    a GSystemType.
1138 1139

    '''
1140

1141
    structure = {
1142
	'data_type': basestring, # check required: only of the DATA_TYPE_CHOICES
Kedar A.'s avatar
Kedar A. committed
1143
        'complex_data_type': [unicode], # can be a list or a dictionary
1144 1145 1146 1147 1148 1149
        'subject_type': [ObjectId], # check required: only one of Type
                                    # Nodes. GSystems cannot be set as
                                    # subject_types
	'applicable_node_type': [basestring],	# can be one or more
                                                # than one of
                                                # NODE_TYPE_CHOICES
Kedar A.'s avatar
Kedar A. committed
1150

1151
	'verbose_name': basestring,
Kedar A.'s avatar
Kedar A. committed
1152 1153
	'null': bool,
	'blank': bool,
1154
	'help_text': unicode,
1155 1156
	'max_digits': int, # applicable if the datatype is a number
	'decimal_places': int, # applicable if the datatype is a float
1157
	'auto_now': bool,
1158
	'auto_now_add': bool,
1159 1160 1161
	'upload_to': unicode,
	'path': unicode,
	'verify_exist': bool,
sayali's avatar
sayali committed
1162

1163
    #   raise issue y used
Kedar A.'s avatar
Kedar A. committed
1164
	'min_length': int,
1165 1166 1167 1168 1169 1170
	'required': bool,
	'label': unicode,
	'unique': bool,
	'validators': list,
	'default': unicode,
	'editable': bool
1171
    }
Nagarjuna's avatar
Nagarjuna committed
1172

1173
    required_fields = ['data_type', 'subject_type']
1174
    use_dot_notation = True
Nagarjuna's avatar
Nagarjuna committed
1175

sayali's avatar
sayali committed
1176 1177 1178 1179 1180
    # validators={
    # 'data_type':x in DATA_TYPE_CHOICES
    # 'data_type':lambda x: x in DATA_TYPE_CHOICES
    # }

1181 1182 1183
    ##########  User-Defined Functions ##########

    @staticmethod
1184
    def append_attribute(attr_id_or_node, attr_dict, attr_value=None, inner_attr_dict=None):
1185 1186 1187 1188 1189 1190

        from bson.dbref import DBRef
        if isinstance(attr_id_or_node, DBRef):
            attr_id_or_node = AttributeType(db.dereference(attr_id_or_node))

        elif isinstance(attr_id_or_node, (unicode, ObjectId)):
1191 1192 1193 1194
            # Convert unicode representation of ObjectId into it's
            # corresponding ObjectId type Then fetch
            # attribute-type-node from AttributeType collection of
            # respective ObjectId
1195
            if ObjectId.is_valid(attr_id_or_node):
1196
                attr_id_or_node = node_collection.one({'_type': 'AttributeType', '_id': ObjectId(attr_id_or_node)})
1197 1198 1199
            else:
                print "\n Invalid ObjectId: ", attr_id_or_node, " is not a valid ObjectId!!!\n"
                # Throw indicating the same
1200

1201
        if not attr_id_or_node.complex_data_type:
1202 1203
            # Code for simple data-type Simple data-types: int, float,
            # ObjectId, list, dict, basestring, unicode
1204
            if inner_attr_dict is not None:
1205 1206
                # If inner_attr_dict exists It means node should ne
                # added to this inner_attr_dict and not to attr_dict
1207
                if not (attr_id_or_node.name in inner_attr_dict):
1208 1209
                    # If inner_attr_dict[attr_id_or_node.name] key
                    # doesn't exists, then only add it!
1210
                    if attr_value is None:
1211 1212 1213 1214 1215
                        inner_attr_dict[attr_id_or_node.name] = {
                            'altnames': attr_id_or_node.altnames, '_id': attr_id_or_node._id,
                            'data_type': eval(attr_id_or_node.data_type),
                            'object_value': attr_value
                        }
1216
                    else:
1217 1218 1219 1220 1221 1222 1223
                        inner_attr_dict[attr_id_or_node.name] = {
                            'altnames': attr_id_or_node.altnames, '_id': attr_id_or_node._id,
                            'data_type': eval(attr_id_or_node.data_type),
                            'object_value': attr_value[attr_id_or_node.name]
                        }

                if attr_id_or_node.name in attr_dict:
1224 1225
                    # If this attribute-node exists in outer
                    # attr_dict, then remove it
1226 1227 1228 1229
                    del attr_dict[attr_id_or_node.name]

            else:
                # If inner_attr_dict is None
1230
                if not (attr_id_or_node.name in attr_dict):
1231 1232
                    # If attr_dict[attr_id_or_node.name] key doesn't
                    # exists, then only add it!
1233 1234 1235 1236 1237
                    attr_dict[attr_id_or_node.name] = {
                        'altnames': attr_id_or_node.altnames, '_id': attr_id_or_node._id,
                        'data_type': eval(attr_id_or_node.data_type),
                        'object_value': attr_value
                    }
1238 1239

        else:
1240
            # Code for complex data-type
1241 1242
            # Complex data-types: [...], {...}
            if attr_id_or_node.data_type == "dict":
1243
                if not (attr_id_or_node.name in attr_dict):
1244
                    inner_attr_dict = {}
1245

1246 1247
                    for c_attr_id in attr_id_or_node.complex_data_type:
                        # NOTE: Here c_attr_id is in unicode format
1248
                        # Hence, this function first converts attr_id
1249 1250
                        # to ObjectId format if unicode found
                        AttributeType.append_attribute(c_attr_id, attr_dict, attr_value, inner_attr_dict)
1251

1252
                    attr_dict[attr_id_or_node.name] = inner_attr_dict
1253

1254 1255
                else:
                    for remove_attr_name in attr_dict[attr_id_or_node.name].iterkeys():
1256
                        if remove_attr_name in attr_dict:
1257 1258
                            # If this attribute-node exists in outer
                            # attr_dict, then remove it
1259 1260
                            del attr_dict[remove_attr_name]

1261 1262 1263 1264 1265
            elif attr_id_or_node.data_type == "list":
                if len(attr_id_or_node.complex_data_type) == 1:
                    # Represents list of simple data-types
                    # Ex: [int], [ObjectId], etc.
                    dt = unicode("[" + attr_id_or_node.complex_data_type[0] + "]")
1266
                    if not (attr_id_or_node.name in attr_dict):
1267 1268
                        # If attr_dict[attr_id_or_node.name] key
                        # doesn't exists, then only add it!
1269 1270 1271 1272 1273 1274
                        attr_dict[attr_id_or_node.name] = {
                            'altnames': attr_id_or_node.altnames, '_id': attr_id_or_node._id,
                            'data_type': eval(dt),
                            'object_value': attr_value
                        }

1275
                else:
1276 1277
                    # Represents list of complex data-types Ex:
                    # [{...}]
1278 1279
                    for c_attr_id in attr_id_or_node.complex_data_type:
                        if not ObjectId.is_valid(c_attr_id):
1280 1281
                            # If basic data-type values are found,
                            # pass the iteration