Skip to content

Commit

Permalink
Merge pull request #2 from OpenPecha/feat/write-pecha-annotation
Browse files Browse the repository at this point in the history
Feat/write pecha annotation
  • Loading branch information
kaldan007 committed Jul 4, 2024
2 parents f643642 + 12a379f commit 0652a92
Show file tree
Hide file tree
Showing 18 changed files with 508 additions and 52 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pechas/
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
Expand Down
1 change: 0 additions & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
10 changes: 8 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 = [
Expand All @@ -17,6 +17,12 @@ classifiers = [
"Operating System :: OS Independent",
]

dependencies = [
"pydantic >= 2.7.4",
"stam == 0.8.2",

]

[project.optional-dependencies]
dev = [
"pytest",
Expand Down
36 changes: 36 additions & 0 deletions src/openpecha/alignment/__init__.py
Original file line number Diff line number Diff line change
@@ -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
36 changes: 0 additions & 36 deletions src/openpecha/alignment/alignment.py

This file was deleted.

79 changes: 68 additions & 11 deletions src/openpecha/alignment/parsers/plaintext.py
Original file line number Diff line number Diff line change
@@ -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

Expand Down
2 changes: 0 additions & 2 deletions src/openpecha/alignment/pecha/pecha.py

This file was deleted.

16 changes: 16 additions & 0 deletions src/openpecha/config.py
Original file line number Diff line number Diff line change
@@ -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"
64 changes: 64 additions & 0 deletions src/openpecha/pecha/__init__.py
Original file line number Diff line number Diff line change
@@ -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,
)
15 changes: 15 additions & 0 deletions src/openpecha/pecha/annotation.py
Original file line number Diff line number Diff line change
@@ -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
76 changes: 76 additions & 0 deletions src/openpecha/pecha/layer.py
Original file line number Diff line number Diff line change
@@ -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))
File renamed without changes.
Empty file removed tests/__init__.py
Empty file.
5 changes: 5 additions & 0 deletions tests/alignment/parsers/plaintext/data/comments.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{D3874}༄༅༅། །རྒྱ་གར་སྐད་དུ། བོ་དྷི་སཏྭ་ཙཱརྱ་ཨ་བ་ཏཱ་ར་སང་ཀཱ་ར།
བོད་སྐད་དུ། བྱང་ཆུབ་སེམས་དཔའི་སྤྱོད་པ་ལ་འཇུག་པའི་ལེགས་པར་སྦྱར་བ།
བཅོམ་ལྡན་འདས་གསུང་གི་མངའ་བདག་འཇམ་དཔལ་གཞོན་ནུར་གྱུར་པ་ལ་ཕྱག་འཚལ་ལོ། །ངོ་བོ་ཉིད་ནི་བྱང་ཆུབ་སེམས་པའི། །རྒྱ་མཚོ་དེ་ལ་ཕྱག་འཚལ་ཏེ། །བདག་འདྲའི་སྤྱོད་པའི་ཡན་ལག་ལ། །འཇུག་ཕྱིར་ལེགས་སྦྱར་བཤད་ཙམ་བྱ། །དམ་པ་རྣམས་ཀྱིས་ནི་ཐོག་མ་དང་བར་དང་ཐ་མར་དགེ་བ་མངོན་པར་འཕེལ་བར་བྱ་བ་ཡིན་པས། བདེ་གཤེགས་ཞེས་བྱ་བ་ལ་སོགས་པ་སྨོས་པ་ཡིན་ཏེ། འདིར་ཕྱག་འཚལ་བ་ནི་དང་པོར་དགེ་བའོ། །ཆོས་བསྟན་པ་ནི་བར་དུ་དགེ་བའོ། །དགེ་བའི་རྩ་བ་ཡོངས་སུ་བསྔོ་བ་ནི་དགེ་བའི་རྩ་བ་མངོན་པར་འཕེལ་བ་ཡིན་པས་ཐ་མར་དགེ་བ་ཡིན་ནོ། །དེ་ལ་བདེ་བར་གཤེགས་པ་ནི་རྟོགས་པར་བྱ་བའི་ལྷག་མ་མི་མངའ་བས་ན་ཡོངས་སུ་རྫོགས་པར་ཐུགས་སུ་ཆུད་པའི་ཕྱིར་བདེ་བར་གཤེགས་པའོ། །ཆོས་ཀྱི་སྐུ་མངའ་བ་ནི་ལུང་དང་རྟོགས་པའི་བདག་ཉིད་ཅན་གྱི་དམ་པའི་ཆོས་ཀྱི་ཚོགས་ནི་ཆོས་ཀྱི་སྐུ་སྟེ་དེ་དང་བཅས་པའོ། །སྲས་བཅས་ནི་ཉིད་ལས་འཁྲུངས་པའི་སྲས་ཏེ། བྱང་ཆུབ་སེམས་དཔའ་དང་བཅས་པའོ། །ལ་ལ་ལས་ནི་བདེ་གཤེགས་དམ་པའི་ཆོས་དང་དགེ་འདུན་བཅས་ཞེས་ཟེར་རོ། །བཙུན་པ་ནི་ཉན་ཐོས་ཆེན་པོ་བརྒྱད་ལ་སོགས་པ་ལ་བྱ་སྟེ། དེ་དག་མ་ལུས་པ་ཀུན་ལ་ཕྱག་འཚལ་བའོ། །དཀོན་མཆོག་གསུམ་པོ་གཙོ་བོར་གྱུར་པས་སོ་སོར་སྨོས་པ་ཡིན་ལ། དེ་དག་ཀྱང་ཕྱག་བྱ་བར་འོས་པ་ཡིན་པས་གུས་པས་ཕྱག་འཚལ་ཏེ། ཞེས་བྱ་བ་སྨོས་ཏེ། འདིར་ཡོན་ཏན་དམ་པའི་བསྟོད་པ་རྒྱ་ཆེ་བ་དང་། མཆོད་པ་ཁྱད་པར་དུ་འཕགས་པའི་དམིགས་པ་ཡིད་ལ་བྱེད་པ་ལས་བྱུང་བའི་མོས་པའི་བསམ་པ་ཤིན་ཏུ་ཕུལ་དུ་བྱུང་བའི་དགའ་བ་རྒྱ་ཆེ་བའི་མཆོད་པ་དང་བཅས་པས་ལུས་ཞིང་ཐམས་ཅད་ཀྱི་རྡུལ་སྙེད་ཀྱིས་བཏུད་ཅིང་ཕྱག་འཚལ་ལོ། །དེ་ལྟར་ཕྱག་བཙལ་ནས་ཅི་ཞིག་བྱེད་ཅེ་ན། བདེ་གཤེགས་སྲས་ཀྱི་ཞེས་བྱ་བ་ལ་སོགས་པ་སྨོས་ཏེ། བདེ་བར་གཤེགས་པའི་བདག་ཉིད་ནི་ཆོས་ཀྱི་སྐུ་སྟེ། དེའི་དབང་དུ་བྱས་པ་ལས་སྐྱེས་པ་ས་ཆེན་པོ་ཐོབ་པ་དང་། རྒྱུ་ལ་གནས་པ་རྣམས་སོ། །དེ་རྣམས་ཀྱི་སྡོམ་པ་ནི་མི་དགེ་བ་སྤོང་བ་དང་། དགེ་བ་ལ་འཇུག་པ་དང་། སེམས་ཅན་གྱི་དོན་བྱ་བའོ། །དེ་ཡང་བཅོམ་ལྡན་འདས་ཀྱིས་ཤིན་ཏུ་ཟབ་ཅིང་རྒྱ་ཆེ་བའི་བདག་ཉིད་ཅན་དུ་གསུངས་ལ། དེར་བྱང་ཆུབ་ཏུ་སེམས་བསྐྱེད་པའི་ཕན་ཡོན་ལ་སོགས་པའི་དོན་རྣམ་པ་བཅུ་པོ་གང་ཡིན་པ་དེས་འཇུག་པའི་བདེ་བར་གཤེགས་པའི་སྲས་ཀྱི་སྡོམ་པ་ལ་འཇུག་པ་བསྟན་པར་བྱའོ། །དེ་ཡང་ལུང་བཞིན་ཞེས་བྱ་བ་སྟེ། ལུང་གི་དོན་དང་མི་འགལ་བར་རོ། །ལུང་ལས་ནི་བཅོམ་ལྡན་འདས་ཀྱིས་རྒྱ་ཆེར་གསུངས་སོ་ཞེ་ན། མདོར་བསྡུས་ནས་ནི་ཞེས་བྱ་བ་སྨོས་སོ། །དེ་ལྟ་ཡིན་དུ་ཆུག་ན། ཅི་འདིར་སྔོན་ཆད་མ་བྱུང་བ་གཞན་འགའ་ཞིག་སྨས་སམ།

