def get_property_customization_by_fn(fn, current_model_customization): """ returns the 1st matching QPropertyCustomization in the customization hierarchy... if the top-level property_customization matches, this is simple if the cusotmizations are existing (ie: already saved), this is straightforward if the customizations are new (ie: not saved), this is icky :param fn: fn to use to find customization :param current_model_customization: customizations to check :return: QPropertyCustomization """ # RECALL THAT AS OF v0.16.0.0 INSTEAD OF PASSING A COMPLEX NESTED DICTIONARY # I AM JUST PASSING A SINGLE model_customization INSTANCE W/ ALL ITS M2M FIELDS ALREADY COMPLETE property_customization = find_in_sequence( fn, current_model_customization.property_customizations(manager="allow_unsaved_property_customizations_manager").all() ) if property_customization: return property_customization if current_model_customization.is_new(): return get_customization_by_fn_recusively( fn, current_model_customization.property_customizations(manager="allow_unsaved_property_customizations_manager").all(), CustomizationTypes.PROPERTY, ) else: # current_model_customization.is_existing() return find_in_sequence( fn, QPropertyCustomization.objects.filter( model_customization__project=current_model_customization.project, name=current_model_customization.name, ) )
def get_customization_by_fn_recusively(fn, current_property_customizations, customization_type, **kwargs): """ used in conjunction w/ the "get_<x>_customization_by_fn" fns above recursively goes through the customization hierarchy (of unsaved customizations) returns the first customization that returns True for fn :param fn: fn to call :param property_customizations: the property customizations from which to begin checking :param customization_type: the type of customization to check :return: either QModelCustomization or QCategoryCustomization or QPropertyCustomization or None """ previously_recursed_customizations = kwargs.pop("previously_recursed_customizations", set()) for property_customization in current_property_customizations: property_customization_key = property_customization.get_key() if property_customization_key not in previously_recursed_customizations: if customization_type == CustomizationTypes.PROPERTY and fn(property_customization): return property_customization if property_customization.use_subforms(): target_model_customizations = property_customization.relationship_target_model_customizations(manager="allow_unsaved_relationship_target_model_customizations_manager").all() for target_model_customization in target_model_customizations: if customization_type == CustomizationTypes.MODEL: if fn(target_model_customization): return target_model_customization elif customization_type == CustomizationTypes.CATEGORY: target_category_customization = find_in_sequence( fn, target_model_customization.category_customizations(manager="allow_unsaved_category_customizations_manager").all() ) if target_category_customization: return target_category_customization else: # customization_type == CustomizationTypes.PROPERTY pass # (this will already have been checked above) previously_recursed_customizations.add(property_customization_key) # only tracking property_customizations b/c those are the only recursive things matching_customization = get_customization_by_fn_recusively( fn, target_model_customization.property_customizations(manager="allow_unsaved_property_customizations_manager").all(), customization_type, previously_recursed_customizations=previously_recursed_customizations, ) if matching_customization: return matching_customization
def parse_ontology_content_property(self, ontology_content, new_property_proxy_order, model_proxy): new_property_proxy_name = ontology_content["name"] new_property_proxy_id = ontology_content.get("id") new_property_proxy_field_type = QPropertyTypes.get(ontology_content["property_type"]) assert new_property_proxy_field_type is not None, "invalid property_type specified" (new_property_proxy, created_property_proxy) = QPropertyProxy.objects.get_or_create( # these fields are the "defining" ones (other fields can change below w/out creating new proxies) ontology=self, model_proxy=model_proxy, name=new_property_proxy_name, cim_id=new_property_proxy_id, field_type=new_property_proxy_field_type, ) category_id = ontology_content.get("category_id") new_property_proxy.category_id = category_id new_property_proxy.category_proxy = find_in_sequence(lambda c: c.cim_id == category_id, model_proxy.category_proxies.all()) new_property_proxy.order = new_property_proxy_order new_property_proxy.is_meta = ontology_content.get("is_meta", False) new_property_proxy.is_nillable = ontology_content.get("is_nillable", False) new_property_proxy.is_hierarchical = ontology_content.get("is_hierarchical", False) new_property_proxy_documentation = ontology_content.get("documentation") if new_property_proxy_documentation: new_property_proxy.documentation = remove_spaces_and_linebreaks(new_property_proxy_documentation) new_property_proxy_cardinality = re.split("\.|,", ontology_content.get("cardinality")) # TODO: DECIDE ONCE-AND-FOR-ALL IF "cardinality" IS SPLIT ON '.' OR ',' new_property_proxy.cardinality_min = new_property_proxy_cardinality[0] new_property_proxy.cardinality_max = new_property_proxy_cardinality[1] new_property_proxy.values = ontology_content.get("values") if new_property_proxy_field_type == QPropertyTypes.ATOMIC: new_property_proxy_atomic_type_type = ontology_content.get("atomic_type") if new_property_proxy_atomic_type_type == "STRING": new_property_proxy_atomic_type_type = "DEFAULT" new_property_proxy_atomic_type = QAtomicTypes.get(new_property_proxy_atomic_type_type) assert new_property_proxy_atomic_type is not None, "invalid atomic_type specified" new_property_proxy.atomic_type = new_property_proxy_atomic_type elif new_property_proxy_field_type == QPropertyTypes.ENUMERATION: new_property_proxy.enumeration_is_open = ontology_content.get("enumeration_is_open") new_property_proxy.enumeration_choices = ontology_content.get("enumeration_members") else: # new_property_proxy_field_type == QPropertyTypes.RELATIONSHIP new_property_proxy.relationship_target_names = ontology_content.get("relationship_targets") # target_names are set now; target_models are set later in "QPropertyProxy.reset" new_property_proxy.save() return new_property_proxy
def __init__(self, *args, **kwargs): # customizers was passed in via curry() in the factory function below customizers = kwargs.pop("customizers", None) # RIGHT, THIS CAUSED AN AWFUL LOT OF CONFUSION # THE CALL TO super() CAN TAKE A "customizer" ARGUMENT # BUT I CAN ONLY FIND THAT CUSTOMIZER BY MATCHING IT AGAINST THE VALUE OF "proxy" # THAT REQUIRES CALLING get_current_field_value() WHICH CHECKS THE PREFIX OF A FORM # BUT THAT PREFIX - AND SOME OTHER THINGS TOO - IS ONLY SET FROM DEEP W/IN THE __init__ FN # OF A BASE CLASS; SO I CALL super FIRST W/OUT A "customizer" ARGUMENT AND THEN CUSTOMIZE # AT THE END OF THIS __init__ FN super(MetadataScientificPropertyForm, self).__init__(*args, **kwargs) if customizers: proxy_pk = int(self.get_current_field_value("proxy")) customizer = find_in_sequence(lambda c: c.proxy.pk == proxy_pk, customizers) assert(customizer.name == self.get_current_field_value("name")) # this is new code; just make sure it works else: customizer = None is_enumeration = self.get_current_field_value("is_enumeration", False) if self.instance.pk and is_enumeration: # ordinarily, this is done in create_scientific_property_form_data above # but if this is an existing model, I still need to do this jiggery-pokery someplace current_enumeration_value = self.get_current_field_value("enumeration_value") if isinstance(current_enumeration_value, basestring) and customizer.enumeration_multi: self.initial["enumeration_value"] = current_enumeration_value.split("|") if not is_enumeration: update_field_widget_attributes(self.fields["atomic_value"], {"onchange": "copy_value(this,'%s-scientific_property_value');" % self.prefix}) update_field_widget_attributes(self.fields["atomic_value"], {"class": "atomic_value changer"}) else: # this is handled via the "multiselect" widget in JS rather than here (b/c the widget is created dynamically via JS and has no _standard_ onchange event) # update_field_widget_attributes(self.fields["enumeration_value"], {"onchange": "copy_value(this,'%s-scientific_property_value');" % self.prefix}) update_field_widget_attributes(self.fields["enumeration_value"], {"class": "multiselect"}) if customizer: # HUH, WHY AM I CALLING THIS EXPLICITLY HERE, WHEN IT OUGHT TO BE CALLED AUTOMATICALLY IN super() ABOVE? # B/C customizer IS NOT PASSED TO super() B/C IT NEEDS TO BE FOUND BASED ON THE CURRENT PROXY # WHICH GETS RETURNED BY get_current_field_value() # WHICH HAS TO HAVE THIS FORM MOSTLY SET UP BEFORE IT WILL WORK # WHICH HAPPENS IN THE CALL TO super() # (SEE ABOVE) self.customize(customizer)
def get_realization_by_fn(fn, current_model_realization, realization_types, **kwargs): """ just like the above fn, except it returns the first realization for which fn returns true :param fn: fn to call :param current_model_realization: the model customization from which to begin checking :param realization_types: the types of customizations to check :return: either QModelRealization or QCategoryRealization or QPropertyRealization or None """ previously_recursed_realizations = kwargs.pop("previously_recursed_realizations", set()) if RealizationTypes.MODEL in realization_types: if fn(current_model_realization): return current_model_realization if RealizationTypes.CATEGORY in realization_types: category_realization = find_in_sequence( fn, current_model_realization.categories(manager="allow_unsaved_categories_manager").all() ) if category_realization: return category_realization for property_realization in current_model_realization.properties(manager="allow_unsaved_properties_manager").all(): property_realization_key = property_realization.key if property_realization_key not in previously_recursed_realizations: if RealizationTypes.PROPERTY in realization_types and fn(property_realization): return property_realization if property_realization.field_type == QPropertyTypes.RELATIONSHIP: previously_recursed_realizations.add(property_realization_key) target_model_realizations = property_realization.relationship_values(manager="allow_unsaved_relationship_values_manager").all() for target_model_realization in target_model_realizations: matching_realization = get_realization_by_fn( fn, target_model_realization, realization_types, previously_recursed_realizations=previously_recursed_realizations, ) if matching_realization: # break out of the loop as soon as I find a match return matching_realization
def save_valid_model_formset(model_formset, model_parent_dictionary={}): # just save everything, regardless of whether it was loaded or not # (all of the logic of dealing w/ non-loaded forms is dealt with in the creation & validation fns) # loaded_model_forms = model_formset.get_loaded_forms() model_forms = model_formset.forms # force model_formset to save instances even if they haven't changed # TODO: MAKE THE commit KWARG CONDITIONAL ON WHETHER THE FORM CHANGED (OR IS NEW) TO CUT DOWN ON DB HITS # TODO: (NOTE, I'LL HAVE TO CHANGE THE LOOP BELOW ONCE I'VE DONE THIS) model_instances = [model_form.save(commit=True) for model_form in model_forms] for model_instance in model_instances: try: model_parent_key = model_parent_dictionary[model_instance.get_model_key()] model_instance.parent = find_in_sequence(lambda m: m.get_model_key() == model_parent_key, model_instances) except KeyError: pass # maybe this model didn't have a parent (or the dict was never passed to this fn) model_instance.save(increment_version=False) return model_instances
def get_new_customization_set(project=None, ontology=None, proxy=None, vocabularies=[]): """ """ model_customization = QModelCustomization(ontology=ontology, proxy=proxy, project=project) model_customization.reset(proxy) vocabulary_customizations = [] for i, vocabulary in enumerate(vocabularies): with allow_unsaved_fk(QModelCustomizationVocabulary, ["model_customization"]): vocabulary_customization = QModelCustomizationVocabulary( model_customization=model_customization, vocabulary=vocabulary, order=i + 1, active=True ) vocabulary_customizations.append(vocabulary_customization) standard_category_customizations = [] for standard_category_proxy in ontology.categorization.category_proxies.all(): with allow_unsaved_fk(QStandardCategoryCustomization, ["model_customization"]): standard_category_customization = QStandardCategoryCustomization( model_customization=model_customization, proxy=standard_category_proxy ) standard_category_customization.reset(standard_category_proxy) standard_category_customizations.append(standard_category_customization) standard_property_customizations = [] for standard_property_proxy in proxy.standard_properties.all(): with allow_unsaved_fk( QStandardPropertyCustomization, ["model_customization", "category", "relationship_subform_customization"] ): standard_property_customization = QStandardPropertyCustomization( model_customization=model_customization, proxy=standard_property_proxy, category=find_in_sequence( lambda category: category.proxy.has_property(standard_property_proxy), standard_category_customizations, ), ) standard_property_customization.reset(standard_property_proxy) # HERE BEGINS THE SUBFORM BIT if standard_property_customization.use_subform(): subform_customization_set = get_new_customization_set( project=project, ontology=ontology, proxy=standard_property_proxy.relationship_target_model, vocabularies=[], # NO VOCABULARIES USED IN SUBFORMS ? ) standard_property_customization.relationship_subform_customization = subform_customization_set[ "model_customization" ] # this is important; I do not have access to unsaved m2m fields, but I can set an attribute on the instance standard_property_customization.relationship_subform_customization_set = subform_customization_set # HERE ENDS THE SUBFORM BIT standard_property_customizations.append(standard_property_customization) scientific_category_customizations = [] scientific_property_customizations = [] for vocabulary in vocabularies: vocabulary_key = vocabulary.get_key() for component in vocabulary.component_proxies.all(): component_key = component.get_key() n_categories = len(scientific_category_customizations) for scientific_category_proxy in component.category_proxies.all(): with allow_unsaved_fk(QScientificCategoryCustomization, ["model_customization"]): scientific_category_customization = QScientificCategoryCustomization( model_customization=model_customization, proxy=scientific_category_proxy, vocabulary_key=vocabulary_key, component_key=component_key, ) scientific_category_customization.reset(scientific_category_proxy, reset_keys=False) scientific_category_customizations.append(scientific_category_customization) n_properties = len(scientific_property_customizations) for scientific_property_proxy in component.scientific_property_proxies.all(): with allow_unsaved_fk(QScientificPropertyCustomization, ["category", "model_customization"]): scientific_property_customization = QScientificPropertyCustomization( model_customization=model_customization, proxy=scientific_property_proxy, vocabulary_key=vocabulary_key, component_key=component_key, category=find_in_sequence( lambda category: category.proxy.has_property(scientific_property_proxy), scientific_category_customizations[n_categories - 1 :], ), # ignore the previous categories ) scientific_property_customization.reset(scientific_property_proxy, reset_keys=False) scientific_property_customizations.append(scientific_property_customization) customization_set = { "model_customization": model_customization, "vocabulary_customizations": vocabulary_customizations, "standard_category_customizations": standard_category_customizations, "standard_property_customizations": standard_property_customizations, "scientific_category_customizations": scientific_category_customizations, "scientific_property_customizations": scientific_property_customizations, } return customization_set
def get_new_realizations(project=None, ontology=None, model_proxy=None, **kwargs): # unlike w/ customizations, I do not create the entire possible set all at once # instead I just deal w/ the minimum number of properties (based on cardinality) # infinite recursion is therefore avoided; not by re-using previously created models # as with customizations, but by only creating a finite amount of models # hooray! parent_property = kwargs.pop("parent_property", None) if parent_property is not None: is_active = parent_property.is_required else: is_active = True model_realization = QModelRealization( project=project, proxy=model_proxy, version="0.0.0", is_active=is_active ) model_realization.reset() category_realizations = [] # TODO: IS THERE A MORE EFFICIENT WAY TO DO THIS? # gets _all_ of the categories that are relevant to this model... used_category_proxies = [p.category_proxy for p in model_proxy.property_proxies.all()] category_proxies = set(model_proxy.category_proxies.all()) category_proxies.update(used_category_proxies) # for category_proxy in model_proxy.category_proxies.all(): for category_proxy_order, category_proxy in enumerate(category_proxies): with allow_unsaved_fk(QCategoryRealization, ["model"]): category_realization = QCategoryRealization( proxy=category_proxy, model=model_realization, order=category_proxy_order, ) category_realization.reset() category_realizations.append(category_realization) model_realization.categories(manager="allow_unsaved_categories_manager").add_potentially_unsaved(*category_realizations) property_realizations = [] for property_proxy_order, property_proxy in enumerate(model_proxy.property_proxies.all()): property_category_realization = find_in_sequence( lambda c: c.proxy == property_proxy.category_proxy, category_realizations ) with allow_unsaved_fk(QPropertyRealization, ["model", "category"]): property_realization = QPropertyRealization( proxy=property_proxy, field_type=property_proxy.field_type, # TODO: I AM HAVING TO PASS "field_type" SO THAT IT'S SET IN "__init__" IN ORDER TO SETUP ANY ENUMERATIONS; model=model_realization, # TODO: AN ALTERNATIVE WOULD BE TO CALL "reset" FROM "__init__" WHENEVER "is_new" IS True. category=property_category_realization, order=property_proxy_order, ) property_realization.reset() property_category_realization.properties(manager="allow_unsaved_category_properties_manager").add_potentially_unsaved(property_realization) # here begins the icky bit if property_realization.field_type == QPropertyTypes.RELATIONSHIP and property_realization.is_hierarchical: # property_realization.is_required: target_relationship_values = [] # TODO: IF I WERE TO PRE-CREATE ALL RELATIONSHIPS THEN HERE IS WHERE I WOULD DO IT # TODO: BUT THAT WOULD BE MIND-BOGGLINGLY COMPLEX... # TODO: ...B/C I WOULD NEED TO KNOW IN ADVANCE WHAT TYPES OF RELATIONSHIPS TO CREATE IN THE CASE OF MULTIPLE TYPES OF TARGETS; # TODO: AS IT IS, I GET AROUND THIS BY ONLY PRE-CREATING SPECIALIZATIONS WHICH ARE EXPLICIT IN THEIR TARGET PROXIES # TODO: BUT I STILL CANNOT HANDLE THIS FOR NON-SPECIALIZED PROXIES if property_realization.has_specialized_values: # assert property_realization.cardinality_min == len(property_proxy.values) for target_model_proxy_id in property_proxy.values: target_model_proxy = property_proxy.relationship_target_models.get(cim_id=target_model_proxy_id) kwargs.update({"parent_property": property_realization}) with allow_unsaved_fk(QModelRealization, ["relationship_property"]): # this lets me access the parent property of a model new_model_realization = get_new_realizations( project=project, ontology=target_model_proxy.ontology, model_proxy=target_model_proxy, **kwargs ) new_model_realization.relationship_property = property_realization target_relationship_values.append(new_model_realization) property_realization.relationship_values(manager="allow_unsaved_relationship_values_manager").add_potentially_unsaved(*target_relationship_values) # here ends the icky bit property_realizations.append(property_realization) model_realization.properties(manager="allow_unsaved_properties_manager").add_potentially_unsaved(*property_realizations) return model_realization
def get_new_customizations(project=None, ontology=None, model_proxy=None, **kwargs): key = kwargs.pop("key") customizations = kwargs.pop("customizations", {}) # TODO: CHANGE THIS TO USE GUIDS INSTEAD OF NAMES FOR KEYS # TODO: TRY TO REWRITE THIS TO USE "prefix" AGAIN (INSTEAD OF EXPLICIT "key") model_proxy_key = key if model_proxy_key not in customizations: model_customization = QModelCustomization( project=project, ontology=ontology, proxy=model_proxy, ) model_customization.reset() customizations[model_proxy_key] = model_customization else: model_customization = customizations[model_proxy_key] category_customizations = [] for catgegory_proxy in ontology.categorization.category_proxies.all(): category_proxy_key = "{0}.{1}".format(model_proxy_key, catgegory_proxy.name) with allow_unsaved_fk(QCategoryCustomization, ["model_customization"]): if category_proxy_key not in customizations: category_customization = QCategoryCustomization( proxy=catgegory_proxy, model_customization=model_customization, ) category_customization.reset() customizations[category_proxy_key] = category_customization else: category_customization = customizations[category_proxy_key] category_customizations.append(category_customization) # assert category_customizations[-1].proxy == ontology.categorization.get_uncategorized_category_proxy() model_customization.category_customizations(manager="allow_unsaved_category_customizations_manager").add_potentially_unsaved(*category_customizations) property_customizations = [] for property_proxy in model_proxy.property_proxies.all(): property_proxy_key = "{0}.{1}".format(model_proxy_key, property_proxy.name) with allow_unsaved_fk(QPropertyCustomization, ["model_customization", "category"]): # close this context manager before using the custom related manager # (too much hackery at once) if property_proxy_key not in customizations: category_customization = find_in_sequence( lambda c: c.proxy.has_property(property_proxy), category_customizations ) property_customization = QPropertyCustomization( proxy=property_proxy, model_customization=model_customization, category=category_customization, ) property_customization.reset() category_customization.property_customizations(manager="allow_unsaved_categories_manager").add_potentially_unsaved(property_customization) customizations[property_proxy_key] = property_customization else: property_customization = customizations[property_proxy_key] property_customizations.append(property_customization) ############################ # here begins the icky bit # ############################ if property_customization.use_subforms(): subform_key = "{0}.{1}".format(model_proxy.name, property_proxy.name) # this property in this model (only 1 level deep) target_model_customizations = [] for target_model_proxy in property_proxy.relationship_target_models.all(): target_model_proxy_key = "{0}.{1}".format(subform_key, target_model_proxy.name) if target_model_proxy_key not in customizations: target_model_customization = get_new_customizations( project=project, ontology=ontology, model_proxy=target_model_proxy, key=target_model_proxy_key, customizations=customizations, ) else: target_model_customization = customizations[target_model_proxy_key] target_model_customizations.append(target_model_customization) property_customization.relationship_target_model_customizations(manager="allow_unsaved_relationship_target_model_customizations_manager").add_potentially_unsaved(*target_model_customizations) ########################## # here ends the icky bit # ########################## model_customization.property_customizations(manager="allow_unsaved_property_customizations_manager").add_potentially_unsaved(*property_customizations) return customizations[model_proxy_key]
def update(self, model_customization): """ looks through the customization and checks if any standard or scientific properties which should be displayed are missing, or which shouldn't be displayed are still there (?) :param model_customization: :return: """ standard_properties = self.standard_properties.all() standard_property_customizations = model_customization.standard_property_customizers.all() for standard_property_customization in standard_property_customizations: standard_property_proxy = standard_property_customization.proxy standard_property = find_in_sequence(lambda sp: standard_property_proxy == sp.proxy, standard_properties) if standard_property_customization.displayed: field_type = standard_property_customization.field_type # if there is no standard property, create it... if not standard_property: new_standard_property = MetadataStandardProperty( proxy=standard_property_proxy, model=self, ) new_standard_property.reset() # and if there is a default value, then set it... if field_type == MetadataFieldTypes.ATOMIC and standard_property_customization.default_value: new_standard_property.atomic_value = standard_property_customization.default_value elif field_type == MetadataFieldTypes.ENUMERATION and standard_property_customization.enumeration_default: new_standard_property.enumeration_value = standard_property_customization.enumeration_default new_standard_property.save() standard_property = new_standard_property if field_type == "RELATIONSHIP": # recurse through subforms... for submodel in standard_property.relationship_value.all(): submodel_customizer = standard_property_customization.subform_customizer if submodel_customizer: submodel.update(submodel_customizer) else: # not standard_property_customization.displayed # if there is a standard property, delete it... if standard_property: # TODO: RE-VISIT THIS LOGIC; EVENTUALLY, I DON'T WANT TO DELETE PROPERTIES standard_property.delete() # do the same basic thing, but w/ scientific properties # TODO: THIS SEEMS LIKE PRETTY INEFFICIENT CODE, ANY WAY TO SPEED THIS UP scientific_properties = self.scientific_properties.all() scientific_property_customizations = model_customization.scientific_property_customizers.filter( vocabulary_key=self.vocabulary_key, component_key=self.component_key, ) for scientific_property_customization in scientific_property_customizations: scientific_property_proxy = scientific_property_customization.proxy scientific_property = find_in_sequence(lambda sp: sp.proxy == scientific_property_proxy, scientific_properties) if scientific_property_customization.displayed: # if there is no scientific property, create it... if not scientific_property: new_scientific_property = MetadataScientificProperty( proxy=scientific_property_proxy, model=self, ) new_scientific_property.reset() new_scientific_property.save() else: # not scientific_property_customization.displayed # if there is a scientific property, delete it... if scientific_property: # TODO: RE-VISIT THIS LOGIC; EVENTUALLY, I DON'T WANT TO DELETE PROPERTIES scientific_property.delete()
def get_label(self): label_property = find_in_sequence(lambda property: property.is_label == True, self.standard_properties.all()) if label_property: return u"%s" % label_property.get_value() else: return None