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
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
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
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
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
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
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
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
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
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")
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
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
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.")
class Topic(NamedEntity): schema = common.recursively_merge_json_schemas( NamedEntity.schema, ({ "type": "object", "properties": { common.TYPE_FIELD: { "enum": ["Topic"] } } }))
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 } }))
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()
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
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]
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, }, }))
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, }, }))
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" }, }, }))
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()
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]
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
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
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", }, } }))
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
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 } }, }))
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]
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