コード例 #1
0
class SamaasaAnnotation(Annotation):
  schema = common.recursively_merge_json_schemas(Annotation.schema, ({
    "type": "object",
    "properties": {
      common.TYPE_FIELD: {
        "enum": ["SamaasaAnnotation"]
      },
      "component_padas": {
        "type": "array",
        "description": "Pointers to PadaAnnotation objects corresponding to components of the samasta-pada",
        "minItems": 1,
        "items": Target.schema
      },
      "samaasa_type": {
        "type": "string"
      }
    },
  }))

  @classmethod
  def get_allowed_target_classes(cls):
    return [PadaAnnotation]

  def validate(self, my_collection=None, user=None):
    super(SamaasaAnnotation, self).validate(my_collection=my_collection, user=user)
    Target.check_target_classes(targets_to_check=self.component_padas, allowed_types=[PadaAnnotation], my_collection=my_collection, targeting_obj=self)

  @classmethod
  def from_details(cls, targets, source, combined_string, samaasa_type="UNK"):
    annotation = SamaasaAnnotation()
    annotation.set_base_details(targets, source)
    annotation.combined_string = combined_string
    annotation.type = samaasa_type
    annotation.validate()
    return annotation
コード例 #2
0
ファイル: __init__.py プロジェクト: rajeshgk/sanskrit_data
class ImageAnnotation(Annotation):
    """ Mark a certain fragment of an image.

  `An introductory video <https://www.youtube.com/watch?v=SHzD3f5nPt0&t=29s>`_
  """
    schema = common.recursively_merge_json_schemas(Annotation.schema, ({
        "type":
        "object",
        "description":
        "A rectangle within an image, picked by a particular annotation source.",
        "properties": {
            common.TYPE_FIELD: {
                "enum": ["ImageAnnotation"]
            },
            "targets": {
                "type": "array",
                "items": ImageTarget.schema
            }
        },
    }))

    target_class = ImageTarget

    @classmethod
    def get_allowed_target_classes(cls):
        return [BookPortion, ImageAnnotation]

    @classmethod
    def from_details(cls, targets, source):
        annotation = ImageAnnotation()
        annotation.set_base_details(targets, source)
        annotation.validate()
        return annotation
コード例 #3
0
class SandhiAnnotation(Annotation):
  schema = common.recursively_merge_json_schemas(Annotation.schema, ({
    "type": "object",
    "properties": {
      common.TYPE_FIELD: {
        "enum": ["SandhiAnnotation"]
      },
      "combined_string": Text.schema,
      "sandhi_type": {
        "type": "string"
      }
    },
    "required": ["combined_string"]
  }))

  @classmethod
  def get_allowed_target_classes(cls):
    return [PadaAnnotation]

  @classmethod
  def from_details(cls, targets, source, combined_string, sandhi_type="UNK"):
    annotation = SandhiAnnotation()
    annotation.set_base_details(targets, source)
    annotation.combined_string = combined_string
    annotation.sandhi_type = sandhi_type
    annotation.validate()
    return annotation
コード例 #4
0
ファイル: __init__.py プロジェクト: rajeshgk/sanskrit_data
class TextOffsetAddress(JsonObject):
    schema = common.recursively_merge_json_schemas(
        JsonObject.schema, {
            "type": "object",
            "description": "A way to specify a substring.",
            "properties": {
                common.TYPE_FIELD: {
                    "enum": ["TextOffsetAddress"]
                },
                "start": {
                    "type": "integer"
                },
                "end": {
                    "type": "integer"
                }
            }
        })

    @classmethod
    def from_details(cls, start, end):
        obj = TextOffsetAddress()
        obj.start = start
        obj.end = end
        obj.validate()
        return obj
コード例 #5
0
ファイル: __init__.py プロジェクト: rajeshgk/sanskrit_data
class TextTarget(Target):
    schema = common.recursively_merge_json_schemas(Target.schema, ({
        "type":
        "object",
        "description":
        "A way to specify a particular substring within a string.",
        "properties": {
            common.TYPE_FIELD: {
                "enum": ["TextTarget"]
            },
            "shabda_id": {
                "type":
                "string",
                "description":
                "Format: pada_index.shabda_index or just pada_index."
                "Suppose that some shabda in 'rāgādirogān satatānuṣaktān' is being targetted. "
                "This has the following pada-vigraha: rāga [comp.]-ādi [comp.]-roga [ac.p.m.]  satata [comp.]-anuṣañj [ac.p.m.]."
                "Then, rāga has the id 1.1. roga has id 1.3. satata has the id 2.1."
            },
            "offset_address": TextOffsetAddress.schema
        },
    }))

    @classmethod
    def from_details(cls, container_id, shabda_id=None, offset_address=None):
        target = TextTarget()
        target.container_id = container_id
        if shabda_id is not None:
            target.shabda_id = shabda_id
        if offset_address is not None:
            target.offset_address = offset_address
        target.validate()
        return target
