def test_project_create_groups_and_permissions(self):

        test_project_name = "project"
        test_project_title = "Project"
        test_project_email = "*****@*****.**"

        project = create_project(
            name=test_project_name,
            title=test_project_title,
            email=test_project_email,
        )

        test_groups_data = [
            # there is a lil hack here...
            # I don't actually care about ids, but my assertDictEqual fn can ignore nested keys...
            # therefore I set the permission id to a FuzzyInt w/ enough range to handle likely cases...
            {'name': u"{0}_member".format(test_project_name), 'permissions':
                [
                    {'codename': u"view_{0}".format(test_project_name), 'name': u"view {0} instances".format(test_project_name), 'id': FuzzyInt(1, 100)}
                ]
            },
            {'name': u"{0}_admin".format(test_project_name), 'permissions':
                [
                    {'codename': u"customize_{0}".format(test_project_name), 'name': u'customize {0} instances'.format(test_project_name), 'id': FuzzyInt(1, 100)},
                    {'codename': u"edit_{0}".format(test_project_name), 'name': u'edit {0} instances'.format(test_project_name), 'id': FuzzyInt(1, 100)},
                    {'codename': u"view_{0}".format(test_project_name), 'name': u'view {0} instances'.format(test_project_name), 'id': FuzzyInt(1, 100)}
                ]
            },
            {'name': u'{0}_user'.format(test_project_name), 'permissions':
                [
                   {'codename': u'edit_{0}'.format(test_project_name), 'name': u'edit {0} instances'.format(test_project_name), 'id': FuzzyInt(1, 100)},
                   {'codename': u'view_{0}'.format(test_project_name), 'name': u'view {0} instances'.format(test_project_name), 'id': FuzzyInt(1, 100)}
                ]
            },
            {'name': u'{0}_pending'.format(test_project_name), 'permissions':
                [
                   {'codename': u'view_{0}'.format(test_project_name), 'name': u'view {0} instances'.format(test_project_name), 'id': FuzzyInt(1, 100)}
                ]
            }
        ]

        actual_groups_data = [
            serialize_model_to_dict(group, include={
                "permissions": [
                    serialize_model_to_dict(permission, exclude=["content_type"])
                    for permission in group.permissions.all()
                ]
            })
            for group in project.groups.all()
        ]

        for actual_group_data, test_group_data in zip(actual_groups_data, test_groups_data):
            self.assertDictEqual(actual_group_data, test_group_data, excluded_keys=["id"])
    def test_reset_model(self):

        model_proxy = self.test_ontology_schema.model_proxies.get(name="model")

        model_customization = QModelCustomization(
            project=self.test_project,
            ontology=self.test_ontology_schema,
            proxy=model_proxy,
        )
        model_customization.reset()

        actual_model_customization_data = serialize_model_to_dict(
            model_customization,
            exclude=["id", "guid", "created", "modified", "relationship_source_property_customization"]  # note that I don't bother to check the 'relationship_source_property_customization' b/c I only ever access the reverse of that relationship (tested below)
        )

        test_model_customization_data = {
            'is_default': False,
            'description': u'',
            'model_title': u'Model',
            'project': self.test_project.pk,
            'synchronization': [],
            'ontology': self.test_ontology_schema.pk,
            'proxy': model_proxy.pk,
            'model_description': u'',
            'model_show_all_categories': False,
            'order': 1,
            'name': u'',
            'owner': None,
            'shared_owners': [],
        }

        self.assertDictEqual(actual_model_customization_data, test_model_customization_data)
    def test_categorization_register(self):

        # (registering the ontology is tested elsewhere)
        self.test_ontology.register()

        self.assertFalse(self.test_categorization.is_registered)
        self.assertEqual(self.test_categorization.category_proxies.count(), 0)
        self.test_categorization.register()
        self.assertTrue(self.test_categorization.is_registered)
        self.assertEqual(self.test_categorization.category_proxies.count(), 3)

        test_category_proxies = self.test_categorization.category_proxies.all()

        actual_standard_category_proxies_data = [
            serialize_model_to_dict(standard_category_proxy, exclude=["id", "guid", "created", "modified"])
            for standard_category_proxy in test_category_proxies
        ]

        test_category_proxies_properties = [
            test_category_proxy.properties.values_list("pk", flat=True)
            for test_category_proxy in test_category_proxies
        ]

        test_standard_category_proxies_data = [
            {'properties': test_category_proxies_properties[0], 'categorization': self.test_categorization.pk, 'name': u'Category One', 'documentation': u'I am category one.', 'key': u'category-one', 'order': 1},
            {'properties': test_category_proxies_properties[1], 'categorization': self.test_categorization.pk, 'name': u'Category Two', 'documentation': u'I am category two.', 'key': u'category-two', 'order': 2},
            {'properties': test_category_proxies_properties[2], 'categorization': self.test_categorization.pk, 'name': u'Category Three', 'documentation': u'I am category three - I am unused.', 'key': u'category-three', 'order': 3},
        ]

        for actual_standard_category_proxy_data, test_standard_category_proxy_data in zip(actual_standard_category_proxies_data, test_standard_category_proxies_data):
            self.assertDictEqual(actual_standard_category_proxy_data, test_standard_category_proxy_data, excluded_keys=["properties"])
            self.assertItemsEqual(actual_standard_category_proxy_data["properties"], test_standard_category_proxy_data["properties"])  # using "assertItemsEqual" instead of "assertListEqual" b/c I don't care about order
    def test_ontology_register(self):

        self.assertFalse(self.test_ontology.is_registered)
        self.test_ontology.register()
        self.assertTrue(self.test_ontology.is_registered)

        test_model_proxies = self.test_ontology.model_proxies.all()
        test_property_proxies = QStandardPropertyProxy.objects.filter(model_proxy__ontology=self.test_ontology)

        actual_model_proxies_data = [
            serialize_model_to_dict(model_proxy, exclude=["id", "guid", "created", "modified"])
            for model_proxy in test_model_proxies
        ]

        test_model_proxies_data = [
            {'name': u'modelComponent', 'stereotype': u'document', 'package': None, 'documentation': u'A ModelCompnent is nice.', 'namespace': None, 'ontology': self.test_ontology.pk, 'order': 0},
            {'name': u'responsibleParty', 'stereotype': None, 'package': None, 'documentation': u'a stripped-down responsible party to use for testing purposes.', 'namespace': None, 'ontology': self.test_ontology.pk, 'order': 1},
            {'name': u'contactType', 'stereotype': None, 'package': None, 'documentation': u'a stripped-down contactType just for testing purposes.', 'namespace': None, 'ontology': self.test_ontology.pk, 'order': 2},
        ]

        for actual_model_proxy_data, test_model_proxy_data in zip(actual_model_proxies_data, test_model_proxies_data):
            self.assertDictEqual(actual_model_proxy_data, test_model_proxy_data)

        actual_property_proxies_data = [
            serialize_model_to_dict(property_proxy, exclude=["id", "guid", "created", "modified"])
            for property_proxy in test_property_proxies
        ]

        test_property_proxies_data = [
            {'field_type': u'ATOMIC', 'atomic_default': None, 'enumeration_nullable': False, 'name': u'string', 'stereotype': None, 'relationship_target_model': None, 'relationship_target_name': None, 'enumeration_choices': u'', 'documentation': u'I am a string', 'namespace': None, 'atomic_type': u'DEFAULT', 'order': 0, 'is_label': True, 'enumeration_open': False, 'cardinality': u'0|1', 'model_proxy': test_model_proxies[0].pk, 'enumeration_multi': False},
            {'field_type': u'ATOMIC', 'atomic_default': None, 'enumeration_nullable': False, 'name': u'boolean', 'stereotype': None, 'relationship_target_model': None, 'relationship_target_name': None, 'enumeration_choices': u'', 'documentation': u'I am a boolean', 'namespace': None, 'atomic_type': u'BOOLEAN', 'order': 1, 'is_label': False, 'enumeration_open': False, 'cardinality': u'0|1', 'model_proxy': test_model_proxies[0].pk, 'enumeration_multi': False},
            {'field_type': u'ATOMIC', 'atomic_default': None, 'enumeration_nullable': False, 'name': u'date', 'stereotype': None, 'relationship_target_model': None, 'relationship_target_name': None, 'enumeration_choices': u'', 'documentation': u'I am a date', 'namespace': None, 'atomic_type': u'DATE', 'order': 2, 'is_label': False, 'enumeration_open': False, 'cardinality': u'0|1', 'model_proxy': test_model_proxies[0].pk, 'enumeration_multi': False},
            {'field_type': u'ATOMIC', 'atomic_default': None, 'enumeration_nullable': False, 'name': u'uncategorized', 'stereotype': None, 'relationship_target_model': None, 'relationship_target_name': None, 'enumeration_choices': u'', 'documentation': u'I am an uncategorized string', 'namespace': None, 'atomic_type': u'DEFAULT', 'order': 3, 'is_label': False, 'enumeration_open': False, 'cardinality': u'0|1', 'model_proxy': test_model_proxies[0].pk, 'enumeration_multi': False},
            {'field_type': u'ENUMERATION', 'atomic_default': None, 'enumeration_nullable': False, 'name': u'enumeration', 'stereotype': None, 'relationship_target_model': None, 'relationship_target_name': None, 'enumeration_choices': u'one|two|three', 'documentation': u'I am an enumreation', 'namespace': None, 'atomic_type': None, 'order': 4, 'is_label': False, 'enumeration_open': False, 'cardinality': u'0|1', 'model_proxy': test_model_proxies[0].pk, 'enumeration_multi': False},
            {'field_type': u'RELATIONSHIP', 'atomic_default': None, 'enumeration_nullable': False, 'name': u'author', 'stereotype': None, 'relationship_target_model': 2, 'relationship_target_name': u'responsibleParty', 'enumeration_choices': u'', 'documentation': u'I am a relationship', 'namespace': None, 'atomic_type': None, 'order': 5, 'is_label': False, 'enumeration_open': False, 'cardinality': u'0|1', 'model_proxy': test_model_proxies[0].pk, 'enumeration_multi': False},
            {'field_type': u'RELATIONSHIP', 'atomic_default': None, 'enumeration_nullable': False, 'name': u'contact', 'stereotype': None, 'relationship_target_model': 2, 'relationship_target_name': u'responsibleParty', 'enumeration_choices': u'', 'documentation': u'I am a relationship', 'namespace': None, 'atomic_type': None, 'order': 6, 'is_label': False, 'enumeration_open': False, 'cardinality': u'0|*', 'model_proxy': test_model_proxies[0].pk, 'enumeration_multi': False},
            {'field_type': u'ATOMIC', 'atomic_default': None, 'enumeration_nullable': False, 'name': u'individualName', 'stereotype': None, 'relationship_target_model': None, 'relationship_target_name': None, 'enumeration_choices': u'', 'documentation': u'', 'namespace': None, 'atomic_type': u'DEFAULT', 'order': 0, 'is_label': True, 'enumeration_open': False, 'cardinality': u'0|1', 'model_proxy': test_model_proxies[1].pk, 'enumeration_multi': False},
            {'field_type': u'RELATIONSHIP', 'atomic_default': None, 'enumeration_nullable': False, 'name': u'contactInfo', 'stereotype': None, 'relationship_target_model': 3, 'relationship_target_name': u'contactType', 'enumeration_choices': u'', 'documentation': u'', 'namespace': None, 'atomic_type': None, 'order': 1, 'is_label': False, 'enumeration_open': False, 'cardinality': u'1|1', 'model_proxy': test_model_proxies[1].pk, 'enumeration_multi': False},
            {'field_type': u'ATOMIC', 'atomic_default': None, 'enumeration_nullable': False, 'name': u'phone', 'stereotype': None, 'relationship_target_model': None, 'relationship_target_name': None, 'enumeration_choices': u'', 'documentation': u'', 'namespace': None, 'atomic_type': u'DEFAULT', 'order': 0, 'is_label': False, 'enumeration_open': False, 'cardinality': u'0|1', 'model_proxy': test_model_proxies[2].pk, 'enumeration_multi': False},
            {'field_type': u'ATOMIC', 'atomic_default': None, 'enumeration_nullable': False, 'name': u'address', 'stereotype': None, 'relationship_target_model': None, 'relationship_target_name': None, 'enumeration_choices': u'', 'documentation': u'', 'namespace': None, 'atomic_type': u'TEXT', 'order': 1, 'is_label': False, 'enumeration_open': False, 'cardinality': u'0|1', 'model_proxy': test_model_proxies[2].pk, 'enumeration_multi': False},
        ]

        for actual_property_proxy_data, test_property_proxy_data in zip(actual_property_proxies_data, test_property_proxies_data):
            self.assertDictEqual(actual_property_proxy_data, test_property_proxy_data)
    def test_categorization_register(self):

        # (registering the ontology is tested elsewhere)
        self.test_ontology_schema.register()

        self.assertFalse(self.test_categorization.is_registered)
        self.assertEqual(self.test_categorization.category_proxies.count(), 0)
        self.test_categorization.register()
        self.assertTrue(self.test_categorization.is_registered)
        self.assertEqual(self.test_categorization.category_proxies.count(), 4)  # 3 defined in categorization, plus "uncategorized" category

        category_proxies = self.test_categorization.category_proxies.all()

        actual_category_proxies_data = [
            dict(
                serialize_model_to_dict(
                    category_proxy,
                    exclude=["id", "guid", "created", "modified"]
                ),
                property_proxies=category_proxy.property_proxies.values_list("pk", flat=True)
            )
            for category_proxy in category_proxies
        ]

        test_category_proxies_properties_data = [
            [QPropertyProxy.objects.get(model_proxy__ontology=self.test_ontology_schema, name__iexact="name", model_proxy__name__iexact="model").pk, QPropertyProxy.objects.get(model_proxy__ontology=self.test_ontology_schema, name__iexact="name", model_proxy__name__iexact="recursive_thing").pk],
            [QPropertyProxy.objects.get(model_proxy__ontology=self.test_ontology_schema, name__iexact="enumeration", model_proxy__name__iexact="model").pk, QPropertyProxy.objects.get(model_proxy__ontology=self.test_ontology_schema, name__iexact="child", model_proxy__name__iexact="recursive_thing").pk],
            [QPropertyProxy.objects.get(model_proxy__ontology=self.test_ontology_schema, name__iexact="thing", model_proxy__name__iexact="model").pk, QPropertyProxy.objects.get(model_proxy__ontology=self.test_ontology_schema, name__iexact="multiple_targets", model_proxy__name__iexact="recursive_thing").pk],
            [QPropertyProxy.objects.get(model_proxy__ontology=self.test_ontology_schema, name__iexact="name", model_proxy__name__iexact="other_thing_one").pk, QPropertyProxy.objects.get(model_proxy__ontology=self.test_ontology_schema, name__iexact="name", model_proxy__name__iexact="other_thing_two").pk],
        ]

        test_category_proxies_data = sort_list_by_key(
            [
                {'is_specialized': False, 'property_proxies': test_category_proxies_properties_data[0], 'categorization': self.test_categorization.pk, 'name': u'Category One', 'documentation': u'I am category one.', 'order': 1},
                {'is_specialized': False, 'property_proxies': test_category_proxies_properties_data[1], 'categorization': self.test_categorization.pk, 'name': u'Category Two', 'documentation': u'I am category two.', 'order': 2},
                {'is_specialized': False, 'property_proxies': test_category_proxies_properties_data[2], 'categorization': self.test_categorization.pk, 'name': u'Category Three', 'documentation': u'I am category three.', 'order': 3},
                {'is_specialized': False, 'property_proxies': test_category_proxies_properties_data[3], 'categorization': self.test_categorization.pk, 'name': UNCATEGORIZED_NAME, 'documentation': UNCATEGORIZED_DOCUMENTATION, 'order': 4},
            ],
            "order"
        )

        for actual_category_proxy_data, test_category_proxy_data in zip(actual_category_proxies_data, test_category_proxies_data):
            self.assertDictEqual(actual_category_proxy_data, test_category_proxy_data, excluded_keys=["property_proxies"])
            self.assertItemsEqual(actual_category_proxy_data["property_proxies"], test_category_proxy_data["property_proxies"])  # using "assertItemsEqual" instead of "assertListEqual" b/c I don't care about order
 def get_supported_document_types(self, obj):
     """
     returns the model_proxies of the current ontology that can be used to create documents
     ie: those w/ the stereotype "document" and that are listed in SUPPORTED_DOCUMENTS
     :param obj:
     :return:
     """
     supported_document_model_proxies = obj.model_proxies.filter(
         is_document=True,
         name__iregex=r'(' + '|'.join(["^{0}$".format(sd) for sd in SUPPORTED_DOCUMENTS["CIM2"]]) + ')',
     ).order_by("name")
     return [
         serialize_model_to_dict(
             model_proxy,
             include={
                 "title": str(model_proxy),
                 "name": model_proxy.name.lower()
             },
             exclude=["guid", "created", "modified", "ontology"]
         )
         for model_proxy in supported_document_model_proxies
     ]
