def _validate_entity_data(self, entity_type, data): if "id" in data or "type" in data: raise ShotgunError("Can't set id or type on create or update") self._validate_entity_fields(entity_type, data.keys()) for field, item in data.items(): if item is None: # none is always ok continue field_info = self._schema[entity_type][field] if field_info["data_type"]["value"] == "multi_entity": if not isinstance(item, list): raise ShotgunError("%s.%s is of type multi_entity, but data %s is not a list" % (entity_type, field, item)) elif item and any(not isinstance(sub_item, dict) for sub_item in item): raise ShotgunError("%s.%s is of type multi_entity, but data %s contains a non-dictionary" % (entity_type, field, item)) elif item and any("id" not in sub_item or "type" not in sub_item for sub_item in item): raise ShotgunError("%s.%s is of type multi-entity, but an item in data %s does not contain 'type' and 'id'" % (entity_type, field, item)) elif item and any(sub_item["type"] not in field_info["properties"]["valid_types"]["value"] for sub_item in item): raise ShotgunError("%s.%s is of multi-type entity, but an item in data %s has an invalid type (expected one of %s)" % (entity_type, field, item, field_info["properties"]["valid_types"]["value"])) elif field_info["data_type"]["value"] == "entity": if not isinstance(item, dict): raise ShotgunError("%s.%s is of type entity, but data %s is not a dictionary" % (entity_type, field, item)) elif "id" not in item or "type" not in item: raise ShotgunError("%s.%s is of type entity, but data %s does not contain 'type' and 'id'" % (entity_type, field, item)) #elif item["type"] not in field_info["properties"]["valid_types"]["value"]: # raise ShotgunError("%s.%s is of type entity, but data %s has an invalid type (expected one of %s)" % (entity_type, field, item, field_info["properties"]["valid_types"]["value"])) else: try: sg_type = field_info["data_type"]["value"] python_type = {"number": int, "float": float, "checkbox": bool, "text": basestring, "serializable": dict, "date": datetime.date, "date_time": datetime.datetime, "url": dict}[sg_type] except KeyError: raise ShotgunError("Field %s.%s: Handling for Shotgun type %s is not implemented" % (entity_type, field, sg_type)) if not isinstance(item, python_type): raise ShotgunError("%s.%s is of type %s, but data %s is not of type %s" % (entity_type, field, type(item), sg_type, python_type))
def _validate_entity_fields(self, entity_type, fields): self._validate_entity_type(entity_type) if fields is not None: valid_fields = set(self._schema[entity_type].keys()) for field in fields: try: field2, entity_type2, field3 = field.split(".", 2) self._validate_entity_fields(entity_type2, [field3]) except ValueError: if field not in valid_fields and field not in ("type", "id"): raise ShotgunError("%s is not a valid field for entity %s" % (field, entity_type))
def _row_matches_filters(self, entity_type, row, filters, filter_operator, retired_only): if retired_only and not row["__retired"] or not retired_only and row["__retired"]: # ignore retired rows unless the retired_only flag is set # ignore live rows if the retired_only flag is set return False elif filter_operator in ("all", None): return all(self._row_matches_filter(entity_type, row, filter) for filter in filters) elif filter_operator == "any": return any(self._row_matches_filter(entity_type, row, filter) for filter in filters) else: raise ShotgunError("%s is not a valid filter operator" % filter_operator)
def batch(self, requests): results = [] for request in requests: if request["request_type"] == "create": results.append(self.create(request["entity_type"], request["data"])) elif request["request_type"] == "update": # note: Shotgun.update returns a list of a single item results.append(self.update(request["entity_type"], request["entity_id"], request["data"])[0]) elif request["request_type"] == "delete": results.append(self.delete(request["entity_type"], request["entity_id"])) else: raise ShotgunError("Invalid request type %s in request %s" % (request["request_type"], request)) return results
def _get_field_from_row(self, entity_type, row, field): # split dotted form fields try: # is it something like sg_sequence.Sequence.code ? field2, entity_type2, field3 = field.split(".", 2) if field2 in row: field_value = row[field2] # all deep links need to be link fields if not isinstance(field_value, dict): raise ShotgunError("Invalid deep query field %s.%s" % (entity_type, field)) # make sure that types in the query match type in the linked field if entity_type2 != field_value["type"]: raise ShotgunError("Deep query field %s.%s does not match type " "with data %s" % (entity_type, field, field_value)) # ok so looks like the value is an entity link # e.g. db contains: {"sg_sequence": {"type":"Sequence", "id": 123 } } linked_row = self._db[ field_value["type"] ][ field_value["id"] ] if field3 in linked_row: return linked_row[field3] else: return None else: # sg returns none for unknown stuff return None except ValueError: # this is not a deep-linked field - just something like "code" if field in row: return row[field] else: # sg returns none for unknown stuff return None
def _row_matches_filter(self, entity_type, row, filter): try: field, operator, rval = filter except ValueError: raise ShotgunError("Filters must be in the form [lval, operator, rval]") lval = self._get_field_from_row(entity_type, row, field) field_type = self._get_field_type(entity_type, field) # if we're operating on an entity, we'll need to grab the name from the lval's row if field_type == "entity": lval_row = self._db[lval["type"]][lval["id"]] if "name" in lval_row: lval["name"] = lval_row["name"] elif "code" in lval_row: lval["name"] = lval_row["code"] return self._compare(field_type, lval, operator, rval)
def _validate_entity_exists(self, entity_type, entity_id): if entity_id not in self._db[entity_type]: raise ShotgunError("No entity of type %s exists with id %s" % (entity_type, entity_id))
def _compare(self, field_type, lval, operator, rval): if field_type == "checkbox": if operator == "is": return lval == rval elif operator == "is_not": return lval != rval elif field_type in ("float", "number", "date", "date_time"): if operator == "is": return lval == rval elif operator == "is_not": return lval != rval elif operator == "less_than": return lval < rval elif operator == "greater_than": return lval > rval elif operator == "between": return lval >= rval[0] and lval <= rval[1] elif operator == "not_between": return lval < rval[0] or lval > rval[1] elif operator == "in": return lval in rval elif field_type == "list": if operator == "is": return lval == rval elif operator == "is_not": return lval != rval elif operator == "in": return lval in rval elif operator == "not_in": return lval not in rval elif field_type == "entity_type": if operator == "is": return lval == rval elif field_type == "text": if operator == "is": return lval == rval elif operator == "is_not": return lval != rval elif operator == "in": return lval in rval elif operator == "contains": return lval in rval elif operator == "not_contains": return lval not in rval elif operator == "starts_with": return lval.startswith(rval) elif operator == "ends_with": return lval.endswith(rval) elif field_type == "entity": if operator == "is": return lval["type"] == rval["type"] and lval["id"] == rval["id"] elif operator == "is_not": return lval["type"] != rval["type"] or lval["id"] != rval["id"] elif operator == "in": return all((lval["type"] == sub_rval["type"] and lval["id"] == sub_rval["id"]) for sub_rval in rval) elif operator == "type_is": return lval["type"] == rval elif operator == "type_is_not": return lval["type"] != rval elif operator == "name_contains": return rval in lval["name"] elif operator == "name_not_contains": return rval not in lval["name"] elif operator == "name_starts_with": return lval["name"].startswith(rval) elif operator == "name_ends_with": return lval["name"].endswith(rval) elif field_type == "multi_entity": if operator == "is": return rval["id"] in (sub_lval["id"] for sub_lval in lval) elif operator == "is_not": return rval["id"] not in (sub_lval["id"] for sub_lval in lval) raise ShotgunError("The %s operator is not supported on the %s type" % (operator, field_type))
def _validate_entity_type(self, entity_type): if entity_type not in self._schema: raise ShotgunError("%s is not a valid entity" % entity_type)