コード例 #6
0
class UserPermission(JsonObject):
    schema = recursively_merge_json_schemas(
        JsonObject.schema, {
            "properties": {
                TYPE_FIELD: {
                    "enum": ["UserPermission"]
                },
                "service": {
                    "type":
                    "string",
                    "description":
                    "Allowable values should be predetermined regular expressions."
                },
                "actions": {
                    "type": "array",
                    "items": {
                        "type": "string",
                        "enum": ["read", "write", "admin"],
                    },
                    "description": "Should be an enum in the future."
                },
            },
        })

    @classmethod
    def from_details(cls, service, actions):
        obj = UserPermission()
        obj.service = service
        obj.actions = actions
        return obj
コード例 #7
0
ファイル: __init__.py プロジェクト: rajeshgk/sanskrit_data
class Annotation(UllekhanamJsonObject):
    schema = common.recursively_merge_json_schemas(
        UllekhanamJsonObject.schema, ({
            "type": "object",
            "properties": {
                common.TYPE_FIELD: {
                    "enum": ["Annotation"]
                },
                "targets": {
                    "minItems": 1,
                },
            },
            "required": ["targets", "source"]
        }))

    def __init__(self):
        super(Annotation, self).__init__()

    @classmethod
    def get_allowed_target_classes(cls):
        return [BookPortion, Annotation]

    def set_base_details(self, targets, source):
        # noinspection PyAttributeOutsideInit
        self.targets = targets
        # noinspection PyAttributeOutsideInit
        self.source = source
コード例 #8
0
class SubantaAnnotation(PadaAnnotation):
  schema = common.recursively_merge_json_schemas(PadaAnnotation.schema, ({
    "type": "object",
    "description": "Anything ending with a sup affix. Includes avyaya-s.",
    "properties": {
      common.TYPE_FIELD: {
        "enum": ["SubantaAnnotation"]
      },
      "linga": {
        "type": "string",
        "enum": ["strii", "pum", "napum", "avyaya"]
      },
      "vibhakti": {
        "type": "string",
        "enum": ["1", "2", "3", "4", "5", "6", "7", "1.sambodhana"]
      },
      "vachana": {
        "type": "integer",
        "enum": [1, 2, 3]
      }
    },
  }))

  # noinspection PyMethodOverriding
  @classmethod
  def from_details(cls, targets, source, word, root, linga, vibhakti, vachana):
    obj = SubantaAnnotation()
    obj.set_base_details(targets, source, word, root)
    obj.linga = linga
    obj.vibhakti = vibhakti
    obj.vachana = vachana
    obj.validate()
    return obj
コード例 #9
0
class BookPositionTarget(Target):
    schema = common.recursively_merge_json_schemas(
        Target.schema, {
            "type": "object",
            "description":
            "A BookPortion could represent a Book or a chapter or a verse or a half-verse or a sentence or any such unit.",
            "properties": {
                TYPE_FIELD: {
                    "enum": ["BookPositionTarget"]
                },
                "position": {
                    "type":
                    "number",
                    "description":
                    "Any number describing the position of one BookPortion within another."
                }
            }
        })

    @classmethod
    def from_details(cls, container_id=None, position=None):
        target = BookPositionTarget()
        if container_id:
            target.container_id = container_id
        if position:
            target.position = position
        target.validate(my_collection=None)
        return target