def serialize_customization_set(customization_set):
    """
    need a special fn to cope w/ this
    b/c it is likely that the customization_set will need to be serialized before it has been saved
    therefore the m2m fields will not yet exist in the db
    the workflow goes:
    * get_new_customization_set where calls to create are wrapped in "allow_unsaved_fk"
    * that customization_set gets cached in the current session
    * AJAX calls to the RESTful API access that cached customization_set
    * which needs to be serialized via this fn and then passed as data to QModelCustomizationSerializer
    * that fn uses my custom "serialize_model_to_dict" fn which correctly handles m2m fields
    :param model_customization:
    :return:
    """

    # get model customization stuff...
    serialization = OrderedDict(
        serialize_model_to_dict(
            customization_set["model_customization"],
            include={},
            exclude=QCUSTOMIZATION_NON_EDITABLE_FIELDS + ["synchronization"],
        )
    )

    # add vocabulary customization stuff...
    serialization["vocabularies"] = [
        serialize_model_to_dict(
            vc,
            include={
                "display_detail": i == 0,  # by default, display the 1st vocabulary in the list
                "vocabulary_name": str(vc.vocabulary),
                "vocabulary_key": vc.vocabulary.get_key(),
                "components": [
                    serialize_model_to_dict(
                        component,
                        include={
                            "key": component.get_key(),
                            "num_properties": component.scientific_property_proxies.count(),
                        },
                        exclude=MPTT_FIELDS + QCUSTOMIZATION_NON_EDITABLE_FIELDS + ["id", "vocabulary"],
                    )
                    for component in vc.vocabulary.component_proxies.all()
                ],
            },
            exclude=QCUSTOMIZATION_NON_EDITABLE_FIELDS,
        )
        for i, vc in enumerate(customization_set["vocabulary_customizations"])
    ]

    # add standard categories...
    serialization["standard_categories"] = [
        serialize_model_to_dict(
            sc,
            include={"key": sc.get_key(), "display_properties": True, "display_detail": False},
            exclude=QCUSTOMIZATION_NON_EDITABLE_FIELDS,
        )
        for sc in customization_set["standard_category_customizations"]
    ]

    # add standard properties...
    # (not using a list comprehension for standard_properties b/c of the complexities of subforms)
    serialization["standard_properties"] = []
    for sp in customization_set["standard_property_customizations"]:
        if sp.relationship_show_subform:
            # I am working w/ the full customization_set even though the field is only bound to the model_customization
            subform_serialization = serialize_customization_set(sp.relationship_subform_customization_set)
        else:
            subform_serialization = OrderedDict({})
        category = sp.category
        serialization["standard_properties"].append(
            serialize_model_to_dict(
                sp,
                include={
                    "key": sp.get_key(),
                    # not all properties of subforms are categorized
                    # (that's okay for now, since I don't display the category widget in subforms anyway)
                    "category_key": category.get_key() if category else None,
                    "display_detail": False,
                    "enumeration_choices": sp.get_enumeration_choices_value(),
                    "relationship_subform_customization": subform_serialization,
                },
                exclude=QCUSTOMIZATION_NON_EDITABLE_FIELDS,
            )
        )

    # add scientific categories...
    serialization["scientific_categories"] = [
        serialize_model_to_dict(
            sc,
            include={"key": sc.get_key(), "display_properties": True, "display_detail": False},
            exclude=QCUSTOMIZATION_NON_EDITABLE_FIELDS,
        )
        for sc in customization_set["scientific_category_customizations"]
    ]

    # and scientific properties...
    serialization["scientific_properties"] = [
        serialize_model_to_dict(
            sp,
            include={"key": sp.get_key(), "category_key": sp.category.get_key(), "display_detail": False},
            exclude=QCUSTOMIZATION_NON_EDITABLE_FIELDS,
        )
        for sp in customization_set["scientific_property_customizations"]
    ]

    return serialization
