diff --git a/.gitignore b/.gitignore index b6e4761..cdf689f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +pechas/ # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 55e4b03..a91a928 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -5,7 +5,6 @@ repos: rev: v4.3.0 hooks: - id: trailing-whitespace - - id: end-of-file-fixer - id: check-yaml - repo: https://github.com/asottile/pyupgrade diff --git a/pyproject.toml b/pyproject.toml index a4cf13f..8f147a9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,12 +3,12 @@ requires = ["setuptools>=61.0"] build-backend = "setuptools.build_meta" [project] -name = "project_name" +name = "openpecha" version = "0.0.1" authors = [ { name="OpenPecha", email="dev@openpecha.org" }, ] -description = "A small example package" +description = "OpenPecha toolkit version 2" readme = "README.md" requires-python = ">=3.8" classifiers = [ @@ -17,6 +17,12 @@ classifiers = [ "Operating System :: OS Independent", ] +dependencies = [ + "pydantic >= 2.7.4", + "stam == 0.8.2", + +] + [project.optional-dependencies] dev = [ "pytest", diff --git a/src/openpecha/alignment/__init__.py b/src/openpecha/alignment/__init__.py index e69de29..5f769d7 100644 --- a/src/openpecha/alignment/__init__.py +++ b/src/openpecha/alignment/__init__.py @@ -0,0 +1,36 @@ +from typing import List, Tuple + + +class AlignmentMetadata: + pass + + +class Alignment: + def __init__( + self, + metadata: AlignmentMetadata, + parser_segment_pairs=None, + alignment_segment_pairs=None, + ): + self.metadata = metadata + self.parser_segment_pairs = parser_segment_pairs + self.alignment_segment_pairs = alignment_segment_pairs + + @classmethod + def from_path(cls, path: str): + pass + + @classmethod + def from_id(cls, alignment_id: str): + pass + + @classmethod + def from_segment_pairs( + cls, + segment_pairs: List[Tuple[Tuple[str, str], Tuple[str, str]]], + metadata: AlignmentMetadata, + ): + return cls(metadata=metadata, parser_segment_pairs=segment_pairs) + + def save(self, path: str): + pass diff --git a/src/openpecha/alignment/alignment.py b/src/openpecha/alignment/alignment.py deleted file mode 100644 index 5f769d7..0000000 --- a/src/openpecha/alignment/alignment.py +++ /dev/null @@ -1,36 +0,0 @@ -from typing import List, Tuple - - -class AlignmentMetadata: - pass - - -class Alignment: - def __init__( - self, - metadata: AlignmentMetadata, - parser_segment_pairs=None, - alignment_segment_pairs=None, - ): - self.metadata = metadata - self.parser_segment_pairs = parser_segment_pairs - self.alignment_segment_pairs = alignment_segment_pairs - - @classmethod - def from_path(cls, path: str): - pass - - @classmethod - def from_id(cls, alignment_id: str): - pass - - @classmethod - def from_segment_pairs( - cls, - segment_pairs: List[Tuple[Tuple[str, str], Tuple[str, str]]], - metadata: AlignmentMetadata, - ): - return cls(metadata=metadata, parser_segment_pairs=segment_pairs) - - def save(self, path: str): - pass diff --git a/src/openpecha/alignment/parsers/plaintext.py b/src/openpecha/alignment/parsers/plaintext.py index a97d60a..92a1dfd 100644 --- a/src/openpecha/alignment/parsers/plaintext.py +++ b/src/openpecha/alignment/parsers/plaintext.py @@ -1,20 +1,77 @@ -class PlainText: - def __init__(self, source_text: str, target_text: str): +from pathlib import Path +from typing import Dict + +from openpecha.ids import get_initial_pecha_id, get_uuid +from openpecha.pecha import Pecha +from openpecha.pecha.annotation import Annotation +from openpecha.pecha.layer import Layer, LayerEnum + + +class PlainTextLineAlignedParser: + def __init__(self, source_text: str, target_text: str, metadata: dict): self.source_text = source_text - self.traget_text = target_text + self.target_text = target_text + self.metadata = metadata @classmethod - def from_files(cls, source_path: str, target_path: str): - source_text = open(source_path).read() - target_text = open(target_path).read() - return cls(source_text, target_text) + def from_files(cls, source_path: Path, target_path: Path, metadata: dict): + source_text = source_path.read_text(encoding="utf-8") + target_text = target_path.read_text(encoding="utf-8") + return cls(source_text, target_text, metadata) + + def create_pecha_layer(self, base_text: str, annotation: LayerEnum): + """ """ + layer_annotations: Dict[str, Annotation] = {} + char_count = 0 + for segment in base_text.split("\n"): + layer_annotations[get_uuid()] = Annotation( + id_=get_uuid(), + segment=segment, + start=char_count, + end=char_count + len(segment), + ) + char_count += len(segment) + + return Layer(annotation_label=annotation, annotations=layer_annotations) - def parse(self, metadata: dict): - # source_segments = self.source_text.split("\n") - # target_segments = self.target_text.split("\n") + def parse(self): + source_pecha_id, target_pecha_id = ( + get_initial_pecha_id(), + get_initial_pecha_id(), + ) + + source_base_fname, target_base_fname = get_uuid(), get_uuid() + source_base_files = {source_base_fname: self.source_text} + target_base_files = {target_base_fname: self.target_text} + + source_annotation = LayerEnum(self.metadata["source"]["annotation_label"]) + target_annotation = LayerEnum(self.metadata["target"]["annotation_label"]) + + source_layers = { + source_base_fname: { + source_annotation: self.create_pecha_layer( + self.source_text, source_annotation + ) + } + } + target_layers = { + target_base_fname: { + target_annotation: self.create_pecha_layer( + self.target_text, target_annotation + ), + } + } + + source_pecha = Pecha( # noqa + source_pecha_id, source_base_files, source_layers, self.metadata["source"] + ) + target_pecha = Pecha( # noqa + target_pecha_id, target_base_files, target_layers, self.metadata["target"] + ) + return source_pecha, target_pecha # TODO: - # 1. Create pecha with segment layers for source and target text + # 2. create a segment pairs [((source_pecha_id,source_segment_id), (target_pecha_id, target_segment_id)), ...] # 3. Create AlignmentMetadata diff --git a/src/openpecha/alignment/pecha/pecha.py b/src/openpecha/alignment/pecha/pecha.py deleted file mode 100644 index cbffb86..0000000 --- a/src/openpecha/alignment/pecha/pecha.py +++ /dev/null @@ -1,2 +0,0 @@ -class Pecha: - pass diff --git a/src/openpecha/config.py b/src/openpecha/config.py new file mode 100644 index 0000000..e0fa952 --- /dev/null +++ b/src/openpecha/config.py @@ -0,0 +1,16 @@ +from pathlib import Path +from shutil import rmtree + + +def _mkdir(path): + if path.exists(): + rmtree(path) + path.mkdir(exist_ok=True, parents=True) + return path + + +BASE_PATH = _mkdir(Path.home() / ".pechadata") +PECHAS_PATH = _mkdir(BASE_PATH / "pechas") + +PECHA_ANNOTATION_STORE_ID = "PechaAnnotationStore" +PECHA_DATASET_ID = "PechaDataSet" diff --git a/src/openpecha/pecha/__init__.py b/src/openpecha/pecha/__init__.py new file mode 100644 index 0000000..781d99e --- /dev/null +++ b/src/openpecha/pecha/__init__.py @@ -0,0 +1,64 @@ +import json +from pathlib import Path +from shutil import rmtree +from typing import Dict + +from stam import AnnotationStore, Offset, Selector + +from openpecha.config import ( + PECHA_ANNOTATION_STORE_ID, + PECHA_DATASET_ID, + PECHAS_PATH, + _mkdir, +) +from openpecha.ids import get_uuid +from openpecha.pecha.annotation import Annotation +from openpecha.pecha.layer import Layer, LayerEnum + + +class Pecha: + def __init__( + self, + pecha_id: str, + bases: Dict[str, str], + layers: Dict[str, Dict[LayerEnum, Layer]], + metadata: Dict[str, str], + ) -> None: + self.pecha_id = pecha_id + self.bases = bases + self.layers = layers + self.metadata = metadata + + @classmethod + def from_path(cls, path: str): + pass + + @classmethod + def from_id(cls, pecha_id: str): + pass + + def write(self, export_path: Path = PECHAS_PATH): + + pecha_dir = _mkdir(export_path / self.pecha_id) + self.base_path = _mkdir(pecha_dir / f"{self.pecha_id}.opf") + """ write metadata """ + self.metadata_fn = self.base_path / "metadata.json" + self.metadata_fn.write_text( + json.dumps(self.metadata, indent=4, ensure_ascii=False), encoding="utf-8" + ) + + """ write base file""" + base_dir = _mkdir(self.base_path / "base") + for base_fname, base_text in self.bases.items(): + base_fn = base_dir / f"{base_fname}.txt" + base_fn.write_text(base_text, encoding="utf-8") + + layer_dir = _mkdir(self.base_path / "layers") + """ write annotation layers""" + for layer_fname, layer_data in self.layers.items(): + for _, layer in layer_data.items(): + _mkdir(layer_dir / layer_fname) + layer.write( + base_file_path=base_dir / layer_fname, + export_path=layer_dir / layer_fname, + ) diff --git a/src/openpecha/pecha/annotation.py b/src/openpecha/pecha/annotation.py new file mode 100644 index 0000000..c7f37c8 --- /dev/null +++ b/src/openpecha/pecha/annotation.py @@ -0,0 +1,15 @@ +from pydantic import BaseModel, Field, ValidationInfo, field_validator + + +class Annotation(BaseModel): + segment: str + start: int = Field(ge=0) + end: int = Field(ge=0) + metadata: dict = Field(default_factory=dict) + + @field_validator("end") + @classmethod + def end_must_not_be_less_than_start(cls, v: int, values: ValidationInfo) -> int: + if "start" in values.data and v < values.data["start"]: + raise ValueError("Span end must not be less than start") + return v diff --git a/src/openpecha/pecha/layer.py b/src/openpecha/pecha/layer.py new file mode 100644 index 0000000..671896c --- /dev/null +++ b/src/openpecha/pecha/layer.py @@ -0,0 +1,76 @@ +import json +from enum import Enum +from pathlib import Path +from typing import Dict + +from stam import AnnotationStore, Offset, Selector + +from openpecha.config import PECHA_ANNOTATION_STORE_ID, PECHA_DATASET_ID +from openpecha.ids import get_uuid +from openpecha.pecha.annotation import Annotation + + +class LayerEnum(Enum): + segment = "Segment" + commentaries = "Commentaries" + + +def get_annotation_category(): + # TODO + # Return annotation category based on the annotation label + return "Structure Type" + + +class Layer: + def __init__(self, annotation_label: LayerEnum, annotations: Dict[str, Annotation]): + self.annotation_label = annotation_label + self.annotations = annotations + + def covert_to_relative_path(self, json_string: str, export_path: Path): + """convert the absolute path to relative path for base file path in json string""" + json_object = json.loads(json_string) + for resource in json_object["resources"]: + original_path = Path(resource["@include"]) + resource["@include"] = str(original_path.relative_to(export_path)) + return json_object + + def write(self, base_file_path: Path, export_path: Path): + """write annotations in stam data model""" + self.annotation_store = AnnotationStore(id=PECHA_ANNOTATION_STORE_ID) + self.resource = self.annotation_store.add_resource( + id=base_file_path.name, filename=base_file_path.as_posix() + ) + self.dataset = self.annotation_store.add_dataset(id=PECHA_DATASET_ID) + + annotation_category = get_annotation_category() + self.dataset.add_key(annotation_category) + + unique_annotation_data_id = get_uuid() + for annotation_id, annotation in self.annotations.items(): + target = Selector.textselector( + self.resource, + Offset.simple(annotation.start, annotation.end), + ) + data = [ + { + "id": unique_annotation_data_id, + "key": annotation_category, + "value": self.annotation_label.value, + "set": self.dataset.id(), + } + ] + self.annotation_store.annotate( + id=annotation_id, + target=target, + data=data, + ) + """ save annotations in json""" + json_string = self.annotation_store.to_json_string() + json_object = self.covert_to_relative_path(json_string, export_path) + """ add four uuid digits to the layer file name for uniqueness""" + layer_fname = f"{self.annotation_label.value}-{get_uuid()[:4]}.json" + with open( + export_path / layer_fname, + "w", + ) as f: + f.write(json.dumps(json_object, indent=4, ensure_ascii=False)) diff --git a/src/openpecha/alignment/pecha/__init__.py b/tests/__init__.py similarity index 100% rename from src/openpecha/alignment/pecha/__init__.py rename to tests/__init__.py diff --git a/tests/__init__.py b/tests/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/alignment/parsers/plaintext/data/comments.txt b/tests/alignment/parsers/plaintext/data/comments.txt new file mode 100755 index 0000000..bac8759 --- /dev/null +++ b/tests/alignment/parsers/plaintext/data/comments.txt @@ -0,0 +1,5 @@ +{D3874}༄༅༅། །རྒྱ་གར་སྐད་དུ། བོ་དྷི་སཏྭ་ཙཱརྱ་ཨ་བ་ཏཱ་ར་སང་ཀཱ་ར། +བོད་སྐད་དུ། བྱང་ཆུབ་སེམས་དཔའི་སྤྱོད་པ་ལ་འཇུག་པའི་ལེགས་པར་སྦྱར་བ། +བཅོམ་ལྡན་འདས་གསུང་གི་མངའ་བདག་འཇམ་དཔལ་གཞོན་ནུར་གྱུར་པ་ལ་ཕྱག་འཚལ་ལོ། །ངོ་བོ་ཉིད་ནི་བྱང་ཆུབ་སེམས་པའི། །རྒྱ་མཚོ་དེ་ལ་ཕྱག་འཚལ་ཏེ། །བདག་འདྲའི་སྤྱོད་པའི་ཡན་ལག་ལ། །འཇུག་ཕྱིར་ལེགས་སྦྱར་བཤད་ཙམ་བྱ། །དམ་པ་རྣམས་ཀྱིས་ནི་ཐོག་མ་དང་བར་དང་ཐ་མར་དགེ་བ་མངོན་པར་འཕེལ་བར་བྱ་བ་ཡིན་པས། བདེ་གཤེགས་ཞེས་བྱ་བ་ལ་སོགས་པ་སྨོས་པ་ཡིན་ཏེ། འདིར་ཕྱག་འཚལ་བ་ནི་དང་པོར་དགེ་བའོ། །ཆོས་བསྟན་པ་ནི་བར་དུ་དགེ་བའོ། །དགེ་བའི་རྩ་བ་ཡོངས་སུ་བསྔོ་བ་ནི་དགེ་བའི་རྩ་བ་མངོན་པར་འཕེལ་བ་ཡིན་པས་ཐ་མར་དགེ་བ་ཡིན་ནོ། །དེ་ལ་བདེ་བར་གཤེགས་པ་ནི་རྟོགས་པར་བྱ་བའི་ལྷག་མ་མི་མངའ་བས་ན་ཡོངས་སུ་རྫོགས་པར་ཐུགས་སུ་ཆུད་པའི་ཕྱིར་བདེ་བར་གཤེགས་པའོ། །ཆོས་ཀྱི་སྐུ་མངའ་བ་ནི་ལུང་དང་རྟོགས་པའི་བདག་ཉིད་ཅན་གྱི་དམ་པའི་ཆོས་ཀྱི་ཚོགས་ནི་ཆོས་ཀྱི་སྐུ་སྟེ་དེ་དང་བཅས་པའོ། །སྲས་བཅས་ནི་ཉིད་ལས་འཁྲུངས་པའི་སྲས་ཏེ། བྱང་ཆུབ་སེམས་དཔའ་དང་བཅས་པའོ། །ལ་ལ་ལས་ནི་བདེ་གཤེགས་དམ་པའི་ཆོས་དང་དགེ་འདུན་བཅས་ཞེས་ཟེར་རོ། །བཙུན་པ་ནི་ཉན་ཐོས་ཆེན་པོ་བརྒྱད་ལ་སོགས་པ་ལ་བྱ་སྟེ། དེ་དག་མ་ལུས་པ་ཀུན་ལ་ཕྱག་འཚལ་བའོ། །དཀོན་མཆོག་གསུམ་པོ་གཙོ་བོར་གྱུར་པས་སོ་སོར་སྨོས་པ་ཡིན་ལ། དེ་དག་ཀྱང་ཕྱག་བྱ་བར་འོས་པ་ཡིན་པས་གུས་པས་ཕྱག་འཚལ་ཏེ། ཞེས་བྱ་བ་སྨོས་ཏེ། འདིར་ཡོན་ཏན་དམ་པའི་བསྟོད་པ་རྒྱ་ཆེ་བ་དང་། མཆོད་པ་ཁྱད་པར་དུ་འཕགས་པའི་དམིགས་པ་ཡིད་ལ་བྱེད་པ་ལས་བྱུང་བའི་མོས་པའི་བསམ་པ་ཤིན་ཏུ་ཕུལ་དུ་བྱུང་བའི་དགའ་བ་རྒྱ་ཆེ་བའི་མཆོད་པ་དང་བཅས་པས་ལུས་ཞིང་ཐམས་ཅད་ཀྱི་རྡུལ་སྙེད་ཀྱིས་བཏུད་ཅིང་ཕྱག་འཚལ་ལོ། །དེ་ལྟར་ཕྱག་བཙལ་ནས་ཅི་ཞིག་བྱེད་ཅེ་ན། བདེ་གཤེགས་སྲས་ཀྱི་ཞེས་བྱ་བ་ལ་སོགས་པ་སྨོས་ཏེ། བདེ་བར་གཤེགས་པའི་བདག་ཉིད་ནི་ཆོས་ཀྱི་སྐུ་སྟེ། དེའི་དབང་དུ་བྱས་པ་ལས་སྐྱེས་པ་ས་ཆེན་པོ་ཐོབ་པ་དང་། རྒྱུ་ལ་གནས་པ་རྣམས་སོ། །དེ་རྣམས་ཀྱི་སྡོམ་པ་ནི་མི་དགེ་བ་སྤོང་བ་དང་། དགེ་བ་ལ་འཇུག་པ་དང་། སེམས་ཅན་གྱི་དོན་བྱ་བའོ། །དེ་ཡང་བཅོམ་ལྡན་འདས་ཀྱིས་ཤིན་ཏུ་ཟབ་ཅིང་རྒྱ་ཆེ་བའི་བདག་ཉིད་ཅན་དུ་གསུངས་ལ། དེར་བྱང་ཆུབ་ཏུ་སེམས་བསྐྱེད་པའི་ཕན་ཡོན་ལ་སོགས་པའི་དོན་རྣམ་པ་བཅུ་པོ་གང་ཡིན་པ་དེས་འཇུག་པའི་བདེ་བར་གཤེགས་པའི་སྲས་ཀྱི་སྡོམ་པ་ལ་འཇུག་པ་བསྟན་པར་བྱའོ། །དེ་ཡང་ལུང་བཞིན་ཞེས་བྱ་བ་སྟེ། ལུང་གི་དོན་དང་མི་འགལ་བར་རོ། །ལུང་ལས་ནི་བཅོམ་ལྡན་འདས་ཀྱིས་རྒྱ་ཆེར་གསུངས་སོ་ཞེ་ན། མདོར་བསྡུས་ནས་ནི་ཞེས་བྱ་བ་སྨོས་སོ། །དེ་ལྟ་ཡིན་དུ་ཆུག་ན། ཅི་འདིར་སྔོན་ཆད་མ་བྱུང་བ་གཞན་འགའ་ཞིག་སྨས་སམ། + +ལུང་ཇི་ལྟ་བ་ཡིན་ཞེ་ན། སྔོན་ཆད་ཅེས་བྱ་བ་ལ་སོགས་པ་སྨོས་སོ། །སྡེབ་སྦྱོར་མཁས་པས་སྔོན་མ་ཡིན་ནམ་ཞེ་ན། སྡེབ་སྦྱོར་ཞེས་བྱ་བ་ལ་སོགས་པ་སྨོས་སོ། །གང་གི་ཕྱིར་འདིར་སྡེབ་སྦྱོར་ལ་མཁས་པ་མེད་པ་ཉིད་ཀྱི་ཕྱིར་གཞན་གྱི་དོན་དུ་བདག་གིས་འདི་གཞུང་དུ་ཉེ་བར་སྦྱར་བ་མ་བྱས་སོ་ཞེས་བྱ་བར་དགོངས་སོ། །དེ་ལྟར་གལ་ཏེ་གཞན་གྱི་དོན་དུ་མ་བྱས་ན་ཅིའི་ཕྱིར་བྱེད་ཅེ་ན། དེའི་ཕྱིར་རང་གི་ཞེས་བྱ་བ་ལ་སོགས་པ་སྨོས་ཏེ། ཡིད་ལ་འདིར་བྱང་ཆུབ་ཀྱི་སེམས་ཏེ། དེ་བསྒོམ་པའི་ཕྱིར་ཞེས་བྱ་བ་ནི་བསླབ་པའི་ཕྱིར་རོ། །བཅོམ་ལྡན་འདས་ཀྱིས་ལུང་ལས་ཚིག་གི་དོན་རྒྱ་ཆེར་གསུངས་པ་དེ་ལས་མདོར་བསྡུས་ཏེ་རང་གི་ཡིད་ལ་བསྒོམ་པར་བྱ་བའི་ཕྱིར་བདག་གིས་འདི་བྱས་སོ་ཞེས་པའོ། ། \ No newline at end of file diff --git a/tests/alignment/parsers/plaintext/data/segments.txt b/tests/alignment/parsers/plaintext/data/segments.txt new file mode 100755 index 0000000..1c756a1 --- /dev/null +++ b/tests/alignment/parsers/plaintext/data/segments.txt @@ -0,0 +1,5 @@ +རྒྱ་གར་སྐད་དུ། བོ་དྷི་སཏྭ་ཙརྱ་ཨ་བ་ཏཱ་ར། +བོད་སྐད་དུ། བྱང་ཆུབ་སེམས་དཔའི་སྤྱོད་པ་ལ་འཇུག་པ། +སངས་རྒྱས་དང་བྱང་ཆུབ་སེམས་དཔའ་ཐམས་ཅད་ལ་ཕྱག་འཚལ་ལོ། ། +བདེ་གཤེགས་ཆོས་ཀྱི་སྐུ་མངའ་སྲས་བཅས་དང་། །ཕྱག་འོས་ཀུན་ལའང་གུས་པར་ཕྱག་འཚལ་ཏེ། །བདེ་གཤེགས་སྲས་ཀྱི་སྡོམ་ལ་འཇུག་པ་ནི། །ལུང་བཞིན་མདོར་བསྡུས་ནས་ནི་བརྗོད་པར་བྱ། ། +སྔོན་ཆད་མ་བྱུང་བ་ཡང་འདིར་བརྗོད་མེད། །སྡེབ་སྦྱོར་མཁས་པའང་བདག་ལ་ཡོད་མིན་ཏེ། །དེ་ཕྱིར་གཞན་དོན་བསམ་པ་བདག་ལ་མེད། །རང་གི་ཡིད་ལ་བསྒོམ་ཕྱིར་ངས་འདི་བརྩམས། ། \ No newline at end of file diff --git a/tests/alignment/parsers/plaintext/test_plaintext.py b/tests/alignment/parsers/plaintext/test_plaintext.py new file mode 100644 index 0000000..60c1aea --- /dev/null +++ b/tests/alignment/parsers/plaintext/test_plaintext.py @@ -0,0 +1,59 @@ +from pathlib import Path + +from openpecha.alignment.parsers.plaintext import PlainTextLineAlignedParser +from openpecha.pecha import Pecha + + +def get_data_dir(): + return Path(__file__).parent / "data" + + +def get_metadata(): + return { + "source": { + "annotation_category": "Structure Type", + "annotation_label": "Segment", + }, + "target": { + "annotation_category": "Structure Type", + "annotation_label": "Comment", + }, + } + + +def test_plaintext_parse(): + DATA_DIR = get_data_dir() + source_path = DATA_DIR / "segments.txt" + target_path = DATA_DIR / "comments.txt" + + metadata = get_metadata() + plaintext = PlainTextLineAlignedParser.from_files( + source_path, target_path, metadata + ) + plaintext.parse() + + assert ( + len(plaintext.source_segments) == 5 + ), "plaintext parser is not parsing source_segments correctly" + assert ( + len(plaintext.target_segments) == 5 + ), "plaintext parser is not parsing target_segments correctly" + + +def test_plaintext_save(): + DATA_DIR = get_data_dir() + source_path = DATA_DIR / "segments.txt" + target_path = DATA_DIR / "comments.txt" + + metadata = get_metadata() + plaintext = PlainTextLineAlignedParser.from_files( + source_path, target_path, metadata + ) + source_pecha, target_pecha = plaintext.save() + + assert isinstance( + source_pecha, Pecha + ), f"source_pecha is not an instance of Pecha, but {type(source_pecha)}" + assert isinstance( + target_pecha, Pecha + ), f"target_pecha is not an instance of Pecha, but {type(target_pecha)}" diff --git a/tests/pecha/test_pecha.py b/tests/pecha/test_pecha.py new file mode 100644 index 0000000..47f8e18 --- /dev/null +++ b/tests/pecha/test_pecha.py @@ -0,0 +1,77 @@ +from pathlib import Path +from shutil import rmtree + +from openpecha.pecha import Pecha +from openpecha.pecha.annotation import Annotation + + +def get_data_dir(): + export_path = Path(__file__).parent / "data" + export_path.mkdir(parents=True, exist_ok=True) + return export_path + + +def get_segments(): + return { + "f2b056668a0c4ad3a085bdcd8e2d7adb": "རྒྱ་གར་སྐད་དུ། བོ་དྷི་སཏྭ་ཙརྱ་ཨ་བ་ཏཱ་ར།", + "b696df2dbe314e8a87881a2bc391d0d5": "བོད་སྐད་དུ། བྱང་ཆུབ་སེམས་དཔའི་སྤྱོད་པ་ལ་འཇུག་པའི་ལེགས་པར་སྦྱར་བ།", + } + + +def get_metadata(): + return { + "annotation_category": "Structure Type", + "annotation_label": "Segment", + } + + +def get_expected_annotations(): + expected_annotations = [ + Annotation( + id_="f2b056668a0c4ad3a085bdcd8e2d7adb", + segment="རྒྱ་གར་སྐད་དུ། བོ་དྷི་སཏྭ་ཙརྱ་ཨ་བ་ཏཱ་ར།", + start=0, + end=39, + metadata={}, + ), + Annotation( + id_="b696df2dbe314e8a87881a2bc391d0d5", + segment="བོད་སྐད་དུ། བྱང་ཆུབ་སེམས་དཔའི་སྤྱོད་པ་ལ་འཇུག་པའི་ལེགས་པར་སྦྱར་བ།", + start=39, + end=103, + metadata={}, + ), + ] + return expected_annotations + + +def test_pecha_set_annotations(): + pecha_id = "IE7D6875F" + segments = get_segments() + metadata = get_metadata() + pecha = Pecha(pecha_id=pecha_id, segments=segments, metadata=metadata) + assert isinstance( + pecha, Pecha + ), "Not able to create Pecha object with id, segments and metadata" + + annotations = list(pecha.set_annotations()) + assert ( + annotations == get_expected_annotations() + ), "Pecha not able to set annotations for the segments" + + +def test_pecha_write_annotations(): + pecha_id = "IE7D6875F" + segments = get_segments() + metadata = get_metadata() + pecha = Pecha(pecha_id=pecha_id, segments=segments, metadata=metadata) + export_path = get_data_dir() + pecha.write_annotations(export_path=export_path) + assert pecha.base_fn.exists(), "Pecha not able to write base file" + assert pecha.metadata_fn.exists(), "Pecha not able to write metadata file" + assert pecha.annotation_fn.rglob( + "*.json" + ), "Pecha not able to write annotation file" + + """ clean up """ + rmtree(Path(export_path / pecha_id)) diff --git a/tests/test_ids.py b/tests/test_ids.py new file mode 100644 index 0000000..39f6913 --- /dev/null +++ b/tests/test_ids.py @@ -0,0 +1,78 @@ +import re + +from openpecha.ids import ( + get_alignment_id, + get_base_id, + get_collection_id, + get_diplomatic_id, + get_id, + get_initial_pecha_id, + get_open_pecha_id, + get_uuid, + get_work_id, +) + + +def test_get_uuid(): + uuid = get_uuid() + assert re.match( + r"^[0-9a-fA-F]{32}$", uuid + ), f"UUID {uuid} is not in the correct format" + + +def test_get_id(): + prefix = "T" + length = 4 + generated_id = get_id(prefix, length) + assert re.match( + r"^T[0-9A-F]{4}$", generated_id + ), f"ID {generated_id} is not in the correct format" + + +def test_get_base_id(): + base_id = get_base_id() + assert re.match( + r"^[0-9A-F]{4}$", base_id + ), f"Base ID {base_id} is not in the correct format" + + +def test_get_initial_pecha_id(): + initial_pecha_id = get_initial_pecha_id() + assert re.match( + r"^I[0-9A-F]{8}$", initial_pecha_id + ), f"Initial Pecha ID {initial_pecha_id} is not in the correct format" + + +def test_get_open_pecha_id(): + open_pecha_id = get_open_pecha_id() + assert re.match( + r"^O[0-9A-F]{8}$", open_pecha_id + ), f"Open Pecha ID {open_pecha_id} is not in the correct format" + + +def test_get_diplomatic_id(): + diplomatic_id = get_diplomatic_id() + assert re.match( + r"^D[0-9A-F]{8}$", diplomatic_id + ), f"Diplomatic ID {diplomatic_id} is not in the correct format" + + +def test_get_work_id(): + work_id = get_work_id() + assert re.match( + r"^W[0-9A-F]{8}$", work_id + ), f"Work ID {work_id} is not in the correct format" + + +def test_get_alignment_id(): + alignment_id = get_alignment_id() + assert re.match( + r"^A[0-9A-F]{8}$", alignment_id + ), f"Alignment ID {alignment_id} is not in the correct format" + + +def test_get_collection_id(): + collection_id = get_collection_id() + assert re.match( + r"^C[0-9A-F]{8}$", collection_id + ), f"Collection ID {collection_id} is not in the correct format"