コード例 #10
0
ファイル: __init__.py プロジェクト: rajeshgk/sanskrit_data
class TextAnnotation(Annotation):
    schema = common.recursively_merge_json_schemas(Annotation.schema, ({
        "type":
        "object",
        "description":
        "Annotation of some (sub)text from within the object (image or another text) being annotated. Tells: 'what is written in this image? or text portion?",
        "properties": {
            common.TYPE_FIELD: {
                "enum": ["TextAnnotation"]
            },
            "content": Text.schema,
        },
        "required": ["content"]
    }))

    @classmethod
    def get_allowed_target_classes(cls):
        return [BookPortion, ImageAnnotation]

    @classmethod
    def from_details(cls, targets, source, content):
        annotation = TextAnnotation()
        annotation.set_base_details(targets, source)
        annotation.content = content
        annotation.validate()
        return annotation

    @classmethod
    def add_indexes(cls, my_collection):
        super(TextAnnotation, cls).add_indexes(my_collection=my_collection)
        my_collection.create_index(keys_dict={"content.search_strings": 1},
                                   index_name="content_search_strings")
コード例 #11
0
class TinantaAnnotation(PadaAnnotation):
  schema = common.recursively_merge_json_schemas(PadaAnnotation.schema, ({
    "type": "object",
    "description": "Anything ending with a tiN affix.",
    "properties": {
      common.TYPE_FIELD: {
        "enum": ["TinantaAnnotation"]
      },
      "lakAra": {
        "type": "string",
        "enum": ["laT", "laN", "vidhi-liN", "AshIr-liN", "loT", "liT", "luT", "LT", "luN", "LN", "leT"]
      },
      "puruSha": {
        "type": "string",
        "enum": ["prathama", "madhyama", "uttama"]
      },
      "vachana": {
        "type": "integer",
        "enum": [1, 2, 3]
      }
    },
  }))

  # noinspection PyMethodOverriding
  @classmethod
  def from_details(cls, targets, source, word, root, lakAra, puruSha, vachana):
    obj = TinantaAnnotation()
    obj.set_base_details(targets, source, word, root)
    obj.lakAra = lakAra
    obj.puruSha = puruSha
    obj.vachana = vachana
    obj.validate()
    return obj
コード例 #12
0
ファイル: __init__.py プロジェクト: rajeshgk/sanskrit_data
class Rectangle(JsonObject):
    schema = common.recursively_merge_json_schemas(
        JsonObject.schema, ({
            "type": "object",
            "description": "A rectangle within an image.",
            "properties": {
                common.TYPE_FIELD: {
                    "enum": ["Rectangle"]
                },
                "x1": {
                    "type": "integer"
                },
                "y1": {
                    "type": "integer"
                },
                "w": {
                    "type": "integer"
                },
                "h": {
                    "type": "integer"
                },
            },
            "required": ["x1", "y1", "w", "h"]
        }))

    @classmethod
    def from_details(cls, x=-1, y=-1, w=-1, h=-1, score=0.0):
        rectangle = Rectangle()
        rectangle.x1 = x
        rectangle.y1 = y
        rectangle.w = w
        rectangle.h = h
        rectangle.score = score
        rectangle.validate()
        return rectangle

    # Two (segments are 'equal' if they overlap
    def __eq__(self, other):
        xmax = max(self.x, other.x)
        ymax = max(self.y, other.y)
        overalap_w = min(self.x + self.w, other.x + other.w) - xmax
        overalap_h = min(self.y + self.h, other.y + other.h) - ymax
        return overalap_w > 0 and overalap_h > 0

    def __ne__(self, other):
        return not self.__eq__(other)

    # noinspection PyTypeChecker
    def __cmp__(self, other):
        if self == other:
            logging.info(str(self) + " overlaps " + str(other))
            return 0
        elif (self.y < other.y) or ((self.y == other.y) and
                                    (self.x < other.x)):
            return -1
        else:
            return 1