ལུང་ཇི་ལྟ་བ་ཡིན་ཞེ་ན། སྔོན་ཆད་ཅེས་བྱ་བ་ལ་སོགས་པ་སྨོས་སོ། །སྡེབ་སྦྱོར་མཁས་པས་སྔོན་མ་ཡིན་ནམ་ཞེ་ན། སྡེབ་སྦྱོར་ཞེས་བྱ་བ་ལ་སོགས་པ་སྨོས་སོ། །གང་གི་ཕྱིར་འདིར་སྡེབ་སྦྱོར་ལ་མཁས་པ་མེད་པ་ཉིད་ཀྱི་ཕྱིར་གཞན་གྱི་དོན་དུ་བདག་གིས་འདི་གཞུང་དུ་ཉེ་བར་སྦྱར་བ་མ་བྱས་སོ་ཞེས་བྱ་བར་དགོངས་སོ། །དེ་ལྟར་གལ་ཏེ་གཞན་གྱི་དོན་དུ་མ་བྱས་ན་ཅིའི་ཕྱིར་བྱེད་ཅེ་ན། དེའི་ཕྱིར་རང་གི་ཞེས་བྱ་བ་ལ་སོགས་པ་སྨོས་ཏེ། ཡིད་ལ་འདིར་བྱང་ཆུབ་ཀྱི་སེམས་ཏེ། དེ་བསྒོམ་པའི་ཕྱིར་ཞེས་བྱ་བ་ནི་བསླབ་པའི་ཕྱིར་རོ། །བཅོམ་ལྡན་འདས་ཀྱིས་ལུང་ལས་ཚིག་གི་དོན་རྒྱ་ཆེར་གསུངས་པ་དེ་ལས་མདོར་བསྡུས་ཏེ་རང་གི་ཡིད་ལ་བསྒོམ་པར་བྱ་བའི་ཕྱིར་བདག་གིས་འདི་བྱས་སོ་ཞེས་པའོ། །
Loading

0 comments on commit 0652a92

Please sign in to comment.