def serialize_realizations(current_model_realization, **kwargs):
    """
    need a special fn to cope w/ this
    b/c it is likely that these realizations will need to be serialized before they have been saved
    therefore the m2m fields will not yet exist in the db
    the workflow goes:
    * get_new_realizations where calls to create are wrapped in "allow_unsaved_fk" & custom "QUnsavedRelatedManager"
    * those realizations get cached in the current session
    * AJAX calls the RESTful API to access those cached realizations
    * which needs to be serialized via this fn and then passed as data to QModelRealizationSerializer
    :param current_model_realization
    :return: OrderedDict
    """

    # get the model stuff...
    model_serialization = serialize_model_to_dict(
        current_model_realization,
        include={
            "key": current_model_realization.key,
            "is_meta": current_model_realization.is_meta,
            # "version": current_model_realization.version.fully_specified(),
            "title": current_model_realization.title,
            "is_selected": False,
            "display_detail": False,
        },
        exclude=["guid", "created", "modified", "synchronization"]
    )

    # and the categories stuff...
    category_serializations = []
    for category_realization in current_model_realization.categories(manager="allow_unsaved_categories_manager").all():
        category_serialization = serialize_model_to_dict(
            category_realization,
            include={
                "key": category_realization.key,
                "is_uncategorized": category_realization.is_uncategorized,
                "properties_keys": category_realization.get_properties_keys(),
                "display_detail": True,
            },
            exclude=["guid", "created", "modified"]
        )
        category_serializations.append(category_serialization)

    # and the properties stuff...
    property_serializations = []
    for property_realization in current_model_realization.properties(manager="allow_unsaved_properties_manager").all():
        property_serialization = serialize_model_to_dict(
            property_realization,
            include={
                "relationship_references": create_empty_reference_list_serialization(),  # just a placeholder
                "key": property_realization.key,
                "is_meta": property_realization.is_meta,
                "is_hierarchical": property_realization.is_hierarchical,
                "cardinality_min": property_realization.cardinality_min,
                "cardinality_max": property_realization.cardinality_max,
                "is_multiple": property_realization.is_multiple,
                "is_infinite": property_realization.is_infinite,
                "possible_relationship_target_types": property_realization.get_potential_relationship_target_types(),
                "category_key": property_realization.category_key,
                "display_detail": True,
            },
            exclude=["guid", "created", "modified"]
        )
        # here begins the icky bit
        target_model_serializations = []
        if property_realization.field_type == QPropertyTypes.RELATIONSHIP:
            target_model_realizations = property_realization.relationship_values(manager="allow_unsaved_relationship_values_manager").all()
            for target_model_realization in target_model_realizations:
                target_model_serialization = serialize_realizations(target_model_realization)
                target_model_serializations.append(target_model_serialization)
        property_serialization["relationship_values"] = target_model_serializations
        # here ends the icky bit

        property_serializations.append(property_serialization)

    # and put it all together...
    serialization = OrderedDict(model_serialization)
    serialization["categories"] = category_serializations
    serialization["properties"] = property_serializations

    return serialization