コード例 #13
0
ファイル: users.py プロジェクト: sanskrit-code/sanskrit_data
class AuthenticationInfo(JsonObject):
  schema = recursively_merge_json_schemas(
    JsonObject.schema, {
      "properties": {
        TYPE_FIELD: {
          "enum": ["AuthenticationInfo"]
        },
        "auth_user_id": {
          "type": "string"
        },
        "auth_provider": {
          "type": "string",
          "enum": ["google", "vedavaapi"]
        },
        "auth_secret_bcrypt": {
          "type": "string",
          "description": "This should be hashed, and merits being stored in a database."
        },
        "auth_secret_plain": {
          "type": "string",
          "description": "This should NEVER be set when stored in a database; but is good for client-server transmission purposes."
        }
      }
    }
  )

  VEDAVAAPI_AUTH = "vedavaapi"

  def __str__(self):
    return self.auth_provider + "____" + self.auth_user_id

  def check_password(self, plain_password):
    # Check hased password. Using bcrypt, the salt is saved into the hash itself
    import bcrypt
    return bcrypt.checkpw(plain_password.encode(encoding='utf8'), self.auth_secret_bcrypt.encode(encoding='utf8'))

  @classmethod
  def from_details(cls, auth_user_id, auth_provider, auth_secret_hashed=None):
    obj = AuthenticationInfo()
    obj.auth_user_id = auth_user_id
    obj.auth_provider = auth_provider
    if auth_secret_hashed:
      obj.auth_secret_hashed = auth_secret_hashed
    return obj

  def set_bcrypt_password(self):
    if hasattr(self, "auth_secret_plain") and self.auth_secret_plain != "" and self.auth_secret_plain is not None:
      # noinspection PyAttributeOutsideInit
      self.auth_secret_bcrypt = hash_password(plain_password=self.auth_secret_plain)
      delattr(self, "auth_secret_plain")

  def validate_schema(self):
    super(AuthenticationInfo, self).validate_schema()
    from jsonschema import ValidationError
    self.set_bcrypt_password()
    if hasattr(self, "auth_secret_hashed") and (self.auth_secret_hashed == "" or self.auth_secret_hashed is None):
      raise ValidationError(message="auth_secret_hashed should be non-empty if present.")
コード例 #14
0
ファイル: __init__.py プロジェクト: rajeshgk/sanskrit_data
class Topic(NamedEntity):
    schema = common.recursively_merge_json_schemas(
        NamedEntity.schema, ({
            "type": "object",
            "properties": {
                common.TYPE_FIELD: {
                    "enum": ["Topic"]
                }
            }
        }))
コード例 #15
0
ファイル: __init__.py プロジェクト: rajeshgk/sanskrit_data
class MetreAnnotation(Annotation):
    schema = common.recursively_merge_json_schemas(
        Annotation.schema, ({
            "description": "A metre, which may be ",
            "properties": {
                common.TYPE_FIELD: {
                    "enum": ["MetreAnnotation"]
                },
                "metre": Metre.schema
            }
        }))
コード例 #16
0
ファイル: schema.py プロジェクト: rahulyhg/core_services
class ApiProjectMeta(common.JsonObject):
    schema = common.recursively_merge_json_schemas(common.JsonObject.schema, {
        "type": "object",
        "properties": {
            common.TYPE_FIELD: {
                "enum": ["ApiProjectMeta"]
            }
        }
    })

    @classmethod
    def from_details(cls):
        return cls()
コード例 #17
0
ファイル: schema.py プロジェクト: rahulyhg/core_services
class AuthorizedCredsSetup(common.JsonObject):
    schema = common.recursively_merge_json_schemas(common.JsonObject.schema, {
        "type": "object",
        "properties": {
            common.TYPE_FIELD: {
                "enum": ["AuthorizedCredsSetup"]
            },
            "authorized_creds_files": {
                "type": "object",
                "additionalProperties": AuthorizedCredsMeta.schema
            }
        },
        "default_authorized_creds_file": {
            "type": "string"
        }
    })

    @classmethod
    def from_details(cls, authorized_creds_files=None, default_authorized_creds_file=None):
        obj = cls()
        if authorized_creds_files:
            obj.authorized_creds_files= authorized_creds_files
        if default_authorized_creds_file:
            obj.default_authorized_creds_file= default_authorized_creds_file
        return obj

    # noinspection PyUnresolvedReferences
    def get_authorized_creds_file(self, authorized_creds_file=None, scopes=None, fallback_if_not_exist=False, resolve_defaults=True):
        if not hasattr(self, 'authorized_creds_files'):
            return None
        if authorized_creds_file is not None:
            if not hasattr(self.authorized_creds_files, authorized_creds_file):
                if not fallback_if_not_exist:
                    return None
            else:
                return authorized_creds_file

        if not resolve_defaults:
            return None
        if scopes is not None:
            cred_files_dict = self.to_json_map()['authorized_creds_files']
            profiles = {}
            for key, value in cred_files_dict.items():
                if not False in [scope in value.scopes for scope in scopes]:
                    return key
            return None

        if hasattr(self, 'default_authorized_creds_file'):
            return self.default_authorized_creds_file

        return None
コード例 #18
0
ファイル: __init__.py プロジェクト: rajeshgk/sanskrit_data
class TranslationAnnotation(TextAnnotation):
    schema = common.recursively_merge_json_schemas(TextAnnotation.schema, ({
        "description":
        "A comment that can be associated with nearly any Annotation or BookPortion.",
        "properties": {
            common.TYPE_FIELD: {
                "enum": ["TranslationAnnotation"]
            },
        }
    }))

    @classmethod
    def get_allowed_target_classes(cls):
        return [BookPortion, Annotation]
コード例 #19
0
ファイル: __init__.py プロジェクト: rajeshgk/sanskrit_data
class TopicAnnotation(Annotation):
    """See schema.description."""
    schema = common.recursively_merge_json_schemas(Annotation.schema, ({
        "type":
        "object",
        "description":
        "A given text may be quoted from some other book. This annotation helps specify such origin.",
        "properties": {
            common.TYPE_FIELD: {
                "enum": ["TopicAnnotation"]
            },
            "topic": Topic.schema,
        },
    }))
コード例 #20
0
class Praatipadika(common.JsonObject):
  schema = common.recursively_merge_json_schemas(common.JsonObject.schema, ({
    "type": "object",
    "description": "A prAtipadika.",
    "properties": {
      common.TYPE_FIELD: {
        "enum": ["Praatipadika"]
      },
      "root": "string",
      "prakaara": "string",
      "linga": "string",
      "rootAnalysis": RootAnalysis.schema,
    },
  }))
コード例 #21
0
class RootAnalysis(common.JsonObject):
  schema = common.recursively_merge_json_schemas(common.JsonObject.schema, ({
    "type": "object",
    "description": "Analysis of any root.",
    "properties": {
      common.TYPE_FIELD: {
        "enum": ["RootAnalysis"]
      },
      "root": "string",
      "pratyayas": {
        "type": "array",
        "item": "string"
      },
    },
  }))
コード例 #22
0
ファイル: __init__.py プロジェクト: rajeshgk/sanskrit_data
class ValidationAnnotation(Annotation):
    schema = common.recursively_merge_json_schemas(Annotation.schema, ({
        "type":
        "object",
        "description":
        "Any user can validate a certain annotation (or other object). But it is up to various systems whether such 'validation' has any effect.",
        "properties": {
            common.TYPE_FIELD: {
                "enum": ["ValidationAnnotation"]
            },
            "source": ValidationAnnotationSource.schema
        },
    }))

    def __init__(self):
        super(ValidationAnnotation, self).__init__()
        self.source = ValidationAnnotationSource()
コード例 #23
0
ファイル: __init__.py プロジェクト: rajeshgk/sanskrit_data
class QuoteAnnotation(TextAnnotation):
    schema = common.recursively_merge_json_schemas(
        TextAnnotation.schema, ({
            "description": "A quote, a memorable text fragment.",
            "properties": {
                common.TYPE_FIELD: {
                    "enum": ["QuoteAnnotation"]
                },
                "editable_by_others": {
                    "default": False
                },
            }
        }))

    @classmethod
    def get_allowed_target_classes(cls):
        return [BookPortion, Annotation]
コード例 #24
0
ファイル: schema.py プロジェクト: rahulyhg/core_services
class OauthProviderSetup(common.JsonObject):
    schema = common.recursively_merge_json_schemas(common.JsonObject.schema, {
        "type": "object",
        "properties": {
            common.TYPE_FIELD: {
                "enum": ["ApiProjectSetup"]
            },
            "projects": {
                "type": "object",
                "additionalProperties": ApiProjectMeta.schema
            },
            "default_project": {
                "type": "string"
            }
        }
    })

    @classmethod
    def from_details(cls, projects, defaults=None):
        if defaults is None:
            defaults = {}

        obj = cls()
        obj.projects = projects
        for key, value in defaults.items():
            setattr(obj, key, value)
        return obj

    # noinspection PyUnresolvedReferences
    def get_project_name(self, project=None, fallback_if_not_exist=False, resolve_defaults=True):
        if not hasattr(self, 'projects'):
            return None
        if project is not None:
         if not hasattr(self.projects, project):
            if not fallback_if_not_exist:
                return None
         else:
             return project

        if not resolve_defaults:
            return None
        if not hasattr(self, 'default_project'):
            return None

        return self.default_project