示例#9
0
def serialize_new_customizations(current_model_customization, **kwargs):
    """
    need a special fn to cope w/ this
    b/c getting DRF to work w/ potentially infinite recursion is impossible
    it is likely that these customizations will need to be serialized before they have been saved

    therefore the m2m fields will not yet exist in the db
    the workflow goes:
    * get_new_customizations where calls to create are wrapped in "allow_unsaved_fk" & custom "QUnsavedRelatedManager" are used
    * those customizations get cached in the current session
    * AJAX calls to the RESTful API access those cached customizations
    * which needs to be serialized via this fn and then passed as data to QModelCustomizationSerializer
    :param customizations
    :return: OrderedDict
    """
    previously_serialized_customizations = kwargs.pop("previously_serialized_customizations", {})
    prefix = kwargs.pop("prefix", None)

    # get model customization stuff...
    model_customization_key = current_model_customization.get_fully_qualified_key(prefix=prefix)
    if model_customization_key not in previously_serialized_customizations:
        model_customization_serialization = serialize_model_to_dict(
            current_model_customization,
            include={
                "key": current_model_customization.get_key(),
                "proxy_name": str(current_model_customization.proxy),
                "display_detail": False,
            },
            exclude=QCUSTOMIZATION_NON_EDITABLE_FIELDS + ["synchronization", ]
        )
        previously_serialized_customizations[model_customization_key] = model_customization_serialization
    else:
        model_customization_serialization = previously_serialized_customizations[model_customization_key]

    # and the categories stuff...
    category_customization_serializations = []
    for category_customization in current_model_customization.category_customizations(manager="allow_unsaved_category_customizations_manager").all():
        category_customization_key = category_customization.get_fully_qualified_key(prefix=prefix)
        if category_customization_key not in previously_serialized_customizations:
            category_customization_serialization = serialize_model_to_dict(
                category_customization,
                include={
                    "key": category_customization.get_key(),
                    "num_properties": category_customization.property_customizations(manager="allow_unsaved_categories_manager").count(),
                    "proxy_name": str(category_customization.proxy),
                    "display_properties": True,
                    "display_detail": False,
                },
                exclude=QCUSTOMIZATION_NON_EDITABLE_FIELDS
            )
            previously_serialized_customizations[category_customization_key] = category_customization_serialization
        else:
            category_customization_serialization = previously_serialized_customizations[category_customization_key]
        category_customization_serializations.append(category_customization_serialization)

    # and the properties stuff...
    property_customization_serializations = []
    for property_customization in current_model_customization.property_customizations(manager="allow_unsaved_property_customizations_manager").all():
        property_customization_key = property_customization.get_fully_qualified_key(prefix=prefix)
        if property_customization_key not in previously_serialized_customizations:
            use_subforms = property_customization.use_subforms()
            category_customization = property_customization.category
            property_customization_serialization = serialize_model_to_dict(
                property_customization,
                include={
                    "key": property_customization.get_key(),
                    "category_key": category_customization.get_key(),
                    "proxy_name": str(property_customization.proxy),
                    "display_detail": False,
                    # "enumeration_choices": standard_property_customization.get_enumeration_choices_value(),
                    "use_subforms": use_subforms,
                },
                exclude=QCUSTOMIZATION_NON_EDITABLE_FIELDS
            )

            ############################
            # here begins the icky bit #
            ############################

            subform_customizations_serializations = []
            if use_subforms:
                subform_prefix = property_customization.get_fully_qualified_key()  # note I do _not_ pass the prefix kwarg
                for subform_model_customization in property_customization.relationship_target_model_customizations(manager="allow_unsaved_relationship_target_model_customizations_manager").all():
                    subform_model_customization_key = subform_model_customization.get_fully_qualified_key(prefix=subform_prefix)
                    if subform_model_customization_key not in previously_serialized_customizations:
                        subform_customizations_serialization = serialize_new_customizations(
                            subform_model_customization,
                            previously_serialized_customizations=previously_serialized_customizations,
                            prefix=subform_prefix,
                        )
                        previously_serialized_customizations[subform_model_customization_key] = subform_customizations_serialization
                    else:
                        subform_customizations_serialization = previously_serialized_customizations[subform_model_customization_key]
                    subform_customizations_serializations.append(subform_customizations_serialization)
            property_customization_serialization["relationship_target_model_customizations"] = subform_customizations_serializations

            ##########################
            # here ends the icky bit #
            ##########################

        else:
            property_customization_serialization = previously_serialized_customizations[property_customization_key]
        property_customization_serializations.append(property_customization_serialization)

    # and put it all together...
    serialization = OrderedDict(model_customization_serialization)
    serialization["categories"] = category_customization_serializations
    serialization["properties"] = property_customization_serializations

    return serialization
    def test_reset_property(self):

        model_proxy = self.test_ontology_schema.model_proxies.get(name="model")

        atomic_property_proxy = QPropertyProxy.objects.get(name__iexact="name", model_proxy=model_proxy)
        enumeration_property_proxy = QPropertyProxy.objects.get(name__iexact="enumeration", model_proxy=model_proxy)
        relationship_property_proxy = QPropertyProxy.objects.get(name__iexact="thing", model_proxy=model_proxy)

        uncategorized_category = QCategoryProxy.objects.get(categorization=self.test_categorization, name=UNCATEGORIZED_NAME)

        test_model_customization = QModelCustomization(
            project=self.test_project,
            proxy=model_proxy,
        )
        test_model_customization.reset()

        with allow_unsaved_fk(QCategoryCustomization, ["model_customization"]):
            test_category_customization = QCategoryCustomization(
                proxy=QCategoryProxy.objects.get(pk=1),  # don't actually care which category I use for testing
                model_customization=test_model_customization,
            )
            test_category_customization.reset()

        with allow_unsaved_fk(QPropertyCustomization, ["model_customization", "category"]):

            # testing ATOMIC properties...

            atomic_property_customization = QPropertyCustomization(
                proxy=atomic_property_proxy,
                model_customization=test_model_customization,
                category=test_category_customization,
            )
            atomic_property_customization.reset()

            actual_atomic_property_customization_data = serialize_model_to_dict(
                atomic_property_customization,
                exclude=["id", "guid", "created", "modified"],
                include={
                    "relationship_target_model_customizations": atomic_property_customization.relationship_target_model_customizations.all(),
                }
            )

            test_atomic_property_customization_data = {
                'name': u'',
                'field_type': u'ATOMIC',
                'is_nillable': False,
                'documentation': u'',
                'relationship_show_subform': False,
                'atomic_type': u'DEFAULT',
                'enumeration_open': False,
                'relationship_target_model_customizations': [],
                # 'model_customization': ?!?,
                'proxy': atomic_property_proxy.pk,
                'inline_help': False,
                'is_hidden': False,
                'is_editable': True,
                'is_required': True,
                'atomic_default': None,
                'order': 1,
                'property_title': 'Name',
                'atomic_suggestions': ''
            }

            self.assertDictEqual(actual_atomic_property_customization_data, test_atomic_property_customization_data, excluded_keys=["category", "model_customization"])
            self.assertEqual(atomic_property_customization.model_customization, test_model_customization)
            self.assertEqual(atomic_property_customization.category, test_category_customization)

            # testing ENUMERATION properties...

            enumeration_property_customization = QPropertyCustomization(
                proxy=enumeration_property_proxy,
                model_customization=test_model_customization,
                category=test_category_customization,
            )
            enumeration_property_customization.reset()

            actual_enumeration_property_customization_data = serialize_model_to_dict(
                enumeration_property_customization,
                exclude=["id", "guid", "created", "modified"],
                include={
                    "relationship_target_model_customizations": atomic_property_customization.relationship_target_model_customizations.all()
                }
            )

            test_enumeration_property_customization_data = {
                'name': u'',
                'field_type': u'ENUMERATION',
                'is_nillable': True,
                'documentation': u'A test enumeration.',
                'relationship_show_subform': False,
                'atomic_type': 'DEFAULT',
                'enumeration_open': False,
                'relationship_target_model_customizations': [],
                # 'model_customization': ?!?,
                'proxy': enumeration_property_proxy.pk,
                'inline_help': False,
                'is_hidden': False,
                'is_editable': True,
                'is_required': False,
                'atomic_default': None,
                'order': 2,
                'property_title': u'Enumeration',
                'atomic_suggestions': None
            }

            self.assertDictEqual(actual_enumeration_property_customization_data, test_enumeration_property_customization_data, excluded_keys=["category", "model_customization"])
            self.assertEqual(enumeration_property_customization.model_customization, test_model_customization)
            self.assertEqual(enumeration_property_customization.category, test_category_customization)

            # testing RELATIONSHIP properties...

            relationship_property_customization = QPropertyCustomization(
                proxy=relationship_property_proxy,
                model_customization=test_model_customization,
                category=test_category_customization,
            )
            relationship_property_customization.reset()

            actual_relationship_property_customization_data = serialize_model_to_dict(
                relationship_property_customization,
                exclude=["id", "guid", "created", "modified"],
                include={
                    "relationship_target_model_customizations": atomic_property_customization.relationship_target_model_customizations.all(),
                }
            )

            test_relationship_property_customization_data = {
                'name': u'',
                'field_type': u'RELATIONSHIP',
                'is_nillable': True,
                'documentation': u'A relationship property; there are lots of spaces in this documentation.',
                'relationship_show_subform': True,
                'atomic_type': 'DEFAULT',
                'atomic_suggestions': None,
                'enumeration_open': False,
                'relationship_target_model_customizations': [],
                # 'model_customization': ?!?,
                'proxy': relationship_property_proxy.pk,
                'inline_help': False,
                'is_hidden': False,
                'is_editable': True,
                'is_required': False,
                'atomic_default': None,
                'order': 3,
                'property_title': u'Thing',
            }

            self.assertDictEqual(actual_relationship_property_customization_data, test_relationship_property_customization_data, excluded_keys=["category", "model_customization"])
            self.assertEqual(relationship_property_customization.model_customization, test_model_customization)
            self.assertEqual(relationship_property_customization.category, test_category_customization)
    def test_ontology_register(self):

        self.assertFalse(self.test_ontology_schema.is_registered)
        self.assertIsNone(self.test_ontology_schema.last_registered_version)
        self.test_ontology_schema.register()
        self.assertTrue(self.test_ontology_schema.is_registered)
        self.assertEqual(self.test_ontology_schema.last_registered_version, self.test_ontology_schema.version)

        test_model_proxies = self.test_ontology_schema.model_proxies.all()
        test_category_proxies = QCategoryProxy.objects.filter(model_proxy__ontology=self.test_ontology_schema)
        test_property_proxies = QPropertyProxy.objects.filter(model_proxy__ontology=self.test_ontology_schema)

        actual_model_proxies_data = [
            serialize_model_to_dict(model_proxy, exclude=["id", "guid", "created", "modified"])
            for model_proxy in test_model_proxies
        ]

        test_model_proxies_data = sort_sequence_by_key(
            [
                {'name': u'model', 'package': u'test_package', 'cim_id': '1', 'documentation': u'this is a test model', 'label': None,
                 'is_document': True, 'ontology': self.test_ontology_schema.pk, 'order': 1, 'is_meta': False},
                {'name': u'recursive_thing', 'package': u'test_package', 'cim_id': '2', 'documentation': None, 'label': None,
                 'is_document': False, 'ontology': self.test_ontology_schema.pk, 'order': 2, 'is_meta': False},
                {'name': u'other_thing_one', 'package': u'test_package', 'cim_id': '3', 'documentation': None, 'label': None,
                 'is_document': False, 'ontology': self.test_ontology_schema.pk, 'order': 3, 'is_meta': False},
                {'name': u'other_thing_two', 'package': u'test_package', 'cim_id': '4', 'documentation': None, 'label': None,
                 'is_document': False, 'ontology': self.test_ontology_schema.pk, 'order': 4, 'is_meta': False},
            ],
            "order"
        )

        for actual_model_proxy_data, test_model_proxy_data in zip(actual_model_proxies_data, test_model_proxies_data):
            self.assertDictEqual(actual_model_proxy_data, test_model_proxy_data)

        actual_category_proxies_data = [
            serialize_model_to_dict(category_proxy, exclude=["id", "guid", "created", "modified"])
            for category_proxy in test_category_proxies
        ]

        test_category_proxies_data = sort_sequence_by_key(
            [
                {'name': UNCATEGORIZED_CATEGORY_PROXY_NAME, 'package': UNCATEGORIZED_CATEGORY_PROXY_PACKAGE, 'cim_id': None,
                 'documentation': None, 'order': 1, 'model_proxy': 1, 'is_meta': False},
                {'name': UNCATEGORIZED_CATEGORY_PROXY_NAME, 'package': UNCATEGORIZED_CATEGORY_PROXY_PACKAGE, 'cim_id': None,
                 'documentation': None, 'order': 1, 'model_proxy': 2, 'is_meta': False},
                {'name': UNCATEGORIZED_CATEGORY_PROXY_NAME, 'package': UNCATEGORIZED_CATEGORY_PROXY_PACKAGE, 'cim_id': None,
                 'documentation': None, 'order': 1, 'model_proxy': 3, 'is_meta': False},
                {'name': UNCATEGORIZED_CATEGORY_PROXY_NAME, 'package': UNCATEGORIZED_CATEGORY_PROXY_PACKAGE, 'cim_id': None,
                 'documentation': None, 'order': 1, 'model_proxy': 4, 'is_meta': False},
            ],
            "order"
        )

        for actual_category_proxy_data, test_category_proxy_data in zip(actual_category_proxies_data, test_category_proxies_data):
            self.assertDictEqual(actual_category_proxy_data, test_category_proxy_data, excluded_keys=["model_proxy"])
        self.assertEqual(test_model_proxies.count(), test_category_proxies.filter(package=UNCATEGORIZED_CATEGORY_PROXY_PACKAGE, name=UNCATEGORIZED_CATEGORY_PROXY_NAME).count())

        actual_property_proxies_data = [
            serialize_model_to_dict(property_proxy, exclude=["id", "guid", "created", "modified"])
            for property_proxy in test_property_proxies
        ]

        model_model_proxy = test_model_proxies.get(name__iexact="model")
        recursive_thing_model_proxy = test_model_proxies.get(name__iexact="recursive_thing")
        other_thing_one_model_proxy = test_model_proxies.get(name__iexact="other_thing_one")
        other_thing_two_model_proxy = test_model_proxies.get(name__iexact="other_thing_two")
        model_model_proxy_uncategorized_category_proxy = model_model_proxy.category_proxies.get(
            package=UNCATEGORIZED_CATEGORY_PROXY_PACKAGE, name=UNCATEGORIZED_CATEGORY_PROXY_NAME
        )
        recursive_thing_model_proxy_uncategorized_category_proxy = recursive_thing_model_proxy.category_proxies.get(
            package=UNCATEGORIZED_CATEGORY_PROXY_PACKAGE, name=UNCATEGORIZED_CATEGORY_PROXY_NAME
        )
        other_thing_one_model_proxy_uncategorized_category_proxy = other_thing_one_model_proxy.category_proxies.get(
            package=UNCATEGORIZED_CATEGORY_PROXY_PACKAGE, name=UNCATEGORIZED_CATEGORY_PROXY_NAME
        )
        other_thing_two_model_proxy_uncategorized_category_proxy = other_thing_two_model_proxy.category_proxies.get(
            package=UNCATEGORIZED_CATEGORY_PROXY_PACKAGE, name=UNCATEGORIZED_CATEGORY_PROXY_NAME
        )

        test_property_proxies_data = \
            [

                {'category_proxy': model_model_proxy_uncategorized_category_proxy.pk, 'is_nillable': True, 'relationship_target_models': [], 'name': 'name',
                 'package': 'test_package', 'enumeration_choices': None, 'documentation': None, 'atomic_type': 'DEFAULT', 'cim_id': "1.1",
                 'order': 1, 'enumeration_is_open': False, 'cardinality_max': '1', 'relationship_target_names': None,
                 'cardinality_min': '1', 'field_type': 'ATOMIC', 'model_proxy': model_model_proxy.pk, 'is_meta': False},
                {'category_proxy': model_model_proxy_uncategorized_category_proxy.pk, 'is_nillable': True, 'relationship_target_models': [], 'name': 'enumeration',
                 'package': 'test_package', 'cim_id': "1.2",
                 'enumeration_choices': [{u'documentation': u'documentation for one', u'order': 1, u'value': u'one'},
                                         {u'documentation': u'documentation for two', u'order': 2, u'value': u'two'},
                                         {u'documentation': u'documentation for three', u'order': 3, u'value': u'three'}],
                 'documentation': 'this is a test enumeration', 'atomic_type': None, 'order': 2, 'enumeration_is_open': True,
                 'cardinality_max': '1', 'relationship_target_names': None, 'cardinality_min': '0',
                 'field_type': 'ENUMERATION', 'model_proxy': model_model_proxy.pk, 'is_meta': False},
                {'category_proxy': model_model_proxy_uncategorized_category_proxy.pk, 'is_nillable': True, 'relationship_target_models': [2], 'name': 'thing',
                 'package': 'test_package', 'enumeration_choices': None, 'cim_id': '1.3',
                 'documentation': 'a relationship property; there are lots of spaces in this documentation',
                 'atomic_type': None, 'order': 3, 'enumeration_is_open': False, 'cardinality_max': '1',
                 'relationship_target_names': ['test_package.recursive_thing'], 'cardinality_min': '0',
                 'field_type': 'RELATIONSHIP', 'model_proxy': model_model_proxy.pk, 'is_meta': False},
                {'category_proxy': recursive_thing_model_proxy_uncategorized_category_proxy.pk, 'is_nillable': True, 'relationship_target_models': [], 'name': 'name',
                 'package': 'test_package', 'enumeration_choices': None, 'documentation': None, 'atomic_type': 'DEFAULT', 'cim_id': '2.1',
                 'order': 1, 'enumeration_is_open': False, 'cardinality_max': '1', 'relationship_target_names': None,
                 'cardinality_min': '1', 'field_type': 'ATOMIC', 'model_proxy': recursive_thing_model_proxy.pk, 'is_meta': False},
                {'category_proxy': recursive_thing_model_proxy_uncategorized_category_proxy.pk, 'is_nillable': True, 'relationship_target_models': [recursive_thing_model_proxy.pk], 'name': 'child',
                 'package': 'test_package', 'enumeration_choices': None, 'documentation': None, 'atomic_type': None, 'cim_id': '2.2',
                 'order': 2, 'enumeration_is_open': False, 'cardinality_max': 'N',
                 'relationship_target_names': ['test_package.recursive_thing'], 'cardinality_min': '0',
                 'field_type': 'RELATIONSHIP', 'model_proxy': recursive_thing_model_proxy.pk, 'is_meta': False},
                {'category_proxy': recursive_thing_model_proxy_uncategorized_category_proxy.pk, 'is_nillable': True, 'relationship_target_models': [other_thing_one_model_proxy.pk, other_thing_two_model_proxy.pk], 'name': 'multiple_targets',
                 'package': 'test_package', 'enumeration_choices': None, 'documentation': None, 'atomic_type': None, 'cim_id': '2.3',
                 'order': 3, 'enumeration_is_open': False, 'cardinality_max': '1',
                 'relationship_target_names': ['test_package.other_thing_one', 'test_package.other_thing_two'],
                 'cardinality_min': '0', 'field_type': 'RELATIONSHIP', 'model_proxy': recursive_thing_model_proxy.pk, 'is_meta': False},
                {'category_proxy': other_thing_one_model_proxy_uncategorized_category_proxy.pk, 'is_nillable': True, 'relationship_target_models': [], 'name': 'name',
                 'package': 'test_package', 'enumeration_choices': None, 'documentation': None, 'atomic_type': 'DEFAULT', 'cim_id': '3.1',
                 'order': 1, 'enumeration_is_open': False, 'cardinality_max': '1', 'relationship_target_names': None,
                 'cardinality_min': '1', 'field_type': 'ATOMIC', 'model_proxy': other_thing_one_model_proxy.pk, 'is_meta': False},
                {'category_proxy': other_thing_two_model_proxy_uncategorized_category_proxy.pk, 'is_nillable': True, 'relationship_target_models': [], 'name': 'name',
                 'package': 'test_package', 'enumeration_choices': None, 'documentation': None, 'atomic_type': 'DEFAULT', 'cim_id': '4.1',
                 'order': 1, 'enumeration_is_open': False, 'cardinality_max': '1', 'relationship_target_names': None,
                 'cardinality_min': '1', 'field_type': 'ATOMIC', 'model_proxy': other_thing_two_model_proxy.pk, 'is_meta': False},
            ]

        for actual_property_proxy_data, test_property_proxy_data in zip(actual_property_proxies_data, test_property_proxies_data):
            self.assertDictEqual(actual_property_proxy_data, test_property_proxy_data)