コード例 #25
0
ファイル: schema.py プロジェクト: rahulyhg/core_services
class ApiClientMeta(common.JsonObject):
    schema = common.recursively_merge_json_schemas(common.JsonObject.schema, {
        "type": "object",
        "properties": {
            common.TYPE_FIELD: {
                "enum": ["ApiClientMeta"]
            },
            "client_type": {
                "type": "string",
            }
        },
        "required": ["client_type"]
    })

    @classmethod
    def from_details(cls, client_type):
        obj = cls()
        obj.client_type = client_type
        return obj
コード例 #26
0
class PublicationDetails(JsonObject):
    schema = common.recursively_merge_json_schemas(
        JsonObject.schema, ({
            "type": "object",
            "description": "Publication details of a BookPortion.",
            "properties": {
                TYPE_FIELD: {
                    "enum": ["PublicationDetails"]
                },
                "release_time": {
                    "type": "string"
                },
                "publisher": NamedEntity.schema,
                "canonical_source": {
                    "type": "string",
                },
                "issue_page": {
                    "type": "string",
                },
            }
        }))
コード例 #27
0
ファイル: schema.py プロジェクト: rahulyhg/core_services
class AuthorizedCredsMeta(common.JsonObject):
    schema = common.recursively_merge_json_schemas(common.JsonObject.schema, {
        "type": "object",
        "properties": {
            common.TYPE_FIELD: {
                "enum": ["AuthorizedCredsMeta"]
            },
            "scopes": {
                "type": "array",
                "items": "string",
                "minItems": 1
            }
        },
        "required": ["scopes"]
    })

    @classmethod
    def from_details(cls, scopes):
        obj = cls()
        obj.scopes = scopes
        return obj
コード例 #28
0
ファイル: __init__.py プロジェクト: rajeshgk/sanskrit_data
class RatingAnnotation(Annotation):
    """See schema.description."""
    schema = common.recursively_merge_json_schemas(Annotation.schema, ({
        "type":
        "object",
        "description":
        "A given text may be quoted from some other book. This annotation helps specify such origin.",
        "properties": {
            common.TYPE_FIELD: {
                "enum": ["RatingAnnotation"]
            },
            "rating": {
                "type": "number"
            },
            "editable_by_others": {
                "type": "boolean",
                "description":
                "Can this annotation be taken over by others for wiki-style editing or deleting?",
                "default": False
            }
        },
    }))
コード例 #29
0
class TextSambandhaAnnotation(Annotation):
  schema = common.recursively_merge_json_schemas(Annotation.schema, ({
    "type": "object",
    "description": "Describes connection between two text portions. Such connection is directional (ie it connects words in a source sentence to words in a target sentence.)",
    "properties": {
      common.TYPE_FIELD: {
        "enum": ["TextSambandhaAnnotation"]
      },
      "targets": {
        "description": "A pair of texts being connected. First text is the 'source text', second is the 'target text'",
      },
      "category": {
        "type": "string"
      },
      "source_text_padas": {
        "type": "array",
        "description": "The entity being annotated.",
        "items": Target.schema,
        "minItems": 1,
      },
      "target_text_padas": {
        "type": "array",
        "description": "The entity being annotated.",
        "minItems": 1,
        "items": Target.schema
      }
    },
    "required": ["combined_string"]
  }))

  def validate(self, my_collection=None, user=None):
    super(TextSambandhaAnnotation, self).validate(my_collection=my_collection, user=user)
    Target.check_target_classes(targets_to_check=self.source_text_padas, allowed_types=[PadaAnnotation], my_collection=my_collection, targeting_obj=self)
    Target.check_target_classes(targets_to_check=self.target_text_padas, allowed_types=[PadaAnnotation], my_collection=my_collection, targeting_obj=self)

  @classmethod
  def get_allowed_target_classes(cls):
    return [BookPortion, TextAnnotation]
コード例 #30
0
class CreationDetails(NamedEntity):
    """Many names are possible for the same work (eg. meghasandeshaH vs meghadUtam) - hence we extend the NamedEntity schema."""
    schema = common.recursively_merge_json_schemas(
        NamedEntity.schema, ({
            "type": "object",
            "properties": {
                TYPE_FIELD: {
                    "enum": ["CreationDetails"]
                },
                "authors": {
                    "type": "array",
                    "items": NamedEntity.schema
                }
            }
        }))

    @classmethod
    def from_details(cls, names, authors=None):
        obj = CreationDetails()
        obj.names = names
        if authors is not None:
            obj.authors = authors
        return obj