diff --git a/migrations/env.py b/migrations/env.py index c269b94fd..0aeea2db7 100644 --- a/migrations/env.py +++ b/migrations/env.py @@ -37,6 +37,7 @@ from pecha_api.events.event_metadata_model import EventMetadata from pecha_api.mantra.mantra_metadata_model import MantraMetadata from pecha_api.traditions.tradition_models import Tradition, TraditionMetadata, UserTradition +from pecha_api.region_restrictions.region_restriction_models import ChinaRestrictedItem # this is the Alembic Config object, which provides # access to the values within the .ini file in use. diff --git a/migrations/versions/g2h3i4j5k6l7_add_china_restricted_items_table.py b/migrations/versions/g2h3i4j5k6l7_add_china_restricted_items_table.py new file mode 100644 index 000000000..87b081174 --- /dev/null +++ b/migrations/versions/g2h3i4j5k6l7_add_china_restricted_items_table.py @@ -0,0 +1,111 @@ +"""add china_restricted_items table for China timezone content exclusions + +Revision ID: g2h3i4j5k6l7 +Revises: f1a2b3c4d5e7 +Create Date: 2026-07-05 12:00:00.000000 + +Items listed in this table are hidden from clients whose X-Timezone header +matches a Chinese IANA timezone (see pecha_api/assets/chinese_timezone.json). +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import postgresql + +from migrations.idempotency import index_exists, table_exists + +revision: str = "g2h3i4j5k6l7" +down_revision: Union[str, None] = "f1a2b3c4d5e7" +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + +china_restricted_item_type = postgresql.ENUM( + "MANTRA", + "ACCUMULATOR", + "GROUP_ACCUMULATOR", + "PLAN", + "SERIES", + "GROUP", + "RECITATION", + "RECITATION_COLLECTION", + name="china_restricted_item_type", + create_type=False, +) + + +def upgrade() -> None: + op.execute( + """ + DO $$ BEGIN + CREATE TYPE china_restricted_item_type AS ENUM ( + 'MANTRA', + 'ACCUMULATOR', + 'GROUP_ACCUMULATOR', + 'PLAN', + 'SERIES', + 'GROUP', + 'RECITATION', + 'RECITATION_COLLECTION' + ); + EXCEPTION + WHEN duplicate_object THEN NULL; + END $$; + """ + ) + + if not table_exists("china_restricted_items"): + op.create_table( + "china_restricted_items", + sa.Column( + "id", + sa.UUID(), + nullable=False, + server_default=sa.text("gen_random_uuid()"), + ), + sa.Column("item_type", china_restricted_item_type, nullable=False), + sa.Column("item_id", sa.UUID(), nullable=False), + sa.Column( + "created_at", + sa.DateTime(timezone=True), + nullable=False, + server_default=sa.text("CURRENT_TIMESTAMP"), + ), + sa.Column( + "updated_at", + sa.DateTime(timezone=True), + nullable=True, + server_default=sa.text("CURRENT_TIMESTAMP"), + ), + sa.PrimaryKeyConstraint("id"), + sa.UniqueConstraint( + "item_type", + "item_id", + name="uq_china_restricted_items_type_id", + ), + ) + + if not index_exists("china_restricted_items", "idx_china_restricted_items_item_type"): + op.create_index( + "idx_china_restricted_items_item_type", + "china_restricted_items", + ["item_type"], + unique=False, + ) + if not index_exists("china_restricted_items", "idx_china_restricted_items_item_id"): + op.create_index( + "idx_china_restricted_items_item_id", + "china_restricted_items", + ["item_id"], + unique=False, + ) + + +def downgrade() -> None: + if index_exists("china_restricted_items", "idx_china_restricted_items_item_id"): + op.drop_index("idx_china_restricted_items_item_id", table_name="china_restricted_items") + if index_exists("china_restricted_items", "idx_china_restricted_items_item_type"): + op.drop_index("idx_china_restricted_items_item_type", table_name="china_restricted_items") + if table_exists("china_restricted_items"): + op.drop_table("china_restricted_items") + op.execute("DROP TYPE IF EXISTS china_restricted_item_type") diff --git a/pecha_api/accumulator/accumulator_repository.py b/pecha_api/accumulator/accumulator_repository.py index c239f71e5..2a882c10a 100644 --- a/pecha_api/accumulator/accumulator_repository.py +++ b/pecha_api/accumulator/accumulator_repository.py @@ -208,28 +208,41 @@ def add_history_row(db: Session, accumulator_id: UUID, user_id: UUID, count: int ) +def get_user_total_counted_by_parent( + db: Session, + user_id: UUID, + parent_id: UUID, +) -> int: + """Sum of all history counts across every accumulator the user has had + for this preset, including soft-deleted instances.""" + total = ( + db.query(func.sum(AccumulatorHistory.count)) + .join(Accumulator, AccumulatorHistory.accumulator_id == Accumulator.id) + .filter( + Accumulator.user_id == user_id, + Accumulator.parent_id == parent_id, + AccumulatorHistory.user_id == user_id, + ) + .scalar() + ) + return total or 0 + + def get_accumulator_with_history( db: Session, user_id: UUID, parent_id: UUID, ) -> Optional[Tuple[Accumulator, int, List[AccumulatorHistory]]]: """Fetch the user's active accumulator created from a given preset - (parent_id), along with their total counted and ordered session rows. + (parent_id), along with their lifetime total counted for that preset and + ordered session rows for the active accumulator. Returns None if the user has no accumulator for that preset.""" accumulator = get_user_accumulator_by_parent(db, user_id, parent_id) if not accumulator: return None accumulator_id = accumulator.id - - total_counted = ( - db.query(func.sum(AccumulatorHistory.count)) - .filter( - AccumulatorHistory.accumulator_id == accumulator_id, - AccumulatorHistory.user_id == user_id - ) - .scalar() - ) or 0 + total_counted = get_user_total_counted_by_parent(db, user_id, parent_id) sessions = ( db.query(AccumulatorHistory) diff --git a/pecha_api/accumulator/accumulator_service.py b/pecha_api/accumulator/accumulator_service.py index 0aed2db78..9fdd62d86 100644 --- a/pecha_api/accumulator/accumulator_service.py +++ b/pecha_api/accumulator/accumulator_service.py @@ -53,6 +53,10 @@ from .accumulator_metadata_model import AccumulatorMetadata from .accumulator_history_model import AccumulatorHistory from .accumulator_enums import AccumulatorType +from pecha_api.region_restrictions.region_restriction_enums import RestrictedItemType +from pecha_api.region_restrictions.region_restriction_service import ( + filter_items_for_timezone, +) from .response_message import ( NOT_FOUND, FORBIDDEN, @@ -329,15 +333,22 @@ def get_all_accumulators_service( limit: int = 20, language: Optional[str] = None, search: Optional[str] = None, + timezone_name: Optional[str] = None, ) -> PublicAccumulatorsResponse: with SessionLocal() as db: accumulators, total = get_all_accumulators(db, skip, limit, search=search) - mantra_ids = [a.mantra_id for a in accumulators if a.mantra_id is not None] + visible_accumulators = filter_items_for_timezone( + accumulators, + timezone_name=timezone_name, + item_type=RestrictedItemType.ACCUMULATOR, + id_of=lambda accumulator: accumulator.id, + ) + mantra_ids = [a.mantra_id for a in visible_accumulators if a.mantra_id is not None] mantras_by_id = get_mantras_by_ids(db, mantra_ids) return PublicAccumulatorsResponse( accumulators=[ convert_accumulator_to_public_dto(a, mantras_by_id, language) - for a in accumulators + for a in visible_accumulators ], total=total, skip=skip, diff --git a/pecha_api/accumulator/accumulator_views.py b/pecha_api/accumulator/accumulator_views.py index 97e6f5a8e..c52926c0b 100644 --- a/pecha_api/accumulator/accumulator_views.py +++ b/pecha_api/accumulator/accumulator_views.py @@ -1,4 +1,4 @@ -from fastapi import APIRouter, Depends, Query +from fastapi import APIRouter, Depends, Header, Query from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials from typing import Annotated, Optional from uuid import UUID @@ -49,8 +49,18 @@ async def get_all_preset_accumulators( Optional[str], Query(description="Filter presets by mantra text, title, or pronunciation (case-insensitive, any language)"), ] = None, + x_timezone: Annotated[ + Optional[str], + Header(alias="X-Timezone", description="IANA timezone (e.g. Asia/Shanghai). Restricted accumulators are hidden for Chinese timezones."), + ] = None, ): - return get_all_accumulators_service(skip=skip, limit=limit, language=language, search=search) + return get_all_accumulators_service( + skip=skip, + limit=limit, + language=language, + search=search, + timezone_name=x_timezone, + ) @accumulator_router.get("/user", response_model=AccumulatorsResponse) diff --git a/pecha_api/app.py b/pecha_api/app.py index 30fe327fb..2540968aa 100644 --- a/pecha_api/app.py +++ b/pecha_api/app.py @@ -65,6 +65,7 @@ from pecha_api.events import events_router, cms_events_router from pecha_api.traditions import tradition_views from pecha_api.plans.admin.admin_views import cms_admin_router +from pecha_api.region_restrictions.region_restriction_views import cms_china_restrictions_router from pecha_api.plans.transfers.transfer_views import ( cms_transfers_router, group_transfers_router, @@ -103,6 +104,7 @@ api.include_router(author_groups_views.cms_groups_router) api.include_router(cms_notification_views.cms_notifications_router) api.include_router(cms_admin_router) +api.include_router(cms_china_restrictions_router) api.include_router(cms_transfers_router) api.include_router(group_transfers_router) api.include_router(plan_transfers_router) diff --git a/pecha_api/daily_log/daily_log_cache_service.py b/pecha_api/daily_log/daily_log_cache_service.py index a33de6801..70e53ef18 100644 --- a/pecha_api/daily_log/daily_log_cache_service.py +++ b/pecha_api/daily_log/daily_log_cache_service.py @@ -1,5 +1,5 @@ import asyncio -from datetime import date, datetime, timedelta, timezone +from datetime import date, datetime, timezone from typing import Optional from uuid import UUID @@ -7,6 +7,7 @@ from pecha_api.cache.cache_enums import CacheType from pecha_api.cache.cache_repository import delete_cache, exists_in_cache, get_cache_data, set_cache from pecha_api.daily_log.daily_log_response_models import UserStatsResponse +from pecha_api.timezone_utils import get_day_bounds_in_timezone from pecha_api.utils import Utils _DAILY_LOG_CACHE_VALUE = "logged" @@ -17,10 +18,17 @@ def _build_daily_log_cache_key(user_id: UUID, log_date: date) -> str: return Utils.generate_hash_key(payload) -def _seconds_until_end_of_utc_day() -> int: +def _timezone_cache_suffix(timezone_name: Optional[str]) -> str: + if not timezone_name or not timezone_name.strip(): + return "UTC" + return timezone_name.strip() + + +def _seconds_until_end_of_day_in_timezone(timezone_name: Optional[str]) -> int: now = datetime.now(timezone.utc) - tomorrow = (now + timedelta(days=1)).replace(hour=0, minute=0, second=0, microsecond=0) - return max(int((tomorrow - now).total_seconds()), 1) + _, day_end = get_day_bounds_in_timezone(timezone_name, at=now) + now_in_timezone = now.astimezone(day_end.tzinfo) + return max(int((day_end - now_in_timezone).total_seconds()), 1) async def is_user_logged_today_in_cache(user_id: UUID, log_date: date) -> bool: @@ -28,43 +36,80 @@ async def is_user_logged_today_in_cache(user_id: UUID, log_date: date) -> bool: return await exists_in_cache(cache_key) -async def set_user_daily_log_cache(user_id: UUID, log_date: date) -> None: +async def set_user_daily_log_cache( + user_id: UUID, + log_date: date, + timezone_name: Optional[str] = None, +) -> None: cache_key = _build_daily_log_cache_key(user_id=user_id, log_date=log_date) await set_cache( hash_key=cache_key, value=_DAILY_LOG_CACHE_VALUE, - cache_time_out=_seconds_until_end_of_utc_day(), + cache_time_out=_seconds_until_end_of_day_in_timezone(timezone_name), ) -def _build_user_stats_cache_key(user_id: UUID) -> str: - payload = [str(user_id), CacheType.USER_STATS.value] +def _build_user_stats_cache_key( + user_id: UUID, + timezone_name: Optional[str] = None, +) -> str: + payload = [ + str(user_id), + CacheType.USER_STATS.value, + _timezone_cache_suffix(timezone_name), + ] return Utils.generate_hash_key(payload) -async def get_user_stats_cache(user_id: UUID) -> Optional[UserStatsResponse]: - cache_key = _build_user_stats_cache_key(user_id=user_id) +async def get_user_stats_cache( + user_id: UUID, + timezone_name: Optional[str] = None, +) -> Optional[UserStatsResponse]: + cache_key = _build_user_stats_cache_key( + user_id=user_id, + timezone_name=timezone_name, + ) cache_data = await get_cache_data(hash_key=cache_key) if cache_data and isinstance(cache_data, dict): return UserStatsResponse(**cache_data) return None -async def set_user_stats_cache(user_id: UUID, data: UserStatsResponse) -> None: - cache_key = _build_user_stats_cache_key(user_id=user_id) +async def set_user_stats_cache( + user_id: UUID, + data: UserStatsResponse, + timezone_name: Optional[str] = None, +) -> None: + cache_key = _build_user_stats_cache_key( + user_id=user_id, + timezone_name=timezone_name, + ) cache_time_out = config.get_int("CACHE_USER_STATS_TIMEOUT") await set_cache(hash_key=cache_key, value=data, cache_time_out=cache_time_out) -async def invalidate_user_stats_cache(user_id: UUID) -> None: - cache_key = _build_user_stats_cache_key(user_id=user_id) +async def invalidate_user_stats_cache( + user_id: UUID, + timezone_name: Optional[str] = None, +) -> None: + cache_key = _build_user_stats_cache_key( + user_id=user_id, + timezone_name=timezone_name, + ) await delete_cache(hash_key=cache_key) -def schedule_invalidate_user_stats_cache(user_id: UUID) -> None: +def schedule_invalidate_user_stats_cache( + user_id: UUID, + timezone_name: Optional[str] = None, +) -> None: """Invalidate stats cache from sync callers without blocking.""" try: loop = asyncio.get_running_loop() - loop.create_task(invalidate_user_stats_cache(user_id)) + loop.create_task( + invalidate_user_stats_cache(user_id=user_id, timezone_name=timezone_name) + ) except RuntimeError: - asyncio.run(invalidate_user_stats_cache(user_id)) + asyncio.run( + invalidate_user_stats_cache(user_id=user_id, timezone_name=timezone_name) + ) diff --git a/pecha_api/daily_log/daily_log_service.py b/pecha_api/daily_log/daily_log_service.py index 5431763fd..4b1593301 100644 --- a/pecha_api/daily_log/daily_log_service.py +++ b/pecha_api/daily_log/daily_log_service.py @@ -1,10 +1,11 @@ -from datetime import date, datetime, timedelta, timezone -from typing import Set +from datetime import date, timedelta +from typing import Optional, Set from uuid import UUID from sqlalchemy.orm import Session from pecha_api.db.database import SessionLocal +from pecha_api.timezone_utils import get_date_in_timezone from pecha_api.daily_log.daily_log_cache_service import ( get_user_stats_cache, invalidate_user_stats_cache, @@ -28,12 +29,11 @@ from pecha_api.users.users_service import validate_and_extract_user_details -def _utc_today() -> date: - return datetime.now(timezone.utc).date() +def _today_for_timezone(timezone_name: Optional[str] = None) -> date: + return get_date_in_timezone(timezone_name) -def calculate_streak(log_dates: Set[date]) -> int: - today = _utc_today() +def calculate_streak(log_dates: Set[date], today: date) -> int: yesterday = today - timedelta(days=1) if today not in log_dates and yesterday not in log_dates: @@ -49,38 +49,64 @@ def calculate_streak(log_dates: Set[date]) -> int: return streak -async def record_daily_log_if_needed(user_id: UUID, db: Session | None = None) -> None: - today = _utc_today() +async def record_daily_log_if_needed( + user_id: UUID, + db: Session | None = None, + timezone_name: Optional[str] = None, +) -> None: + today = _today_for_timezone(timezone_name=timezone_name) if await is_user_logged_today_in_cache(user_id=user_id, log_date=today): return if db is not None: if has_log_for_date(db=db, user_id=user_id, log_date=today): - await set_user_daily_log_cache(user_id=user_id, log_date=today) + await set_user_daily_log_cache( + user_id=user_id, + log_date=today, + timezone_name=timezone_name, + ) return save_daily_log(db=db, user_id=user_id, log_date=today) - await set_user_daily_log_cache(user_id=user_id, log_date=today) - await invalidate_user_stats_cache(user_id=user_id) + await set_user_daily_log_cache( + user_id=user_id, + log_date=today, + timezone_name=timezone_name, + ) + await invalidate_user_stats_cache(user_id=user_id, timezone_name=timezone_name) return with SessionLocal() as session: if has_log_for_date(db=session, user_id=user_id, log_date=today): - await set_user_daily_log_cache(user_id=user_id, log_date=today) + await set_user_daily_log_cache( + user_id=user_id, + log_date=today, + timezone_name=timezone_name, + ) return save_daily_log(db=session, user_id=user_id, log_date=today) - await set_user_daily_log_cache(user_id=user_id, log_date=today) - await invalidate_user_stats_cache(user_id=user_id) + await set_user_daily_log_cache( + user_id=user_id, + log_date=today, + timezone_name=timezone_name, + ) + await invalidate_user_stats_cache(user_id=user_id, timezone_name=timezone_name) -async def get_user_streak_service(token: str) -> UserStreakResponse: +async def get_user_streak_service( + token: str, + timezone_name: Optional[str] = None, +) -> UserStreakResponse: current_user = validate_and_extract_user_details(token=token) - today = _utc_today() + today = _today_for_timezone(timezone_name=timezone_name) - await record_daily_log_if_needed(user_id=current_user.id) + await record_daily_log_if_needed( + user_id=current_user.id, + timezone_name=timezone_name, + ) with SessionLocal() as db: streak = get_user_streak(db=db, user_id=current_user.id, today=today) @@ -88,15 +114,25 @@ async def get_user_streak_service(token: str) -> UserStreakResponse: return UserStreakResponse(streak=streak) -async def get_user_stats_service(token: str) -> UserStatsResponse: +async def get_user_stats_service( + token: str, + timezone_name: Optional[str] = None, +) -> UserStatsResponse: current_user = validate_and_extract_user_details(token=token) - today = _utc_today() + today = _today_for_timezone(timezone_name=timezone_name) user_id = current_user.id with SessionLocal() as db: - await record_daily_log_if_needed(user_id=user_id, db=db) + await record_daily_log_if_needed( + user_id=user_id, + db=db, + timezone_name=timezone_name, + ) - cached_stats = await get_user_stats_cache(user_id=user_id) + cached_stats = await get_user_stats_cache( + user_id=user_id, + timezone_name=timezone_name, + ) if cached_stats is not None: return cached_stats @@ -118,5 +154,9 @@ async def get_user_stats_service(token: str) -> UserStatsResponse: total_accumulated=total_accumulated, total_practice_days=total_practice_days, ) - await set_user_stats_cache(user_id=user_id, data=stats) + await set_user_stats_cache( + user_id=user_id, + data=stats, + timezone_name=timezone_name, + ) return stats diff --git a/pecha_api/daily_log/daily_log_views.py b/pecha_api/daily_log/daily_log_views.py index e08a125a2..52c7e154f 100644 --- a/pecha_api/daily_log/daily_log_views.py +++ b/pecha_api/daily_log/daily_log_views.py @@ -1,6 +1,6 @@ -from typing import Annotated +from typing import Annotated, Optional -from fastapi import APIRouter, Depends +from fastapi import APIRouter, Depends, Header from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer from starlette import status @@ -16,12 +16,26 @@ @daily_log_router.get("/streak", status_code=status.HTTP_200_OK, response_model=UserStreakResponse) async def get_user_streak( authentication_credential: Annotated[HTTPAuthorizationCredentials, Depends(oauth2_scheme)], + x_timezone: Annotated[ + Optional[str], + Header(alias="X-Timezone", description="IANA timezone for determining today's date."), + ] = None, ) -> UserStreakResponse: - return await get_user_streak_service(token=authentication_credential.credentials) + return await get_user_streak_service( + token=authentication_credential.credentials, + timezone_name=x_timezone, + ) @daily_log_router.get("/stats", status_code=status.HTTP_200_OK, response_model=UserStatsResponse) async def get_user_stats( authentication_credential: Annotated[HTTPAuthorizationCredentials, Depends(oauth2_scheme)], + x_timezone: Annotated[ + Optional[str], + Header(alias="X-Timezone", description="IANA timezone for determining today's date."), + ] = None, ) -> UserStatsResponse: - return await get_user_stats_service(token=authentication_credential.credentials) + return await get_user_stats_service( + token=authentication_credential.credentials, + timezone_name=x_timezone, + ) diff --git a/pecha_api/group_accumulator/group_accumulator_service.py b/pecha_api/group_accumulator/group_accumulator_service.py index 973dcc1ef..1a9e7673b 100644 --- a/pecha_api/group_accumulator/group_accumulator_service.py +++ b/pecha_api/group_accumulator/group_accumulator_service.py @@ -22,6 +22,11 @@ ) from pecha_api.uploads.S3_utils import generate_presigned_access_url from pecha_api.users.users_models import Users +from pecha_api.region_restrictions.region_restriction_enums import RestrictedItemType +from pecha_api.region_restrictions.region_restriction_service import ( + assert_visible_for_timezone, + filter_items_for_timezone, +) from .group_accumulator_repository import ( create_group_accumulator, get_group_accumulators, @@ -193,9 +198,16 @@ def get_group_accumulators_service( skip: int = 0, limit: int = 20, token: Optional[str] = None, + timezone_name: Optional[str] = None, ) -> GroupAccumulatorsResponse: with SessionLocal() as db: accumulators, total = get_group_accumulators(db, group_id, skip, limit) + accumulators = filter_items_for_timezone( + accumulators, + timezone_name=timezone_name, + item_type=RestrictedItemType.GROUP_ACCUMULATOR, + id_of=lambda accumulator: accumulator.id, + ) joined_ids: set[UUID] = set() if token: @@ -233,6 +245,12 @@ def get_group_accumulator_service( timezone_name: Optional[str] = None, token: Optional[str] = None, ) -> GroupAccumulatorDetailDTO: + assert_visible_for_timezone( + timezone_name=timezone_name, + item_type=RestrictedItemType.GROUP_ACCUMULATOR, + item_id=group_accumulator_id, + not_found_detail="Group accumulator not found", + ) with SessionLocal() as db: group_accumulator = get_group_accumulator_by_id(db, group_accumulator_id) if not group_accumulator: diff --git a/pecha_api/group_accumulator/group_accumulator_views.py b/pecha_api/group_accumulator/group_accumulator_views.py index f1156cb75..7402b5d52 100644 --- a/pecha_api/group_accumulator/group_accumulator_views.py +++ b/pecha_api/group_accumulator/group_accumulator_views.py @@ -42,6 +42,10 @@ async def get_group_accumulators( Optional[HTTPAuthorizationCredentials], Depends(optional_oauth2_scheme), ] = None, + x_timezone: Annotated[ + Optional[str], + Header(alias="X-Timezone", description="IANA timezone (e.g. Asia/Shanghai). Restricted group accumulators are hidden for Chinese timezones."), + ] = None, ): token = credentials.credentials if credentials else None return get_group_accumulators_service( @@ -49,6 +53,7 @@ async def get_group_accumulators( skip=skip, limit=limit, token=token, + timezone_name=x_timezone, ) diff --git a/pecha_api/mantra/mantra_service.py b/pecha_api/mantra/mantra_service.py index b6a049a0f..33e69d7c4 100644 --- a/pecha_api/mantra/mantra_service.py +++ b/pecha_api/mantra/mantra_service.py @@ -9,6 +9,8 @@ from ..plans.authors.plan_authors_service import validate_cms_author_details from .mantra_model import Mantra from .mantra_repository import get_all_mantras, save_mantra +from pecha_api.region_restrictions.region_restriction_enums import RestrictedItemType +from pecha_api.region_restrictions.region_restriction_service import filter_items_for_timezone from .mantra_response_models import ( CreateMantraRequest, MantraDTO, @@ -35,13 +37,22 @@ def _build_mantra_dto(mantra, language: Optional[str]) -> MantraDTO: ) -def get_mantras_service(language: Optional[str] = None) -> MantraResponse: +def get_mantras_service( + language: Optional[str] = None, + timezone_name: Optional[str] = None, +) -> MantraResponse: with SessionLocal() as db: mantras = get_all_mantras(db, language=language) + visible_mantras = filter_items_for_timezone( + mantras, + timezone_name=timezone_name, + item_type=RestrictedItemType.MANTRA, + id_of=lambda mantra: mantra.id, + ) return MantraResponse( - mantras=[_build_mantra_dto(mantra, language) for mantra in mantras] + mantras=[_build_mantra_dto(mantra, language) for mantra in visible_mantras] ) diff --git a/pecha_api/mantra/mantra_views.py b/pecha_api/mantra/mantra_views.py index 28168a85a..d5ad90368 100644 --- a/pecha_api/mantra/mantra_views.py +++ b/pecha_api/mantra/mantra_views.py @@ -1,4 +1,4 @@ -from fastapi import APIRouter, Depends, Query +from fastapi import APIRouter, Depends, Header, Query from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer from typing import Annotated, Optional from starlette import status @@ -27,9 +27,13 @@ ) def get_mantras_endpoint( language: Annotated[Optional[str], Query(description=language_query_description("Filter by language code", lowercase_example=True))] = None, + x_timezone: Annotated[ + Optional[str], + Header(alias="X-Timezone", description="IANA timezone (e.g. Asia/Shanghai). Restricted mantras are hidden for Chinese timezones."), + ] = None, ): - return get_mantras_service(language=language) + return get_mantras_service(language=language, timezone_name=x_timezone) @cms_mantra_router.post( diff --git a/pecha_api/plans/groups/groups_service.py b/pecha_api/plans/groups/groups_service.py index 898e418c9..897a69b18 100644 --- a/pecha_api/plans/groups/groups_service.py +++ b/pecha_api/plans/groups/groups_service.py @@ -130,6 +130,8 @@ from pecha_api.users.users_service import validate_and_extract_user_details from pecha_api.users.users_repository import get_users_by_ids from pecha_api.users.users_models import Users +from pecha_api.region_restrictions.region_restriction_enums import RestrictedItemType +from pecha_api.region_restrictions.region_restriction_service import filter_items_for_timezone GROUP_NOT_FOUND = "Group not found" INVITE_NOT_FOUND = "Invite not found" @@ -739,6 +741,27 @@ def update_author_group(token: str, group_id: UUID, request: UpdateAuthorGroupRe ) +def delete_author_group(token: str, group_id: UUID) -> None: + author = validate_and_extract_author_details(token=token) + with SessionLocal() as db: + group = get_group_by_id(db=db, group_id=group_id) + if not group: + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=GROUP_NOT_FOUND) + if not is_super_admin(author): + member = _get_member_or_403(db=db, group_id=group_id, author_id=author.id) + _assert_role_allowed( + member=member, + allowed_roles=[AuthorGroupMemberRole.OWNER], + ) + + now = datetime.now(timezone.utc) + group.deleted_at = now + group.deleted_by = author.email + group.updated_at = now + group.updated_by = author.email + update_group(db=db, group=group) + + def get_author_group_detail( group_id: UUID, require_public: bool = True, @@ -831,6 +854,7 @@ def list_public_groups( tag_id: Optional[UUID] = None, group_type: AuthorGroupType = AuthorGroupType.COMMUNITY, token: Optional[str] = None, + timezone_name: Optional[str] = None, ) -> PublicAuthorGroupListResponse: with SessionLocal() as db: exclude_group_ids = None @@ -852,6 +876,12 @@ def list_public_groups( is_public=True, group_type=group_type, ) + groups = filter_items_for_timezone( + groups, + timezone_name=timezone_name, + item_type=RestrictedItemType.GROUP, + id_of=lambda group: group.id, + ) group_ids = [group.id for group in groups] follower_count_map = get_followers_count_map(db=db, group_ids=group_ids) joiner_count_map = get_joiners_count_map(db=db, group_ids=group_ids) diff --git a/pecha_api/plans/groups/groups_views.py b/pecha_api/plans/groups/groups_views.py index db6108787..b56d0de36 100644 --- a/pecha_api/plans/groups/groups_views.py +++ b/pecha_api/plans/groups/groups_views.py @@ -1,7 +1,7 @@ from typing import Annotated, Optional from uuid import UUID -from fastapi import APIRouter, Depends, Query, Response +from fastapi import APIRouter, Depends, Header, Query, Response from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer from starlette import status @@ -35,6 +35,7 @@ accept_group_invite_by_id, create_author_group, create_group_member_invite, + delete_author_group, delete_group_member, follow_group, get_author_group_detail, @@ -130,6 +131,18 @@ def patch_cms_group( ) +@cms_groups_router.delete("/{group_id}", status_code=status.HTTP_204_NO_CONTENT) +def delete_cms_group( + group_id: UUID, + authentication_credential: Annotated[HTTPAuthorizationCredentials, Depends(oauth2_scheme)], +): + delete_author_group( + token=authentication_credential.credentials, + group_id=group_id, + ) + return Response(status_code=status.HTTP_204_NO_CONTENT) + + @cms_groups_router.get("/{group_id}", status_code=status.HTTP_200_OK, response_model=AuthorGroupDetailDTO) def get_cms_group( group_id: UUID, @@ -370,6 +383,10 @@ def get_public_groups( ] = AuthorGroupType.COMMUNITY, skip: Annotated[int, Query(ge=0)] = 0, limit: Annotated[int, Query(ge=1, le=100)] = 20, + x_timezone: Annotated[ + Optional[str], + Header(alias="X-Timezone", description="IANA timezone (e.g. Asia/Shanghai). Restricted groups are hidden for Chinese timezones."), + ] = None, ): return list_public_groups( search=search, @@ -379,6 +396,7 @@ def get_public_groups( skip=skip, limit=limit, token=authentication_credential.credentials if authentication_credential else None, + timezone_name=x_timezone, ) diff --git a/pecha_api/plans/public/plan_service.py b/pecha_api/plans/public/plan_service.py index fe9f150a6..21834bf42 100644 --- a/pecha_api/plans/public/plan_service.py +++ b/pecha_api/plans/public/plan_service.py @@ -38,6 +38,11 @@ ) from pecha_api.plans.groups.groups_repository import get_group_id_for_plan, get_group_ids_by_plan_ids from pecha_api.plans.tags.tag_helpers import tags_to_summary_dtos, generate_tag_image_url +from pecha_api.region_restrictions.region_restriction_enums import RestrictedItemType +from pecha_api.region_restrictions.region_restriction_service import ( + assert_visible_for_timezone, + filter_items_for_timezone, +) from pecha_api.plans.tags.tag_repository import get_published_tags_for_language, get_all_tags_paginated, get_tag_by_id from pecha_api.plans.tags.tag_response_models import PublicTagsListResponse from pecha_api.texts.segments.segments_repository import get_segments_by_ids @@ -73,7 +78,8 @@ async def get_published_plans( sort_by: str = "title", sort_order: str = "asc", skip: int = 0, - limit: int = 20 + limit: int = 20, + timezone_name: Optional[str] = None, ) -> PublicPlansResponse: try: @@ -90,6 +96,12 @@ async def get_published_plans( tag=tag, group_id=group_id, ) + plan_aggregates = filter_items_for_timezone( + plan_aggregates, + timezone_name=timezone_name, + item_type=RestrictedItemType.PLAN, + id_of=lambda aggregate: aggregate.plan.id, + ) plan_ids = [plan_aggregate.plan.id for plan_aggregate in plan_aggregates] group_id_by_plan_id = get_group_ids_by_plan_ids(db=db, plan_ids=plan_ids) @@ -144,9 +156,18 @@ async def get_published_plans( ) -async def get_published_plan(plan_id: UUID) -> PublicPlanDTO: +async def get_published_plan( + plan_id: UUID, + timezone_name: Optional[str] = None, +) -> PublicPlanDTO: try: + assert_visible_for_timezone( + timezone_name=timezone_name, + item_type=RestrictedItemType.PLAN, + item_id=plan_id, + not_found_detail=ErrorConstants.PLAN_NOT_FOUND, + ) with SessionLocal() as db: plan = get_published_plan_by_id(db=db, plan_id=plan_id) diff --git a/pecha_api/plans/public/plan_views.py b/pecha_api/plans/public/plan_views.py index b82552786..df11d3ac9 100644 --- a/pecha_api/plans/public/plan_views.py +++ b/pecha_api/plans/public/plan_views.py @@ -1,4 +1,4 @@ -from fastapi import APIRouter, HTTPException, Query, Depends, Request +from fastapi import APIRouter, HTTPException, Query, Depends, Request, Header from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials from typing import Annotated, Optional from uuid import UUID @@ -63,6 +63,10 @@ async def get_plans( sort_order: Annotated[str, Query(enum=["asc", "desc"])] = "asc", skip: Annotated[int, Query(ge=0)] = 0, limit: Annotated[int, Query(ge=1, le=50)] = 20, + x_timezone: Annotated[ + Optional[str], + Header(alias="X-Timezone", description="IANA timezone (e.g. Asia/Shanghai). Restricted plans are hidden for Chinese timezones."), + ] = None, ): return await get_published_plans( tag=tag, @@ -73,6 +77,7 @@ async def get_plans( sort_order=sort_order, skip=skip, limit=limit, + timezone_name=x_timezone, ) @@ -89,8 +94,14 @@ def get_plan_tags( @public_plans_router.get("/{plan_id}", status_code=status.HTTP_200_OK, response_model=PublicPlanDTO) -async def get_plan_details(plan_id: UUID): - return await get_published_plan(plan_id=plan_id) +async def get_plan_details( + plan_id: UUID, + x_timezone: Annotated[ + Optional[str], + Header(alias="X-Timezone", description="IANA timezone (e.g. Asia/Shanghai). Restricted plans are hidden for Chinese timezones."), + ] = None, +): + return await get_published_plan(plan_id=plan_id, timezone_name=x_timezone) @public_plans_router.get("/{plan_id}/daily", status_code=status.HTTP_200_OK, response_model=DailyPlanResponse) diff --git a/pecha_api/plans/series/public_series_view.py b/pecha_api/plans/series/public_series_view.py index 20b6208c9..56dc01727 100644 --- a/pecha_api/plans/series/public_series_view.py +++ b/pecha_api/plans/series/public_series_view.py @@ -1,4 +1,4 @@ -from fastapi import APIRouter, Depends, Query +from fastapi import APIRouter, Depends, Header, Query from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer from typing import Optional, Annotated from uuid import UUID @@ -39,6 +39,10 @@ async def get_series_list( credentials: Annotated[ Optional[HTTPAuthorizationCredentials], Depends(optional_oauth2_scheme) ] = None, + x_timezone: Annotated[ + Optional[str], + Header(alias="X-Timezone", description="IANA timezone (e.g. Asia/Shanghai). Restricted series are hidden for Chinese timezones."), + ] = None, ): return get_filtered_series( search=search, @@ -47,6 +51,7 @@ async def get_series_list( language=language, group_id=group_id, token=_token_from_credentials(credentials), + timezone_name=x_timezone, ) @@ -89,9 +94,14 @@ async def get_series( credentials: Annotated[ Optional[HTTPAuthorizationCredentials], Depends(optional_oauth2_scheme) ] = None, + x_timezone: Annotated[ + Optional[str], + Header(alias="X-Timezone", description="IANA timezone (e.g. Asia/Shanghai). Restricted series are hidden for Chinese timezones."), + ] = None, ): return get_series_detail( series_id=series_id, language=language, token=_token_from_credentials(credentials), + timezone_name=x_timezone, ) diff --git a/pecha_api/plans/series/series_service.py b/pecha_api/plans/series/series_service.py index 5ad93b38e..11a16b4b8 100644 --- a/pecha_api/plans/series/series_service.py +++ b/pecha_api/plans/series/series_service.py @@ -60,6 +60,11 @@ require_can_read_group_content, ) from pecha_api.plans.tags.tag_helpers import tags_to_summary_dtos +from pecha_api.region_restrictions.region_restriction_enums import RestrictedItemType +from pecha_api.region_restrictions.region_restriction_service import ( + assert_visible_for_timezone, + filter_items_for_timezone, +) from starlette import status @@ -603,6 +608,7 @@ def get_filtered_series( language: Optional[str] = None, group_id: Optional[UUID] = None, token: Optional[str] = None, + timezone_name: Optional[str] = None, ) -> SeriesListResponse: with SessionLocal() as db_session: rows, total = get_series_paginated( @@ -618,6 +624,12 @@ def get_filtered_series( published_only=True, group_ids=[group_id] if group_id is not None else None, ) + rows = filter_items_for_timezone( + rows, + timezone_name=timezone_name, + item_type=RestrictedItemType.SERIES, + id_of=lambda row: row[0].id, + ) group_summaries = _group_summaries_for_series_rows( db=db_session, series_rows=[row for row, _, _ in rows], @@ -741,7 +753,14 @@ def get_series_detail( series_id: UUID, language: Optional[str] = None, token: Optional[str] = None, + timezone_name: Optional[str] = None, ) -> SeriesDTO: + assert_visible_for_timezone( + timezone_name=timezone_name, + item_type=RestrictedItemType.SERIES, + item_id=series_id, + not_found_detail=f"Series with id '{series_id}' not found", + ) with SessionLocal() as db_session: row = get_series_by_id(db=db_session, series_id=series_id) if not row or _to_plan_status(row.status) != PlanStatus.PUBLISHED: diff --git a/pecha_api/recitations/order/order_recitations_bo.json b/pecha_api/recitations/order/order_recitations_bo.json new file mode 100644 index 000000000..e2110dc9b --- /dev/null +++ b/pecha_api/recitations/order/order_recitations_bo.json @@ -0,0 +1,1780 @@ +{ + "recitations": [ + { + "title": "སྐྱབས་འགྲོ་སེམས་བསྐྱེད།", + "text_id": "28a07e24-1415-464e-b307-b353741218a2", + "image_url": null, + "first_segment": { + "id": "72e7a301-721d-42bf-b4b6-e0bc34f015f7", + "content": "སྐྱབས་འགྲོ་སེམས་བསྐྱེད།\nགང་གིས་རྟེན་ཅིང་འབྲེལ་བར་འབྱུང་། །\nའགག་པ་མེད་པ་སྐྱེ་མེད་པ། །" + } + }, + { + "title": "སངས་རྒྱས་ཆོས་ཚོགས་མ།", + "text_id": "596d645b-19c5-40e3-83ce-7bc352c4a8f6", + "image_url": null, + "first_segment": { + "id": "241d3d9f-10f7-44bd-9fea-37e443ecb423", + "content": "སངས་རྒྱས་ཆོས་དང་ཚོགས་ཀྱི་མཆོག་རྣམས་ལ། །བྱང་ཆུབ་བར་དུ་བདག་ནི་སྐྱབས་སུ་མཆི། །\nབདག་གིས་སྦྱིན་སོགས་བགྱིས་པའི་ཚོགས་རྣམས་ཀྱིས། །འགྲོ་ལ་ཕན་ཕྱིར་སངས་རྒྱས་འགྲུབ་པར་ཤོག །" + } + }, + { + "title": "ཚད་མེད་བཞི།", + "text_id": "3929ebb0-c06f-4c5e-9abf-7ca523b588c8", + "image_url": null, + "first_segment": { + "id": "2d07efb7-96e0-4731-97dc-1f9ed0de60b6", + "content": "ཚད་མེད་བཞི།\nསེམས་ཅན་ཐམས་ཅད་བདེ་བ་དང་བདེ་བའི་རྒྱུ་དང་ལྡན་པར་གྱུར་ཅིག །\nསེམས་ཅན་ཐམས་ཅད་སྡུག་བསྔལ་དང་སྡུག་བསྔལ་གྱི་རྒྱུ་དང་བྲལ་བར་གྱུར་ཅིག །" + } + }, + { + "title": "ཡན་ལག་བདུན་པ།", + "text_id": "f4b5b0df-c8a1-4d33-9313-1b0731d24dc4", + "image_url": null, + "first_segment": { + "id": "2793a81d-6bff-4422-a62f-c20a6071704e", + "content": "ཡན་ལག་བདུན་པ།\nཇི་སྙེད་སུ་དག་ཕྱོགས་བཅུའི་འཇིག་རྟེན་ན། །དུས་གསུམ་གཤེགས་པ་མི་ཡི་སེངྒེ་ཀུན། །བདག་གིས་མ་ལུས་དེ་དག་ཐམས་ཅད་ལ། །ལུས་དང་ངག་ཡིད་དང་བས་ཕྱག་བགྱིའོ། །\nབཟང་པོ་སྤྱོད་པའི་སྨོན་ལམ་སྟོབས་དག་གིས། །རྒྱལ་བ་ཐམས་ཅད་ཡིད་ཀྱི་མངོན་སུམ་དུ། །ཞིང་གི་རྡུལ་སྙེད་ལུས་རབ་བཏུད་པ་ཡིས།། རྒྱལ་བ་ཀུན་ལ་རབ་ཏུ་ཕྱག་འཚལ་ལོ། །" + } + }, + { + "title": "མཎྜལ།", + "text_id": "b0a92917-04d1-4360-a3a4-1ef2edc23093", + "image_url": null, + "first_segment": { + "id": "f72f6cb9-aea8-4363-9aca-917a4f5743be", + "content": "༄༅། །མཎྜལ།\nཨོཾ་བཛྲ་བྷཱུ་མི་ཨཱཿཧཱུྃ། གཞི་ཡོངས་སུ་དག་པ་དབང་ཆེན་གསེར་གྱི་ས་གཞི། ཨོཾ་བཛྲ་རེ་ཁེ་ཨཱཿཧཱུྃ། ཕྱི་ལྕགས་རི་འཁོར་ཡུག་གིས་བསྐོར་བའི་དབུས་སུ་ཧཱུྃ།\nརིའི་རྒྱལ་པོ་རི་རབ། ཤར་ལུས་འཕགས་པོ། ལྷོ་འཛམ་བུ་གླིང༌། ནུབ་བ་ལང་སྤྱོད། བྱང་སྒྲ་མི་སྙན། ལུས་དང་ལུས་འཕགས། རྔ་ཡབ་དང་རྔ་ཡབ་གཞན། གཡོ་ལྡན་དང་ལམ་མཆོག་འགྲོ། སྒྲ་མི་སྙན་དང་སྒྲ་མི་སྙན་གྱི་ཟླ། རིན་པོ་ཆེའི་རི་བོ། དཔག་བསམ་གྱི་ཤིང༌། འདོད་འཇོའི་བ། མ་རྨོས་པའི་ལོ་ཏོག །" + } + }, + { + "title": "བཅོམ་ལྡན་འདས་མ་ཤེས་རབ་ཀྱི་ཕ་རོལ་ཏུ་ཕྱིན་པའི་སྙིང་པོ།", + "text_id": "8bb4a625-0112-40c0-9c60-bb26b3df2b10", + "image_url": null, + "first_segment": { + "id": "5ec73d58-a5a4-4482-81a0-7bf8ca8842f9", + "content": "བཅོམ་ལྡན་འདས་མ་ཤེས་རབ་ཀྱི་ཕ་རོལ་ཏུ་ཕྱིན་པའི་སྙིང་པོ། །\n༄༅། །​རྒྱ་གར་སྐད་དུ། བྷ་ག་བ་ཏི་པྲ་ཛྙ་པ་ར་མི་ཏཱྀ་ཧྲད་ཡ། བོད་སྐད་དུ། བཅོམ་ལྡན་འདས་མ་ཤེས་རབ་ཀྱི་ཕ་རོལ་ཏུ་ཕྱིན་པའི་སྙིང་པོ།\nབམ་པོ་གཅིག་གོ །​" + } + }, + { + "title": "སྒྲོལ་མ་ཉེར་གཅིག་ལ་བསྟོད་པ།", + "text_id": "edbef9bb-e001-432e-b6c3-e574c094ea62", + "image_url": null, + "first_segment": { + "id": "04041fc2-d73f-4ea9-a415-666beb026e93", + "content": "༄༅༅། །རྒྱ་གར་སྐད་དུ། ན་མཿཏཱ་རཱ་ཨེ་ཀ་བིཾ་ཤ་ཏི་སྟོ་ཏྲ་གུ་ཎ་ཧི་ཏ་སཱ་ཀ། བོད་སྐད་དུ། སྒྲོལ་མ་ལ་ཕྱག་འཚལ་ཉི་ཤུ་རྩ་གཅིག་གིས་བསྟོད་པ་ཕན་ཡོན་དང་བཅས་པ།\nརྗེ་བཙུན་མ་འཕགས་མ་སྒྲོལ་མ་ལ་ཕྱག་འཚལ་ལོ། །\nཕྱག་འཚལ་སྒྲོལ་མ་མྱུར་མ་དཔའ་མོ། །སྤྱན་ནི་སྐད་ཅིག་གློག་དང་འདྲ་མ། །འཇིག་རྟེན་གསུམ་མགོན་ཆུ་སྐྱེས་ཞལ་གྱི། །གེ་སར་ཕྱེ་བ་ལས་ནི་བྱུང་མ། །" + } + }, + { + "title": "འཕགས་པ་བཟང་པོ་སྤྱོད་པའི་སྨོན་ལམ་གྱི་རྒྱལ་པོ།", + "text_id": "c5dcf7bc-46c9-4de8-a24a-1354595c1f62", + "image_url": null, + "first_segment": { + "id": "5c9fcb10-7cc8-4f7d-882a-324eff2d5152", + "content": "རྒྱ་གར་སྐད་དུ། ཨཱརྻ་བྷ་དྲ་ཙརྻ་པྲ་ཎི་དྷཱ་ན་རཱ་ཛ། བོད་སྐད་དུ། འཕགས་པ་བཟང་པོ་སྤྱོད་པའི་སྨོན་ལམ་གྱི་རྒྱལ་པོ།\nའཇམ་དཔལ་གཞོན་ནུར་གྱུར་པ་ལ་ཕྱག་འཚལ་ལོ། །\nཇི་སྙེད་སུ་དག་ཕྱོགས་བཅུའི་འཇིག་རྟེན་ན། །དུས་གསུམ་གཤེགས་པ་མི་ཡི་སེང་གེ་ཀུན། །བདག་གིས་མ་ལུས་དེ་དག་ཐམས་ཅད་ལ། །ལུས་དང་ངག་ཡིད་དང་བས་ཕྱག་བགྱིའོ། །" + } + }, + { + "title": "དཀོན་མཆོག་རྗེས་དྲན་གྱི་མདོ།", + "text_id": "3766f2c4-2deb-47f9-b17f-1f7d73dd9a20", + "image_url": null, + "first_segment": { + "id": "06e52129-d23e-4b38-ab3f-b45f1518b757", + "content": "༄༅། །འཕགས་པ་དཀོན་མཆོག་གསུམ་རྗེས་སུ་དྲན་པའི་གྱི་མདོ།\nཐམས་ཅད་མཁྱེན་པ་ལ་ཕྱག་འཚལ་ལོ། །\nའདི་ལྟར་སངས་རྒྱས་བཅོམ་ལྡན་འདས་དེ་ནི་དེ་བཞིན་གཤེགས་པ་དགྲ་བཅོམ་པ་ཡང་དག་པར་རྫོགས་པའི་སངས་རྒྱས་རིག་པ་དང་ཞབས་སུ་ལྡན་པ། བདེ་བར་གཤེགས་པ།" + } + }, + { + "title": "བྱང་ཆུབ་སེམས་དཔའི་ལྟུང་བ་བཤགས་པ།", + "text_id": "4082b889-67e7-4903-9c44-6edc3386bb1b", + "image_url": null, + "first_segment": { + "id": "9243b410-059b-460b-a1cf-c3b13c01c7c6", + "content": "བྱང་ཆུབ་སེམས་དཔའི་ལྟུང་བ་བཤགས་པ།\n༄༅། །ན་མོ། བདག་མིང་འདི་ཞེས་བགྱི་བ།\nབླ་མ་ལ་སྐྱབས་སུ་མཆིའོ། །སངས་རྒྱས་ལ་སྐྱབས་སུ་མཆིའོ། །ཆོས་ལ་སྐྱབས་སུ་མཆིའོ། །དགེ་འདུན་ལ་སྐྱབས་སུ་མཆིའོ། །" + } + }, + { + "title": "འཇམ་དབྱངས་ཀྱི་བསྟོད་པ་གང་བློ་མ།", + "text_id": "c5855030-d2c8-43a5-b275-9048275c2f20", + "image_url": null, + "first_segment": { + "id": "356f82ba-c5b1-4858-9154-297301385da1", + "content": "༄༅། ། འཇམ་དབྱངས་ཀྱི་བསྟོད་པ་གང་བློ་མ་བཞུགས་སོ། །\nབླ་མ་དང་མགོན་པོ་འཇམ་དཔལ་དབྱངས་ལ་ཕྱག་འཚལ་ལོ། །\nགང་གི་བློ་གྲོས་སྒྲིབ་གཉིས་སྤྲིན་བྲལ་ཉི་ལྟར་རྣམ་དག་རབ་གསལ་བས། །ཇི་སྙེད་དོན་ཀུན་ཇི་བཞིན་གཟིགས་ཕྱིར་ཉིད་ཀྱིས་ཐུགས་ཀར་གླེགས་བམ་འཛིན། །གང་དག་སྲིད་པའི་བཙོན་རར་མ་རིག་མུན་འཐོམས་སྡུག་བསྔལ་གྱིས་གཟིར་བའི། །འགྲོ་ཚོགས་ཀུན་ལ་བུ་གཅིག་ལྟར་བརྩེ་ཡན་ལག་དྲུག་ཅུའི་དབྱངས་ལྡན་གསུང༌། །" + } + }, + { + "title": "ཐུབ་པའི་བསྟོད་པ་གང་ཚེ་རྐང་གཉིས་མ།", + "text_id": "076d4986-9de3-4ca9-911b-311c226bf4ed", + "image_url": null, + "first_segment": { + "id": "874f0a80-ed74-45d8-902e-ef07b682aca6", + "content": "༄༅། །ཐུབ་པའི་བསྟོད་པ་གང་ཚེ་རྐང་གཉིས་མ།\nགང་ཚེ་རྐང་གཉིས་གཙོ་བོ་ཁྱོད་བལྟམས་ཚེ། །ས་ཆེན་འདི་ལ་གོམ་པ་བདུན་བོར་ནས། །ང་ནི་འཇིག་རྟེན་འདི་ན་མཆོག་ཅེས་གསུངས། །དེ་ཚེ་མཁས་པ་ཁྱོད་ལ་ཕྱག་འཚལ་ལོ། །\nདང་པོ་དགའ་ལྡན་ལྷ་ཡི་ཡུལ་ནས་བྱོན། །རྒྱལ་པོའི་ཁབ་ཏུ་ཡུམ་གྱི་ལྷུམས་སུ་ཞུགས། ། ལུམྦི་ནི་ཡི་ཚལ་དུ་ཐུབ་པ་བལྟམས། །བཅོམ་ལྡན་ལྷ་ཡི་ལྷ་ལ་ཕྱག་འཚལ་ལོ། །" + } + }, + { + "title": "ཐུབ་པའི་བསྟོད་པ་ཐབས་མཁས་ཐུགས་རྗེ་མ།", + "text_id": "3482b084-ae24-468f-a512-4a6f0148f95a", + "image_url": null, + "first_segment": { + "id": "beaa0f69-56ef-4639-8c09-b4403dd684af", + "content": "ཐུབ་པའི་མཛད་པ་བཅུ་གཉིས་ལ་བསྟོད་པ་ཞེས་བྱ་བ།\nཐབས་མཁས་ཐུགས་རྗེ་ཤཱཀྱའི་རིགས་སུ་འཁྲུངས།། གཞན་གྱིས་མི་ཐུབ་བདུད་ཀྱི་དཔུང་འཇོམས་པ།། གསེར་གྱི་ལྷུན་པོ་ལྟ་བུར་བརྗིད་པའི་སྐུ།། ཤཱཀྱའི་རྒྱལ་བོ་དེ་ལ་ཕྱག་འཚལ་ལོ།།\nགང་གིས་དང་པོར་བྱང་ཆུབ་ཐུགས་བསྐྱེད་ནས།། བསོད་ནམས་ཡེ་ཤེས་ཚོགས་གཉིས་རྫོགས་མཛད་ཅིང༌།། དུས་འདིར་མཛད་པ་རྒྱ་ཆེན་འགྲོ་བ་ཡི།། མགོན་གྱུར་ཁྱེད་ལ་བདག་གིས་བསྟོད་པར་བགྱི།།" + } + }, + { + "title": "སྤྱོད་འཇུག་སྨོན་ལམ།", + "text_id": "f77c4d00-077a-41bf-a055-69007c5c9649", + "image_url": null, + "first_segment": { + "id": "bfbad7e5-1d2f-473c-ae7d-b45ede7a0701", + "content": "༄༅། །སྤྱོད་འཇུག་སྨོན་ལམ་བཞུགས་སོ།།\nབདག་གིས་བྱང་ཆུབ་སྤྱོད་པ་ལ། །འཇུག་པ་རྣམ་པར་བརྩམས་པ་ཡི། །དགེ་བ་གང་དེས་འགྲོ་བ་ཀུན། །བྱང་ཆུབ་སྤྱོད་ལ་འཇུག་པར་ཤོག །\nཕྱོགས་རྣམས་ཀུན་ན་ལུས་དང་སེམས། །སྡུག་བསྔལ་ནད་པ་ཇི་སྙེད་པ། །དེ་དག་བདག་གི་བསོད་ནམས་ཀྱིས། །བདེ་དགའ་རྒྱ་མཚོ་ཐོབ་པར་ཤོག །" + } + }, + { + "title": "༄༅། །འཕགས་པ་བཀྲ་ཤིས་བརྩེགས་པ་ཞེས་བྱ་བ་ཐེག་པ་ཆེན་པོའི་མདོ་བཞུགས་སོ། །", + "text_id": "dd77e022-5450-48cf-9e0c-74b7ff88b9b0", + "image_url": null, + "first_segment": { + "id": "787c8de6-f3f3-4b52-8030-fa3ae04ec082", + "content": "༄༅། །རྒྱ་གར་སྐད་དུ། ཨཱརྱ་མངྒ་ལ་ཀཱུ་ཊ་ནཱ་མ་མཧཱ་ཡཱ་ནཱ་སཱུ་ཏྲ། བོད་སྐད་དུ། འཕགས་པ་བཀྲ་ཤིས་བརྩེགས་པ་ཞེས་བྱ་བ་ཐེག་པ་ཆེན་པོའི་མདོ། སངས་རྒྱས་དང་བྱང་ཆུབ་སེམས་དཔའ་ཐམས་ཅད་ལ་ཕྱག་འཚལ་ལོ། །འདི་སྐད་བདག་གིས་ཐོས་པ་དུས་གཅིག་ན། བཅོམ་ལྡན་འདས་རི་བོ་མཆོག་གི་གནས། བཀྲ་ཤིས་བརྩེགས་པའི་ཕོ་བྲང་། མེ་ཏོག་བརྩེགས་པའི་ཚལ། རྡོ་རྗེ་བརྩེགས་པའི་གུར་ཁང་། རིན་ཆེན་སྤུངས་པའི་ཁྲི། གེ་སར་པདྨའི་གདན་ལ་བཞུགས་ཏེ། བཀྲ་ཤིས་པའི་འཁོར་མང་པོ་དང་ལྡན་པ། བྱང་ཆུབ་སེམས་དཔའ་སེམས་དཔའ་ཆེན་པོ་སྟོང་རྩ་བརྒྱད་ཀྱིས་བསྐོར་བ་ནི། འདི་ལྟ་སྟེ། བྱང་ཆུབ་སེམས་དཔའ་བཀྲ་ཤིས་མཆོག་དང་། བྱང་ཆུབ་སེམས་དཔའ་བཀྲ་ཤིས་སྡུད་དང་། བྱང་ཆུབ་སེམས་དཔའ་བཀྲ་ཤིས་རྒྱན་དང་། བྱང་ཆུབ་སེམས་དཔའ་བཀྲ་ཤིས་ཀུན་ཏུ་གཟིགས་དང་། བྱང་ཆུབ་སེམས་དཔའ་བཀྲ་ཤིས་ཡོངས་སུ་ཁྱབ་དང་། བཀྲ་ཤིས་བྱང་ཆུབ་སེམས་དཔའ་ནམ་མཁའི་སྙིང་པོ་དང་། བཀྲ་ཤིས་བྱང་ཆུབ་སེམས་དཔའ་སའི་སྙིང་པོ་དང་། བཀྲ་ཤིས་བྱང་ཆུབ་སེམས་དཔའ་རླུང་གི་སྙིང་པོ་དང་། བཀྲ་ཤིས་བྱང་ཆུབ་སེམས་དཔའ་མེའི་སྙིང་པོ་དང་། བཀྲ་ཤིས་བྱང་ཆུབ་སེམས་དཔའ་ཆུའི་སྙིང་པོ་དང་། བཀྲ་ཤིས་བྱང་ཆུབ་སེམས་དཔའ་རྡོའི་སྙིང་པོ་དང་། བཀྲ་ཤིས་བྱང་ཆུབ་སེམས་དཔའ་རིན་པོ་ཆེའི་སྙིང་པོ་དང་། བཀྲ་ཤིས་བྱང་ཆུབ་སེམས་དཔའ་བཀོད་པ་མཐའ་ཡས་དང་།\nདེ་བཞིན་དུ་རྣམ་པར་གཟིགས་དང་། བློ་གྲོས་མཆོག་དང་། འོད་དཔག་མེད་དང་། ཚེ་དཔག་མེད་དང་། སངས་རྒྱས་འོད་སྲུང་དང་། བཀྲ་ཤིས་དམ་པའི་ཏོག་ཅན་དང་། བཀྲ་ཤིས་མཐུ་སྟོབས་དང་ལྡན་པ་ལག་ན་རྡོ་རྗེ་དང་། བཀྲ་ཤིས་མཁྱེན་པ་དང་ལྡན་པ་འཇམ་དཔལ་དང་། བཀྲ་ཤིས་ཐུགས་རྗེ་དང་ལྡན་པ་སྤྱན་རས་གཟིགས་དབང་ཕྱུག་དང་། ཤཱ་རིའི་བུ་དང་རབ་འབྱོར་ལ་སོགས་པ་བཀྲ་ཤིས་པའི་འཁོར་སྟོང་རྩ་བརྒྱད་དང་། གཞན་ཡང་ལྷ་དང་ཀླུ་དང་། གནོད་སྦྱིན་དང་། དྲི་ཟ་དང་གྲུལ་བུམ་དང་། མི་དང་མི་མ་ཡིན་པའི་ཚོགས་རབ་ཏུ་མང་པོ་དང་ཐབས་གཅིག་ཏུ་བཞུགས་ཏེ། དེའི་ཚེ་དེའི་དུས་ན་འཇམ་དཔལ་གཞོན་ནུར་གྱུར་པ་སྟན་ལས་ལངས་ཏེ། བཅོམ་ལྡན་འདས་ལ་བསྐོར་བ་མང་དུ་མཛད་དེ། པུས་མོ་གཡས་པའི་ལྷ་ང་ས་ལ་བཙུགས་ཏེ། བཅོམ་ལྡན་འདས་ལ་འདི་སྐད་ཅེས་གསོལ་ཏོ། །བཅོམ་ལྡན་འདས་བཀྲ་ཤིས་དང་ལྡན་པའི་འཁོར་མང་པོ་བཞུགས་པའི་སྐབས་འདིར་ཐོག་མར་བཀྲ་ཤིས་ཤིང་དགེ་བ། བར་དུ་བཀྲ་ཤིས་ཤིང་དགེ་བ། ཐ་མར་བཀྲ་ཤིས་ཤིང་དགེ་བ། བཀྲ་མི་ཤིས་པ་ཐམས་ཅད་ཞི་བར་བྱེད་པ། བཀྲ་ཤིས་པ་ཐམས་ཅད་ཡོངས་སུ་འདུ་བར་བྱེད་པ། ཉེས་སྐྱོན་སེལ་བ་ལེགས་པའི་ཡོན་ཏན་ཐམས་ཅད་ཡོངས་སུ་འདུ་བར་བྱེད་པ། དཔལ་དང་སྐལ་བ་བཟང་པོར་བྱེད་པའི་མདོ་ཅིག་བསྟན་དུ་གསོལ་ཞེས་ཞུས་པ་དང་། བཅོམ་ལྡན་འདས་ཀྱིས་ཞལ་འཛུམ་པ་མཛད་ནས་འདི་སྐད་ཅེས་བཀའ་སྩལ་ཏོ། །\nའཇམ་དཔལ་གཞན་ཡང་བཀྲ་ཤིས་དང་ལྡན་པའི་འཁོར་རྣམས་ཉོན་ཅིག །ངས་སྔོན་བྲམ་ཟེའི་ལུས་ཀྱིས་མི་མཇེད་ཀྱི་འཇིག་རྟེན་གྱི་ཁམས་འདིར་ཚེ་ལོ་སྟོང་ཐུབ་པའི་ཚེ། ཡང་དག་པར་རྫོགས་པའི་སངས་རྒྱས་བཀྲ་ཤིས་ཐམས་ཅད་འབྱུང་བའི་དཔལ་གཟི་བརྗིད་ཅེས་བྱ་བ་ལ་བཀྲ་ཤིས་བརྩེགས་པ་ཞེས་བྱ་བ་ཐེག་པ་ཆེན་པོའི་མདོ། ཡོན་ཏན་སྟོང་ལྡན་ཞེས་བྱ་བའི་ཆོས་ཀྱི་རྣམ་གྲངས་འདི་ཐོས་ཏེ། བཟུང་ཞིང་བཅངས་ཏེ་བཀླགས་ཤིང་བཤད་དེ། འཇིག་རྟེན་གྱི་ཁམས་འདི་ལ་ལེགས་ཉེས་ཀྱི་དབང་གིས་བཀྲ་མི་ཤིས་པའི་རྟགས་མང་དུ་བྱུང་སྟེ། དུས་ཚོད་ཉེས་པ་དང་། སྐྱེ་བ་ཉེས་པ་དང་། ལོ་ཟླ་མི་ཤིས་པ་ལ་སོགས་པ་དང་། ལས་བྱ་ཉེས་པ་དང་། ཟས་ཟ་ཉེས་པ་དང་། གོས་བཙེམ་ཉེས་པ་དང་། ཁྱིམ་དབུབས་ཉེས་པ་དང་། གྲོགས་བཙལ་ཉེས་པ་དང་། ནོར་བསྲེལ་ཉེས་པ་དང་། རྟ་ཕྱུགས་བསྲེལ་ཉེས་པ་དང་། ན་ཉེས་པ་དང་། ཤི་ཉེས་པ་དང་། ལམ་དུ་འཇུག་ཉེས་པ་དང་། རོ་དྲངས་ཉེས་པ་དང་། གཤིད་བྱ་ཉེས་པ་དང་། དུར་གདབ་ཉེས་པ་ལ་སོགས་ཏེ་བཀྲ་མི་ཤིས་པ་ཐམས་ཅད་རབ་ཏུ་ཞི་བར་བྱེད་པ།" + } + }, + { + "title": "རྡོ་རྗེ་རྣམ་པར་འཇོམས་པའི་གཟུངས་བཞུགས་སོ། །", + "text_id": "094ecdb7-3fd5-4015-9951-5d97f5f70367", + "image_url": null, + "first_segment": { + "id": "0a0f0a7e-445a-417c-a456-0a264b207060", + "content": "རྒྱ་གར་སྐད་དུ། བཛྲ་བི་དཱ་ར་ཎ་ནཱ་མ་དྷཱ་ར་ཎི། བོད་སྐད་དུ། རྡོ་རྗེ་རྣམ་པར་འཇོམས་པ་ཞེས་བྱ་བའི་གཟུངས། སངས་རྒྱས་དང་བྱང་ཆུབ་སེམས་དཔའ་ཐམས་ཅད་ལ་ཕྱག་འཚལ་ལོ། །འདི་སྐད་བདག་གིས་ཐོས་པ་དུས་གཅིག་ན། བཅོམ་ལྡན་འདས་རྡོ་རྗེ་ལ་བཞུགས་ཏེ།སངས་རྒྱས་ཀྱི་མཐུས་ལག་ན་རྡོ་རྗེས་ལུས་ཐམས་ཅད་རྡོ་རྗེར་བྱིན་གྱིས་བརླབས་ནས་རྡོ་རྗེའི་ཏིང་ངེ་འཛིན་ལ་སྙོམས་པར་བཞུགས་སོ། །དེ་ནས་ལག་ན་རྡོ་རྗེས་སངས་རྒྱས་ཀྱི་མཐུ་དང༌། སངས་རྒྱས་ཀྱི་བྱིན་གྱིས་རླབས་དང༌།\nབྱང་ཆུབ་སེམས་དཔའ་ཐམས་ཅད་ཀྱི་བྱིན་གྱིས་བརླབས་ཀྱིས། རྡོ་རྗེ་ཁྲོ་བོ་ལས་བྱུང་བ། རྡོ་རྗེ་སྙིང་བོ་རབ་ཏུ་སྨྲས་ཏེ། མི་ཆོད་པ། མི་ཤིགས་པ། བདེན་པ། སྲ་བ། བརྟན་པ། ཐམས་ཅད་དུ་ཐོགས་པ་མེད་པ། ཐམས་ཅད་དུ་མ་ཕམ་པ། སེམས་ཅན་ཐམས་ཅད་སྐྲག་པར་བྱེད་པ། སེམས་ཅན་ཐམས་ཅད་འཇིལ་བར་བྱེད་པ། རིག་སྔགས་ཐམས་ཅད་གཅོད་པར་བྱེད་པ། རིགས་སྔགས་ཐམས་ཅད་གནོན་པར་བྱེད་པ། ལས་ཐམས་ཅད་འཇོམས་པར་བྱེད་པ། གཞན་གྱིས་ལུས་ཐམས་ཅད་འཇིག་པར་བྱེད་པ།\nགདོན་ཐམས་ཅད་བརླག་པར་བྱེད་པ། གདོན་ཐམས་ཅད་ལས་ཐར་པར་བྱེད་པ། འབྱུང་པོ་ཐམས་ཅད་འགུགས་པར་བྱེད་པ། རིག་སྔགས་ཀྱི་ལས་ཐམས་ཅད་བྱེད་དུ་འཇུག་པ། མ་གྲུབ་པ་རྣམས་གྲུབ་པར་བྱེད་པ། གྲུབ་པ་རྣམས་ཆུད་མི་ཟ་བར་བྱེད་པ། འདོད་པ་ཐམས་ཅད་རབ་ཏུ་སྦྱིན་པ། སེམས་ཅན་ཐམས་ཅད་བསྲུང་བ། ཞི་བ། རྒྱས་པ། སེམས་ཅན་ཐམས་ཅད་རེངས་པར་བྱེད་པ། རྨུགས་པར་བྱེད་པའི་གསང་སྔགས་ཀྱི་མཐུ་ཆེན་པོ་འདི། སངས་རྒྱས་ཀྱི་མཐུས་ལག་ན་རྡོ་རྗེས་རབ་ཏུ་སྨྲས་སོ། །ན་མོ་རཏྣ་ཏྲ་ཡཱ་ཡ། ན་མཤྩཎྜ་བཛྲ་པཱ་ཎ་ཡེ། མཧཱ་ཡཀྵ་སེནཱ་པ་ཏ་ཡེ། ཏདྱ་ཐཱ། ཨོཾ་ཏྲུ་ཊ་ཏྲུ་ཊ། ཏྲོ་ཊ་ཡ་ཏྲོ་ཊ་ཡ། སྥུ་ཊ་སྥུ་ཊ། སྥོ་ཊ་ཡ་སྥོ་ཊ་ཡ། གྷཱུརྞ་གྷཱུརྞ། གྷཱུརྞཱ་བ་ཡ་གྷཱུརྞཱ་བ་ཡ། སརྦ་སཏྭཱ་ནི། བོ་དྷ་ཡ་བོ་དྷ་ཡ། སཾ་བོ་དྷ་ཡ་སཾ་བོ་དྷ་ཡ། བྷྲ་མ་བྷྲ་མ། སཾ་བྷྲཱ་མ་ཡ་སཾ་བྷྲཱ་མ་ཡ། སརྦ་བྷཱུ་ཏཱ་ནི། ཀུ་ཊྚ་ཀུ་ཊྚ། སཾ་ཀུ་ཊྚ་ཡ་སཾ་ཀུ་ཊྚ་ཡ། སརྦ་ཤ་ཏྲཱུ་ན། གྷ་ཊ་གྷ་ཊ། སཾ་གྷ་ཊ་ཡ། སཾ་གྷ་ཊ་ཡ། སརྦ་བིདྱཱ་བཛྲ་བཛྲ། སྥོ་ཊ་ཡ་བཛྲ་བཛྲ། ཀ་ཊ་བཛྲ་བཛྲ། མ་ཊ་བཛྲ་བཛྲ། མ་ཐ་བཛྲ་བཛྲ། ཨཊྚ་ཧཱ་" + } + }, + { + "title": "༄༅། །འཕགས་པ་རྒྱལ་མཚན་གྱི་རྩེ་མོའི་དཔུང་རྒྱན་བཞུགས་སོ།།", + "text_id": "826f9339-37e0-49bf-af2d-b9661e2fb929", + "image_url": null, + "first_segment": { + "id": "d693d0c5-a34f-47aa-9ba8-83342fb30a32", + "content": "༈ རྒྱ་གར་སྐད་དུ། ཨཱརྱ་དྷ་ཛ་ཨ་གྲ་ཀེ་ཡཱུ་ར་ནཱ་མ་དྷཱ་ར་ཎི། བོད་སྐད་དུ། འཕགས་པ་རྒྱལ་མཚན་གྱི་རྩེ་མོའི་ དཔུང་རྒྱན་ཞེས་བྱ་བའི་གཟུངས། སངས་རྒྱས་དང་བྱང་ ཆུབ་སེམས་དཔའ་ཐམས་ཅད་ལ་ཕྱག་འཚལ་ལོ། །འདི་ སྐད་བདག་གིས་ཐོས་པ་དུས་གཅིག་ན། བཅོམ་ལྡན་འདས་སུམ་ཅུ་རྩ་གསུམ་པའི་ལྷའི་ནང་ན། ལྭ་བ་དཀར་པོ་ལྟ་ བུའི་རྡོ་ལེབ་ལ་བཞུགས་ཏེ། དེ་ནས་ལྷའི་དབང་པོ་རྒྱ་བྱིན་ལྷ་མ་ཡིན་ལས་ཕམ། རབ་ཏུ་ཕམ་ནས་དེ་རྟབ་ཅིང་རིངས་པའི་གཟུགས་ཀྱིས་བཅོམ་ལྡན་འདས་ག་ལ་བ་དེར་སོང་སྟེ་ ཕྱིན་ནས། བཅོམ་ལྡན་འདས་ཀྱི་ཞབས་ལ་མགོ་བོས་ཕྱག་ འཚལ་ཏེ། བཅོམ་ལྡན་འདས་ལ་འདི་སྐད་ཅེས་གསོལ་ ཏོ༑ ༑བཅོམ་ལྡན་འདས་བདག་འདི་ལྟར་ལྷ་མ་ཡིན་དང་གཡུལ་བཀྱེ་བ་ལས། ལྷ་མ་ཡིན་གྱི་དབང་པོ་ཐག་བཟང་ རིས་ལས་ཕམ། རབ་ཏུ་ཕམ་སྟེ། སུམ་ཅུ་རྩ་གསུམ་པའི་ལྷ་རྣམས་ཀྱང་ཕམ། རབ་ཏུ་ཕམ་ན། བཅོམ་ལྡན་འདས་ བདག་ཅག་གིས་ཇི་ལྟར་བསྒྲུབ་པར་བགྱི། བཅོམ་ལྡན་ འདས་ཀྱིས་བཀའ་སྩལ་པ། ལྷའི་དབང་པོ་ཁྱོད་ཀྱིས་རྒྱལ་ མཚན་གྱི་རྩེ་མོའི་དཔུང་རྒྱན་ཞེས་བྱ་བ་གཞན་གྱིས་མི་ཐུབ་ པའི་གཟུངས་ཟུངས་ཤིག །ངས་ཀྱང་སྔོན་བྱང་ཆུབ་སེམས་ དཔར་གྱུར་པ་ན། དེ་བཞིན་གཤེགས་པ་གཞན་གྱིས་མི་ ཐུབ་པའི་རྒྱལ་མཚན་ལས་འདི་མནོས་སོ།། མནོས་ནས་ ཀྱང་གཞན་ལ་རྒྱ་ཆེར་ཡང་དག་པར་བཤད་དོ།། མངོན་ པར་དྲན་ཏེ་དེ་ཚུན་ཆད་འཇིགས་པའམ། བག་ཚ་བའམ། སྤུ་ཟིང་ཞེས་བྱེད་པའམ། ཐ་ན་སྐད་ཅིག་ཡུད་ཙམ་ཡང་ ལུས་ལ་གནོད་པ་བྱུང་མ་མྱོང་ངོ་།།\nབཅོམ་ལྡན་འདས་ རྒྱལ་མཚན་གྱི་རྩེ་མོའི་དཔུང་རྒྱན་ཞེས་བགྱི་བ་གཞན་གྱིས་ མི་ཐུབ་པའི་གཟུངས་དེ་གང་ལགས། བཅོམ་ལྡན་འདས་ ཀྱིས་བཀའ་སྩལ་པ། ཏདྱ་ཐཱ། ཨོཾ་ཛ་ཡ་ཛ་ཡ། བི་ཛ་ཡ་ བི་ཛ་ཡ། ཛ་ཡ་པྲ་ཧི་ཎི། ཤང་ཀ་རི་ཤང་ཀ་རི། པྲ་བྷད་ ཀ་རེ། བདག་དང་སེམས་ཅན་ཐམས་ཅད་ཀྱི། སརྦ་ཤ་ཏྲུཾ། ཛཾ་བྷ་ཡ་ཛཾ་བྷ་ཡ། སྟཾ་བྷ་ཡ་སྟཾབྷ་ཡ། མོ་ཧ་ཡ་མོ་ ཧ་ཡ། བྷ་ག་བ་ཏི། ཛ་ཡ་བ་ཧི་ནི། མ་ཐཱ་མ་ཐཱ། པྲ་མ་ ཐཱ་པྲ་མ་ཐཱ། གྲ་ས་གྲ་ས། ཧ་ས་ཧ་ས། ཧཱུཾ་ཧཱུཾ། ལ་ཧཱུཾ་ལ་ ཧཱུཾ། ལམ་བོ་དྷ་རེ། ཏྲེ་ནེ་ཏྲེ། ཙ་ཏུར་བྷ་ག་དྲེ། ཙ་ཏུ་ དོཝེ། ཙ་ཏུར་བྷུ་ཛེ། ཨ་སི་མུ་ས་ལ། ཙཀྲ་ཏྲི་ཤཱ་ལ་ བཛྲ་ཀ་བཛྲ་དྷཱ་རི། བདག་གནོད་པ་ཐམས་ཅད་ལས་སྲུངས་ཤིག་སྲུངས་ཤིག །བྷ་ག་བ་ཏི། ཧ་ན་ཧ་ན། ད་ ཧ་ད་ཧ། པ་ཙ་པ་ཙ། མ་ཐ་མ་ཐ། པྲ་མ་ཐ་པྲ་མ་ཐ། དྷུ་ན་དྷུ་ན། བི་དྷུ་ན་བི་དྷུ་ན། ཧཱུཾ་ཧཱུཾ་ཕཊ་ཕཊ། བྷཉྫ་ བྷཉྫ། པ་ར་སེ་ནྱ། བི་དྷུན་ས་ཡ། སརྦ་ཤ་ཏྭཾ་ནཱ་ཤ་ཡ། ། དྷ་ཛ་ཨ་གྲ་ཀེ་ར་ཡཱུ་རེ། ཏི་ཊ་ཏི་ཊ་ཏི་ཊ། བྷི་ཊ་བྷི་ཊ། ཨུ་ལ་ཀཱ་མུ་ཁི་ཨུ་ལ་ཀཱ་དྷཱ་ར་ཎི། ཏྲི་ལོ་ཀྱ་མ་ཐཱ་ནཱི། བི་ དྷུན་ས་པ་ར་སེ་ནྱན། བདག་གནོད་པ་ཐམས་ཅད་ལས་ སྲུངས་ཤིག་སྲུངས་ཤིག །ཙ་ལ་ཙ་ལ། ཙི་ལི་ཙི་ལི། ཙུ་ ལུ་ཙུ་ལུ། ཀཾ་པ་ཀཾ་པ། ཀ་ལ་ཀ་ལ། ཀི་ལི་ཀི་ལི། ཀུ་\nལུ་ཀུ་ལུ། མུཉྫ་མུཉྫ། ཨ་ཊ་ཊ་ཧཱ་ས་མ་བི་དྷཱན་ས་ཡ་པ་ ར་སེ་ནྱན། བདག་གནོད་པ་ཐམས་ཅད་ལས་སྲུངས་ཤིག་ སྲུངས་ཤིག། ཏྲཱ་ས་ཡ་ཏྲཱ་ས་ཡ། བླ་མ་ཡ་བྷ་མ་ཡ། བུད་ དྷ་ས་ཏྱེ་ན། དྷརྨ་ས་ཏྱེ་ན། སཾ་གྷ་ས་ཏྱེ་ན། ས་ཏྱེ་བཱ་ཏེ་ ནཾ། ས་ཏེ་མ་བུད་དྷ་ས་ཏྱེ་མ་ཏི་ཀྲ་མ། དྷརྨ་ས་ཏྱེ་མ་ཏི་ ཀྲ་མ། སཾ་གྷ་ས་ཏྱེ་མ་ཏི་ཀྲ་མ། ས་ཏྱེ་བཱ་ཏི་ནཱཾ། ས་ཏེ་མ་ ཏི་ཀྲ་མ། ལམ་བྷོ་དྷ་རི་ལམ་བྷོ་དྷ་རི། ཀུ་ཊ་ཀུ་ཊ། ཀུཊྚ་ ཀུཊྚ་ཀུཊྚ། ཀུད་ཊ་པ་ཡ་ཀུད་ཊ་པ་ཡ། རུད་དྲ་མ་ནཱ་ཡ། བི་མ་ནཱ་ཡ། ཙནྡྲ་སཱུ་རྱ་བཱ་མཱ་ན་ཏྲེ་ལོ་ཀྱ་ཨ་དྷི་པ་ཏི་མ་ ནཱ་ཡ་སརྦ་དེ་བ་ཨ་དྷི་པ་ཏི་མ་ནཱ་ཡ། སརྦ་ཡཀྵ་རཀྵ་ས་ གན་དྷརྦ། ཀུམ་བྷན་ཌི། མ་ཧོ་ར་ག་དྷི་པ་ཏི་མ་ན་ཡ། བི་དྷུན་ས་ཡ་པ་ར་སེ་ནཱཾ། རང་ག་རང་ག །རང་གཱ་པ་ ཡ་རང་གཱ་པ་ཡ། ཛཱ་ལ་ཛཱ་ལ། པུབླ་མཱ་ལི་ནི། རུན་དྷ་ རུན་དྷ། རི་ཏི་རི་ཏི། ཙི་ཏི་ཙི་ཏི། དྷི་ཏི་དྷི་ཏི། བྲུ་ཀུ་ཊི། མུ་ཁེ་པ་ར་སེ་ན། ཀུ་ལོད་སཱ་དྷ་ནི་ཀ་རི། ཧ་ལ་ཧ་ལ། ཧི་ལི་ཧི་ལི། ཧུ་ལུ་ཧུ་ལུ། ཧེ་ཧེ། རི་ཎི་རི་ཎི། རི་ནི་མོ་ ཏི༔ ཛམ་བྷ་དྷ་ཛེ། སརྦ་བུད་དྷ་ཨཱ་བ་ལོ་ཀི་ཏེ། བདག་ འཇིགས་པ་ཐམས་ཅད་ལས་སྲུངས་ཤིག་སྲུངས་ཤིག །" + } + }, + { + "title": "མཚན་བརྗོད་བསྡུས་པ།", + "text_id": "d60505af-641a-41d3-ae90-0eef3102b51f", + "image_url": null, + "first_segment": { + "id": "2f87a50a-63ae-4055-b8bb-f7052a9bee55", + "content": "མཚན་བརྗོད་བསྡུས་པ།\n༄༅།།རྒྱ་གར་སྐད་དུ། ཨཱརྱ་མཉྫུ་ཤྲཱི་ན་མ་སཾ་གཱི་ཏི། བོད་སྐད་དུ།འཕགས་པ་འཇམ་དཔལ་གྱི་རྒྱུད་ཀྱི་ཡང་སྙིང་།འཇམ་དཔལ་ལ་ཕྱག་འཚལ་ལོ།།\nའདི་ལྟར་སངས་རྒྱས་བཅོམ་ལྡན་འདས།།" + } + }, + { + "title": "དགེ་སློང་མ་དཔལ་མོས་མཛད་པའི་འཕགས་པ་སྤྱན་རས་གཟིགས་དབང་ཕྱུག་གི་བསྟོད་པ།", + "text_id": "7cf87d41-a74e-4add-8bed-0109f0523d10", + "image_url": null, + "first_segment": { + "id": "30fb1804-b444-441d-8c3e-190b33c6c796", + "content": "དགེ་སློང་མ་དཔལ་མོས་མཛད་པའི་འཕགས་པ་སྤྱན་རས་གཟིགས་དབང་ཕྱུག་གི་བསྟོད་པ།\nཔོ་བསྟོད་བཞུགས་སོ།།\nༀ་འཇིག་རྟེན་མགོན་པོ་ལ་ཕྱག་འཚལ་ལོ།།" + } + }, + { + "title": "བྱམས་པའི་སྨོན་ལམ།", + "text_id": "8d66b9d0-affe-46d2-8b1b-d7c58205ee39", + "image_url": null, + "first_segment": { + "id": "a81a4764-207f-40af-aa92-7f7a644aa0e9", + "content": "རྒྱ་གར་སྐད་དུ། ཨཱརྻ་མཻ་ཏྲཱི་པྲ་ཎི་དྷཱ་ན་རཱ་ཛ། བོད་སྐད་དུ། འཕགས་པ་བྱམས་པའི་སྨོན་ལམ་གྱི་རྒྱལ་པོ།\nསངས་རྒྱས་དང་བྱང་ཆུབ་སེམས་དཔའ་ཐམས་ཅད་ལ་ཕྱག་འཚལ་ལོ། །\nཀུན་དགའ་བོ་ཇི་ལྟར་བྱང་ཆུབ་སེམས་དཔའ་སེམས་དཔའ་ཆེན་པོ་བྱམས་པས་སྔོན་བྱང་ཆུབ་སེམས་དཔའི་སྤྱད་པ་སྤྱོད་པའི་ཚེ། ཉིན་ལན་གསུམ་མཚན་ལན་གསུམ་དུ་བླ་གོས་ཕྲག་པ་གཅིག་ཏུ་གཟར་ནས་པུས་མོ་གཡས་པའི་ལྷ་ང་ས་ལ་བཙུགས་ཏེ། ཐལ་མོ་སྦྱར་ནས་སངས་རྒྱས་ཐམས་ཅད་མངོན་དུ་བྱས་ཏེ་ཚིག་འདི་སྐད་ཅེས་སྨོན་ལམ་བཏབ་བོ། །" + } + }, + { + "title": "༈ སུ་རཱུ་པ་ཞེས་བྱ་བའི་གཟུངས།", + "text_id": "b8afe259-a1f5-45e2-9535-1d04d621a501", + "image_url": null, + "first_segment": { + "id": "f5cab029-83ae-4213-872f-36e5358e80db", + "content": "༄༅། །རྒྱ་གར་སྐད་དུ། སུརཱུ་པ་ནཱ་མདྷཱ་ར་ཎཱི། བོད་སྐད་དུ། སུ་རཱུ་པ་ཞེས་བྱ་བའི་གཟུངས། སངས་རྒྱས་དང་། བྱང་ཆུབ་སེམས་དཔའ་ཐམས་ཅད་ལ་ཕྱག་འཚལ་ལོ། །ན་མཿསུ་རཱུ་པཱ་ཡ་ཏ་ཐཱ་ག་ཏཱ་ཡ། ཨརྷ་ཏེ་སོཾམྱཀྶཾ་བུདྡྷ་ཡ། ཏདྱ་ཐཱ། ཨོཾ་སུ་རུ་སུ་རུ། བྲ་སུ་རུ་པྲ་སུ་རུ། ཏ་ར་ཏ་ར། བྷ་ར་བྷ་ར། སཾ་བྷ་ར་སཾ་བྷ་ར། སྨ་ར་སྨ་ར། སནྟ་རྤ་ཡ། སནྟ་རྤ་ཡ། སརྦ་པྲེ་ཏཱ་ནཱཾ་སྭཱ་ཧཱ། གཟུངས་འདིས་ཆུ་དང་བཅས་པའི་ཟན་ལ་ལན་བདུན་ཡོངས་སུ་བཟླས་ཏེ་ལག་པ་གཡོན་པས་སེ་གོལ་གྱི་སྒྲ་ལན་གསུམ་དུ་བྱས་ནས་ཡི་དགས་ཐམས་ཅད་ལ་དབེན་པའི་ས་ཕྱོགས་སུ་སྦྱིན་པར་བྱ་ཞིང་། འདི་སྐད་ཀྱང་བརྗོད་པར་བྱ་སྟེ། སྐབས་ཚོལ་བ་དང་གླགས་ལྟ་བ་རྣམས་དེངས་ཤིག །འཇིག་རྟེན་གྱི་ཁམས་ཐམས་ཅད་ན་གནས་པའི་ཡི་དགས་རྣམས་ལ་བཟའ་བ་འདི་བདག་གིས་སྦྱིན་པར་བགྱིའོ། །ཞེས་པའོ། །\nཟས་མ་ཟོས་པའི་སྔོན་རོལ་ཉིད་དུ་སྦྱིན་པར་བྱའོ། །དེ་ལྟར་ན་ཡི་དགས་ཐམས་ཅད་ལ་འབྲས་བྲེ་བོ་ཆེ་རེ་རེའི་བཟའ་བ་སོ་སོར་བྱིན་པར་འགྱུར་རོ། །དེ་ལྟར་བྱས་ན་སྐྱེ་བ་ནས་སྐྱེ་བར་ཡང་ཉམ་ཆུང་བར་མི་འགྱུར་རོ། །དབུལ་པོར་མི་འགྱུར་རོ། །སྟོབས་ཆེ་བ་དང་། མཛེས་པ་དང་། མཐོང་ན་དགའ་བ་དང་། ཕྱུག་ཅིང་ལོངས་སྤྱོད་ཆེ་བར་འགྱུར་རོ། །ཚེ་རིང་ཞིང་ནད་མེད་པ་དང་། མྱུར་དུ་བླ་ན་མེད་པ་ཡང་དག་པར་རྫོགས་པའི་བྱང་ཆུབ་ཏུ་མངོན་པར་རྫོགས་པར་འཚང་རྒྱ་བར་འགྱུར་རོ། །ཤི་འཕོས་ནས་ཀྱང་བདེ་བ་ཅན་གྱི་འཇིག་རྟེན་གྱི་ཁམས་སུ་སྐྱེ་བར་འགྱུར་རོ། །གཟུགས་ལེགས་ཞེས་བྱ་བའི་གཟུངས་རྫོགས་སོ།" + } + }, + { + "title": "སྤྱན་འདྲེན།", + "text_id": "4793ce41-b1c5-452f-8e1b-084cd0809ce4", + "image_url": null, + "first_segment": { + "id": "f9c130f1-1c76-490c-ab73-ad6da9b85f6f", + "content": "སྤྱན་འདྲེན།\nམ་ལུས་སེམས་ཅན་ཀུན་གྱི་མགོན་གྱུར་ཅིང༌། །བདུད་སྡེ་དཔུང་བཅས་མི་བཟད་འཇོམས་མཛད་ལྷ། །དངོས་རྣམས་མ་ལུས་ཇི་བཞིན་མཁྱེན་གྱུར་པའི། །བཅོམ་ལྡན་འཁོར་བཅས་གནས་འདིར་གཤེགས་སུ་གསོལ།\nབཅོམ་ལྡན་བསྐལ་པ་གྲངས་མེད་དུ་མ་རུ། །འགྲོ་ལ་བརྩེ་ཕྱིར་ཐུགས་རྗེས་རྣམ་སྦྱངས་ཤིང༌། །སྨོན་ལམ་རྒྱ་ཆེན་དགོངས་པ་ཡོངས་རྫོགས་པ། །ཁྱེད་བཞེད་འགྲོ་དོན་མཛད་དུས་འདི་ལགས་ན། །" + } + }, + { + "title": "ཁྲུས་གསོལ།", + "text_id": "5fbdc392-277c-4e0f-af42-90af605edf2c", + "image_url": null, + "first_segment": { + "id": "8e848025-025e-4673-938f-5be3a1b2674e", + "content": "ཁྲུས་གསོལ།\nཇི་ལྟར་བལྟམས་པ་ཙམ་གྱིས་ནི། །ལྷ་རྣམས་ཀྱིས་ནི་ཁྲུས་གསོལ་ལྟར། །ལྷ་ཡི་ཆུ་ནི་དག་པ་ཡིས། །དེ་བཞིན་བདག་གིས་སྐུ་ཁྲུས་གསོལ། །\nཨོཾ་སརྦ་ཏ་ཐཱ་ག་ཏ་ཨ་བྷི་ཥེ་ཀ་ཏ་ས མ་ཡ་ཤྲི་ཡེ་ཨཱ་ཧཱུཾ།" + } + }, + { + "title": "ངས་རྒྱས་ཀྱི་ཞལ་གདམས་དིང་རི་བརྒྱ་རྩ་མ་བཞུགས་སོ།།", + "text_id": "02673b12-d2d2-4c11-887a-9bd8840c4507", + "image_url": null, + "first_segment": { + "id": "ea8b3154-2bc7-485a-82bc-dfbc25653f17", + "content": "༄༅། །ཕ་དམ་པ་སངས་རྒྱས་ཀྱི་ཞལ་གདམས་དིང་རི་བརྒྱ་རྩ་མ་བཞུགས་སོ།།\n༈ ཨོཾ་སྭ་སྟི། དམ་པ་འཆར་ཅན་གྱིས་དམ་པའི་སྤྱན་སྔར་ཕྱིན་ནས་དམ་པ་ཉིད་སྐུ་བགྲེས་འདུག་པ། དམ་པ་ཉིད་ལྟ་བདེ་བ་ནས་བདེ་བར་གཤེགས་པ་ལགས་ཏེ། དིང་རི་བ་རྣམས་སུ་ལ་བློ་གཏོད། ཅི་ཙུག་བྱེད་བྱས་ནས་བཤུམས་པས། ཡང་དམ་པས་དིང་རི་བ་རྣམས་ལ་ཞལ་ཆེམས་སུ་གསུངས་པ།\nལུས་ངག་ཡིད་གསུམ་དམ་པའི་ཆོས་ལ་འབུངས།།" + } + }, + { + "title": "འཕགས་པ་བཀྲ་ཤིས་བརྒྱད་པའི་ཚིགས་སུ་བཅད་པ།", + "text_id": "5fc8fe2b-a988-4bcb-bf5a-e4629d0e9725", + "image_url": null, + "first_segment": { + "id": "aff4dbfe-19b5-4ad4-8c95-7cdefb9102a0", + "content": "ཨོཾ། སྣང་སྲིད་རྣམ་དག་རང་བཞིན་ལྷུན་གྲུབ་པའི། །བཀྲ་ཤིས་ཕྱོགས་བཅུའི་ཞིང་ན་བཞུགས་པ་ཡི། །སངས་རྒྱས་ཆོས་དང་དགེ་འདུན་འཕགས་པའི་ཚོགས། །ཀུན་ལ་ཕྱག་འཚལ་བདག་ཅག་བཀྲ་ཤིས་ཤོག །\nསྒྲོན་མེའི་རྒྱལ་པོ་རྩལ་བརྟན་དོན་འགྲུབ་དགོངས། །བྱམས་པའི་རྒྱན་དཔལ་དགེ་གྲགས་དཔལ་དམ་པ། །ཀུན་ལ་དགོངས་པ་རྒྱ་ཆེར་གྲགས་པ་ཅན། །ལྷུན་པོ་ལྟར་འཕགས་རྩལ་གྲགས་དཔལ་དང་ནི། །སེམས་ཅན་ཐམས་ཅད་ལ་དགོངས་གྲགས་པའི་དཔལ། །ཡིད་ཚིམས་མཛད་པ་རྩལ་རབ་གྲགས་དཔལ་ཏེ། །མཚན་རྩམ་ཐོས་པས་བཀྲ་ཤིས་དཔལ་འཕེལ་བ། །བདེ་བར་བཤེགས་པ་བརྒྱད་ལ་ཕྱག་འཚལ་ལོ། །\nའཇམ་དཔལ་གཞོན་ནུ་དཔལ་ལྡན་རྡོ་རྗེ་འཛིན། །སྤྱན་རས་གཟིགས་དབང་མགོན་པོ་བྱམས་པའི་དཔལ། །ས་ཡི་སྙིང་པོ་སྒྲིབ་པ་རྣམ་པར་སེལ། །ནམ་མཁའི་སྙིང་པོ་འཕགས་མཆོག་ཀུན་ཏུ་བཟང་། །ཨུཏྤལ་རྡོ་རྗེ་པད་དཀར་ཀླུ་ཤིང་དང་། །ནོར་བུ་ཟླ་བ་རལ་གྲི་ཉི་མ་ཡི། །ཕྱག་མཚན་ལེགས་བསྣམས་བཀྲ་ཤིས་དཔལ་གྱི་མཆོག །བྱང་ཆུབ་སེམས་དཔའ་བརྒྱད་ལ་ཕྱག་འཚལ་ལོ།" + } + }, + { + "title": "བསྔོ་བ་དང་སྨོན་ལམ་བསྡུས་པ་བཞུགས་སོ།", + "text_id": "379efa69-6fa3-42e7-b900-9af3cd27b573", + "image_url": null, + "first_segment": { + "id": "d7027170-5452-44ec-85e9-8e16777722dc", + "content": "ཨེ་མ་ཧོ།\nཕྱོགས་བཅུའི་རྒྱལ་བ་སྲས་བཅས་མ་ལུས་དགོངས། །\nའདིས་མཚོན་བདག་གི་དུས་གསུམ་བསགས་པའི་དགེས།" + } + }, + { + "title": "དཔལ་ནཱ་ལེནྡྲའི་པཎ་གྲུབ་བཅུ་བདུན་གྱི་གསོལ་འདེབས།", + "text_id": "c0ec0c8d-3949-4e5b-bf3c-88dc41bd9223", + "image_url": null, + "first_segment": { + "id": "7ed2e36f-59fa-4730-9b9b-6ce127702c36", + "content": "༄༅། །དཔལ་ནཱ་ལེནྡྲའི་པཎ་གྲུབ་བཅུ་བདུན་གྱི་གསོལ་འདེབས།\n༈ འགྲོ་ལ་ཕན་བཞེད་ཐུགས་རྗེས་རབ་བསྐྲུན་པའི། ། སྤངས་རྟོགས་སྐྱོབ་པ་མཆོག་བརྙེས་ལྷ་ཡི་ལྷ། ། རྟེན་འབྱུང་གཏམ་གྱིས་འགྲོ་རྣམས་འདྲེན་མཛད་པའི། ། ཐུབ་དབང་སྨྲ་བའི་ཉི་མར་མགོས་ཕྱག་འཚལ། །\nརྒྱལ་ཡུམ་དགོངས་དོན་མཐའ་བྲལ་དེ་ཉིད་དོན། ། རྟེན་འབྱུང་རིགས་ཚུལ་ཟབ་མོས་གསལ་མཁས་པའི། ། རྒྱལ་བའི་ལུང་བཞིན་ཐེག་མཆོག་དབུ་མའི་སྲོལ། ། འབྱེད་མཛད་ཀླུ་སྒྲུབ་ཞབས་ལ་གསོལ་བ་འདེབས། །" + } + }, + { + "title": "རྒྱན་དྲུག་མཆོག་གཉིས་ཀྱི་བསྟོད་པ་མཁའ་མཉམ་མ།", + "text_id": "3aab29a4-ed0a-439c-bacc-520967c3ae34", + "image_url": null, + "first_segment": { + "id": "f253b252-4b37-4214-b3f9-615f8ebc64fc", + "content": "རྒྱན་དྲུག་མཆོག་གཉིས་ཀྱི་བསྟོད་པ་མཁའ་མཉམ་མ།\nམཁའ་མཉམ་འགྲོ་ལ་མཁྱེན་བརྩེས་རབ་དགོངས་ནས། །\nགྲངས་མེད་གསུམ་དུ་ཚོགས་གཉིས་རབ་རྫོགས་ཏེ། །" + } + }, + { + "title": "བདེན་ཚིག་སྨོན་ལམ།", + "text_id": "e0c49a89-96f1-405d-9cf4-2ffff731af23", + "image_url": null, + "first_segment": { + "id": "a5dcfb69-0f66-4dac-91de-1f9a95ab514e", + "content": "༄༅། །བདེན་ཚིག་སྨོན་ལམ།\nན་མོ་རཏྣ་ཏྲ་ཡཱ་ཡ། །\nཚད་མེད་ཡོན་ཏན་རྒྱ་མཚོའི་དཔལ་མངའ་ཞིང༌། །ཉམ་ཆུང་འགྲོ་ལ་བུ་གཅིག་ལྟར་དགོངས་པའི། །དུས་གསུམ་བདེ་གཤེགས་སྲས་དང་སློབ་མར་བཅས། །བདག་གི་བདེན་པའི་སྨྲེ་ངག་འདིར་དགོངས་ཤིག །" + } + }, + { + "title": "ཐུབ་བསྟན་རིས་མེད་རྒྱས་པའི་སྨོན་ལམ།", + "text_id": "a0b2840b-12e6-4343-9c1e-867d0dd18503", + "image_url": null, + "first_segment": { + "id": "24b16361-3cc0-4b0f-8c5e-09299779325b", + "content": "ཐུབ་བསྟན་རིས་མེད་རྒྱས་པའི་སྨོན་ལམ།\n༄༅། །སྐུ་བཞིའི་བདག་ཉིད་ཀུན་མཁྱེན་ཉི་མའི་གཉེན། །ཚེ་འོད་དཔག་མེད་འཕགས་མཆོག་སྤྱན་རས་གཟིགས། །འཇམ་དབྱངས་གསང་བདག་སྒྲོལ་མ་ཁྲོ་གཉེན་ཅན། །རྒྱལ་དང་རྒྱལ་སྲས་སེམས་དཔའི་ཚོགས་རྣམས་དང་།\nགཏད་རབས་ཆེ་བདུན་རྒྱན་དྲུག་མཆོག་གཉིས་པོ། །གྲུབ་ཆེན་བརྒྱད་ཅུ་གནས་བརྟན་བཅུ་དྲུག་སོགས། །བསྟན་དང་འགྲོ་ལ་གཅིག་ཏུ་ཕན་བཞེད་པ། །སྐྱེས་མཆོག་སེམས་དཔའ་མ་ལུས་དགོས་སུ་གསོལ། །" + } + }, + { + "title": "བསྟན་འབར་མ།", + "text_id": "eedc7326-ac62-40ad-8b1f-87a57d039f5a", + "image_url": null, + "first_segment": { + "id": "87379a31-8761-4f50-aec7-786a15579218", + "content": "བསྟན་འབར་མ།\nསངས་རྒྱས་རྣམ་གཟིགས་གཙུག་ཏོར་ཐམས་ཅད་སྐྱོབ། །འཁོར་བ་འཇིག་དང་གསེར་ཐུབ་འོད་སྲུང་དང་། །ཤཱཀྱ་ཐུབ་པ་གོའུ་ཏཾ་ལྷ་ཡི་ལྷ། །སངས་རྒྱས་དཔའ་བོ་བདུན་ལ་ཕྱག་འཚལ་ལོ། །\nསེམས་ཅན་དོན་དུ་བདག་གིས་སྔོན། །དཀའ་བ་གང་ཞིག་སྤྱད་གྱུར་དང་། །བདག་གི་བདེ་བ་བཏང་བ་ཡིས། །བསྟན་པ་ཡུན་རིང་འབར་གྱུར་ཅིག །" + } + }, + { + "title": "༄༅། །སྤྱི་བཤགས་བཞུགས་སོ། །", + "text_id": "ed8dcb55-576b-4d8d-917c-2a30de31e98f", + "image_url": null, + "first_segment": { + "id": "fe7af7a7-0201-4cb2-aa10-12399857fd8d", + "content": "༄༅། །ཨུ་ཧུ་ལགས། བླ་མ་རྡོ་རྗེ་འཛིན་པ་ཆེན་པོ་ལ་སོགས་པ་ཕྱོགས་བཅུ་ན་བཞུགས་པའི་སངས་རྒྱས་དང་བྱང་ཆུབ་སེམས་དཔའ་ཐམས་ཅད་དང༌། དགེ་འདུན་བཙུན་པ་རྣམས་བདག་ལ་དགོངས་སུ་གསོལ།\nབདག་མིང་འདི་ཞེས་བགྱི་བས་ཚེ་རབས་འཁོར་བ་ཐོག་མ་མ་མཆིས་པ་ནས་ད་ལྟ་ལ་ཐུག་གི་བར་དུ། ཉོན་མོངས་པ་འདོད་ཆགས་དང་ཞེ་སྡང་དང་གཏི་མུག་གི་དབང་གིས་ལུས་ངག་ཡིད་གསུམ་གྱི་སྒོ་ནས་སྡིག་པ་མི་དགེ་བ་བཅུ་བགྱིས་པ་དང་། མཚམས་མ་མཆིས་པ་ལྔ་བགྱིས་པ་དང་། དེ་དང་ཉེ་བ་ལྔ་བགྱིས་པ་དང་། སོ་སོར་ཐར་པའི་སྡོམ་པ་དང་འགལ་བ་དང་། བྱང་ཆུབ་སེམས་དཔའི་བསླབ་པ་དང་འགལ་བ་དང་། གསང་སྔགས་ཀྱི་དམ་ཚིག་དང་འགལ་བ་དང་། ཕ་དང་མ་ལ་མ་གུས་པ་དང་། མཁན་པོ་དང་སློབ་དཔོན་ལ་མ་གུས་པ་དང་། གྲོགས་ཚངས་པ་མཚུངས་པར་སྤྱོད་པ་རྣམས་ལ་མ་གུས་པ་དང་། དཀོན་མཆོག་གསུམ་ལ་གནོད་པའི་ལས་བགྱིས་པ་དང་། དམ་པའི་ཆོས་སྤངས་པ་དང་། འཕགས་པའི་དགེ་འདུན་ལ་སྐུར་བ་བཏབ་པ་དང་། སེམས་ཅན་ལ་གནོད་པའི་ལས་བགྱིས་པ་ལ་སོགས་པ་སྡིག་པ་མི་དགེ་བའི་ཚོགས་བདག་གིས་བགྱིས་པ་དང་། བགྱིད་དུ་སྩལ་བ་དང་། གཞན་གྱིས་བགྱིས་པ་ལ་རྗེས་སུ་ཡི་རང་བ་ལ་སོགས་པ། མདོར་ན་མཐོ་རིས་དང་ཐར་པའི་གེགས་སུ་གྱུར་ཅིང་འཁོར་བ་དང་ངན་སོང་གི་རྒྱུར་གྱུར་པའི་ཉེས་ལྟུང་གི་ཚོགས་ཅི་མཆིས་པ་ཐམས་ཅད། བླ་མ་རྡོ་རྗེ་འཛིན་པ་ཆེན་པོ་ལ་སོགས་པ་ཕྱོགས་བཅུ་ན་བཞུགས་པའི་སངས་རྒྱས་དང་བྱང་ཆུབ་སེམས་དཔའ་ཐམས་ཅད་དང་། དགེ་འདུན་བཙུན་པ་རྣམས་ཀྱི་སྤྱན་སྔར་མཐོལ་ལོ། །མི་འཆབ་བོ། །འཆགས་སོ། །ཕྱིན་ཆད་ཀྱང་སྡོམ་པར་བགྱིད་ལགས་སོ། །\nམཐོལ་ཞིང་བཤགས་ན་བདག་བདེ་བ་ལ་རེག་པར་གནས་པར་འགྱུར་གྱི། ། མ་མཐོལ་མ་བཤགས་ན་ནི་དེ་ལྟར་མི་འགྱུར་བ་ལགས་སོ། ། །།" + } + }, + { + "title": "༄༅། །གཏོར་མ་ཆ་གསུམ་བཞུགས་སོ། །", + "text_id": "caf65ea2-8bbc-49e5-badf-260383319020", + "image_url": null, + "first_segment": { + "id": "96a61053-88bf-46c7-a630-59dbdaa3f382", + "content": "༄༅། །དཔལ་རྡོ་རྗེ་སེམས་དཔའ་ལ་ཕྱག་འཚལ་ལོ། །གཏོར་མ་ཆ་གསུམ་གཏོང་བར་འདོད་པས། རྒྱུ་ནི།\nའབྲས་ཡོས་བཅས་ཤིང་ཏིལ་དང་བཅས། །འབྲས་ཆན་མེ་ཏོག་ཆུ་དང་བཅས། ། སྲན་ཆན་བཅས་ཤིང་མང་དུ་བྱ། །སྣུམ་འཁུར་ལ་སོགས་བཟའ་བ་རྣམས། །\n(ཞེས་རྡོ་རྗེ་འབྱུང་བ་ལས་གསུངས་པས། དེ་ལ་གསུམ། ཕྱོགས་སྐྱོང་གི་གཏོང་གཏོར། འབྱུང་པོའི་སྤྱི་གཏོར། བགེགས་གཏོར་དང་གསུམ་མོ། །དེ་ལ་དང་པོ་ནི།)" + } + }, + { + "title": "ཚིག་བདུན་གསོལ་འདེབས།", + "text_id": "88f29b50-4cba-4844-aa61-9920ad59e741", + "image_url": null, + "first_segment": { + "id": "f8eb5fda-1af0-49a0-913b-1efe1773c46f", + "content": "ཚིག་བདུན་གསོལ་འདེབས།\nཧཱུྃ༔ ཨོ་རྒྱན་ཡུལ་གྱི་ནུབ་བྱང་མཚམས༔\nཔདྨ་གེ་སར་སྡོང་པོ་ལ༔" + } + }, + { + "title": "གསོལ་འདེབས་བར་ཆད་ལམ་སེལ།-", + "text_id": "a5eea34a-7e66-44e2-b7b0-3e5b8d8ed87c", + "image_url": null, + "first_segment": { + "id": "d7884d6c-3207-43eb-a5a4-3afbbd847d98", + "content": "༈ གསོལ་འདེབས་བར་ཆད་ལམ་སེལ་བཞུགས༔\nཨོཾ་ཨཱཿཧཱུྃ་བཛྲ་གུ་རུ་པདྨ་སིདྡྷི་ཧཱུྃ༔ ཆོས་སྐུ་སྣང་བ་མཐའ་ཡས་ལ་གསོལ་བ་འདེབས༔ ལོངས་སྐུ་ཐུགས་རྗེ་ཆེན་པོ་ལ་གསོལ་བ་འདེབས༔ སྤྲུལ་སྐུ་པདྨ་འབྱུང་གནས་ལ་གསོལ་བ་འདེབས༔ བདག་གི་བླ་མ་ངོ་མཚར་སྤྲུལ་པའི་སྐུ༔ རྒྱ་གར་ཡུལ་དུ་སྐུ་འཁྲུངས་ཐོས་བསམ་མཛད༔ བོད་ཡུལ་དབུས་སུ་ཞལ་བྱོན་དྲེགས་པ་བཏུལ༔ ཨོ་རྒྱན་ཡུལ་དུ་སྐུ་བཞུགས་འགྲོ་དོན་མཛད༔ སྐུ་ཡི་ངོ་མཚར་མཐོང་བའི་ཚེ༔ གཡས་པས་རལ་གྲིའི་ཕྱག་རྒྱ་མཛད༔ གཡོན་པས་འགུགས་པའི་ཕྱག་རྒྱ་མཛད༔ ཞལ་བགྲད་མཆེ་གཙིགས་གྱེན་ལ་གཟིགས༔ རྒྱལ་བའི་གདུང་འཛིན་འགྲོ་བའི་མགོན༔ ཐུགས་རྗེས་བདག་ལ་བྱིན་གྱིས་རློབས༔ བརྩེ་བས་བདག་སོགས་ལམ་སྣ་དྲོངས༔ དགོངས་པས་བདག་ལ་དངོས་གྲུབ་སྩོལ༔ ནུས་པས་བདག་སོགས་བར་ཆད་སོལ༔ ཕྱི་ཡི་བར་ཆད་ཕྱི་རུ་སོལ༔ ནང་གི་བར་ཆད་ནང་དུ་སོལ༔ གསང་བའི་བར་ཆད་དབྱིངས་སུ་སོལ༔ གུས་པས་ཕྱག་འཚལ་སྐྱབས་སུ་མཆི༔\nཨོཾ་ཨཱཿཧཱུྃ་བཛྲ་གུ་རུ་པདྨ་སིདྡྷི་ཧཱུྃ༔ དམ་ཆོས་རིན་ཆེན་གསན་པའི་ཚེ༔ སྐུ་གསལ་འོད་ཟེར་མདངས་དང་ལྡན༔ ཕྱག་གཡས་སྡེ་སྣོད་གླེགས་བམ་བསྣམས༔ གཡོན་པས་ཕུར་པའི་པུསྟི་བསྣམས༔ ཟབ་མོའི་ཆོས་རྣམས་ཐུགས་སུ་ཆུད༔ ཡང་ལེ་ཤོད་ཀྱི་པཎྜི་ཏ༔ ཐུགས་རྗེས་བདག་ལ་བྱིན་གྱིས་རློབས༔ བརྩེ་བས་བདག་སོགས་ལམ་སྣ་དྲོངས༔ དགོངས་པས་བདག་ལ་དངོས་གྲུབ་སྩོལ༔ ནུས་པས་བདག་སོགས་བར་ཆད་སོལ༔ ཕྱི་ཡི་བར་ཆད་ཕྱི་རུ་སོལ༔ ནང་གི་བར་ཆད་ནང་དུ་སོལ༔ གསང་བའི་བར་ཆད་དབྱིངས་སུ་སོལ༔ གུས་པས་ཕྱག་འཚལ་སྐྱབས་སུ་མཆི༔" + } + }, + { + "title": "བསམ་པ་ལྷུན་གྲུབ་མ།", + "text_id": "692d93ef-f458-49fe-8db5-2b3e12f84727", + "image_url": null, + "first_segment": { + "id": "db8e605b-5b55-449e-8830-3e07d1fcf0a1", + "content": "༄༅། །བསམ་པ་ལྷུན་གྲུབ་མ་བཞུགས་སོ། །\nཨེ་མ་ཧོ༔\nནུབ་ཕྱོགས་བདེ་བ་ཅན་གྱི་ཞིང་ཁམས་སུ༔ སྣང་བ་མཐའ་ཡས་ཐུགས་རྗེའི་བྱིན་བརླབས་ཏེ༔ སྤྲུལ་སྐུ་པདྨ་འབྱུང་གནས་བྱིན་བརླབས་ཏེ༔ འཛམ་བུ་གླིང་དུ་འགྲོ་བའི་དོན་ལ་བྱོན༔ འགྲོ་དོན་རྒྱུན་ཆད་མེད་པའི་ཐུགས་རྗེ་ཅན༔ ཨོ་རྒྱན་པདྨ་འབྱུང་གནས་ལ་གསོལ་བ་འདེབས༔ བསམ་པ་ལྷུན་གྱིས་འགྲུབ་པར་བྱིན་གྱིས་རློབས༔" + } + }, + { + "title": "ཆགས་མེད་བདེ་སྨོན།", + "text_id": "da7b8478-630c-48f9-b784-3e00ebdc778f", + "image_url": null, + "first_segment": { + "id": "cbda0479-b81d-415a-a22d-3862e8f1ea3c", + "content": "༄༅། །རྣམ་དག་བདེ་ཆེན་ཞིང་གི་སྨོན་ལམ་རཱ་ག་ཨ་སྱས་མཛད་པ་བཞུགས་སོ།།\nའདི་ནས་ཉི་མ་ནུབ་ཀྱི་ཕྱོགས་རོལ་ན། །གྲངས་མེད་འཇིག་རྟེན་མང་པོའི་ཕ་རོལ་ན། །ཅུང་ཟད་སྟེང་དུ་འཕགས་པའི་ཡུལ་ས་ན། །རྣམ་པར་དག་པའི་ཞིང་ཁམས་བདེ་བ་ཅན། །\nབདག་གི་ཆུ་བུར་མིག་གིས་མ་མཐོང་ཡང༌། །རང་སེམས་གསལ་བའི་ཡིད་ལ་ལམ་མེར་གསལ། །དེ་ན་བཅོམ་ལྡན་རྒྱལ་བ་འོད་དཔག་མེད། །པདྨ་རཱ་གའི་མདོག་ཅན་གཟི་བརྗིད་འབར། །" + } + }, + { + "title": "ལམ་གཙོ་རྣམ་གསུམ།", + "text_id": "d5de76ec-c123-428f-b1b2-09e1c9b3f87b", + "image_url": null, + "first_segment": { + "id": "5f6e8cae-9e10-417b-9e00-3f10df68b388", + "content": "ལམ་གཙོ་རྣམ་གསུམ་བཞུགས་སོ། །\nརྗེ་བཙུན་བླ་མ་རྣམས་ལ་ཕྱག་འཚལ་ལོ། །\nརྒྱལ་བའི་གསུང་རབ་ཀུན་གྱི་སྙིང་པོའི་དོན། རྒྱལ་སྲས་དམ་པ་རྣམས་ཀྱིས་བསྔགས་པའི་ལམ། །སྐལ་ལྡན་ཐར་འདོད་རྣམས་ཀྱི་འཇུག་ངོགས་དེ། །ཇི་ལྟར་ནུས་བཞིན་བདག་གིས་བཤད་པར་བྱ། །" + } + }, + { + "title": "བློ་སྦྱོང་ཚིག་བརྒྱད་མ།", + "text_id": "df466705-f7ed-42d5-b06a-70526f865e6a", + "image_url": null, + "first_segment": { + "id": "be7ef8e8-4a0b-4399-bef1-625f36b442cc", + "content": "༈ བློ་སྦྱོང་ཚིག་བརྒྱད་མ།\n༄༅། །བདག་ནི་སེམས་ཅན་ཐམས་ཅད་ལ། །ཡིད་བཞིན་ནོར་བུ་ལས་ལྷག་པའི། །དོན་མཆོག་སྒྲུབ་པའི་བསམ་པ་ཡིས། །རྟག་ཏུ་གཅེས་པར་འཛིན་པར་ཤོག །\nགང་དུ་སུ་དང་འགྲོགས་པའི་ཚེ། །བདག་ཉིད་ཀུན་ལས་དམན་བལྟ་ཞིང་། །གཞན་ལ་བསམ་པ་ཐག་པ་ཡིས། །མཆོག་ཏུ་གཅེས་པར་འཛིན་པར་ཤོག །" + } + }, + { + "title": "ཞབས་བརྟན་རབ་འབྱམས་རྒྱལ་བ།", + "text_id": "4ab69d62-e41a-4706-baad-55273842dffa", + "image_url": null, + "first_segment": { + "id": "3e084244-9792-4720-93db-31fdf460c6e8", + "content": "རྒྱལ་མཆོག་སྐུ་ཕྲེང་བཅུ་བཞི་པ་མཆོག་གི་ཞབས་བརྟན་གསོལ་འདེབས།\nༀ་སྭ་སྟི། རབ་འབྱམས་རྒྱལ་བའི་གསང་གསུམ་མ་ལུས་པ། །གང་འདུལ་ཅིར་ཡང་འཆར་བའི་སྒྱུ་འཕྲུལ་གར། །སྲིད་ཞིའི་དགེ་ལེགས་ཀུན་འབྱུང་ཡིད་བཞིན་ནོར། །དངོས་བརྒྱུད་དྲིན་ཅན་བླ་མའི་ཚོགས་རྣམས་ལ། །བདག་ཅག་གདུང་ཤུགས་དྲག་པོས་གསོལ་འདེབས་ན། །གངས་ཅན་མགོན་པོ་བསྟན་འཛིན་རྒྱ་མཚོ་ཡི། །སྐུ་ཚེ་མི་ཤིགས་བསྐལ་བརྒྱར་རབ་བརྟན་ཅིང་། །བཞེད་དོན་ལྷུན་གྱིས་འགྲུབ་པར་བྱིན་གྱིས་རློབས། །\nཆོས་དབྱིངས་ཀུན་གསལ་ཁྱོན་དང་མཉམ་འཇུག་པའི། །རྡུལ་བྲལ་བདེ་ཆེན་ཡེ་ཤེས་སྒྱུ་མའི་སྤྲིན། །གྲངས་མེད་རྟེན་དང་བརྟན་པའི་དཀྱིལ་འཁོར་དུ།། ཤར་བའི་ཡི་དམ་ལྷ་ཚོགས་ཐམས་ཅད་ལ། །བདག་ཅག་གདུང་ཤུགས་དྲག་པོས་གསོལ་འདེབས་ན། །གངས་ཅན་མགོན་པོ་བསྟན་འཛིན་རྒྱ་མཚོ་ཡི། །སྐུ་ཚེ་མི་ཤིགས་བསྐལ་བརྒྱར་རབ་བརྟན་ཅིང་། །བཞེད་དོན་ལྷུན་གྱིས་འགྲུབ་པར་བྱིན་གྱིས་རློབས། །" + } + }, + { + "title": "དམིགས་བརྩེ་མ།", + "text_id": "4791ea70-792f-4278-9b78-d1e5d94c3a4e", + "image_url": null, + "first_segment": { + "id": "7882e129-b482-40cc-b78f-01af05dbf0b5", + "content": "དམིགས་བརྩེ་མ།\nདམིགས་མེད་བརྩེ་བའི་གཏེར་ཆེན་སྤྱན་རས་གཟིགས།།\nདྲི་མེད་མཁྱེན་པའི་དབང་པོ་འཇམ་པའི་དབྱངས།།" + } + }, + { + "title": "སྣང་སྲིད་དབང་དུ་སྡུད་པའི་གསོལ་འདེབས་བྱིན་རླབས་སྤྲིན་ཆེན།", + "text_id": "98912ac6-ff5d-4402-beeb-8d1be2af7421", + "image_url": null, + "first_segment": { + "id": "345281a6-462c-47a3-bda7-5f7dba882f9e", + "content": "དབང་སྡུད་གསོལ་འདེབས།\nཨོཾ་ཨཱ་ཧཱུྃཿཧྲཱིཿ\nབདེ་ཆེན་འབར་བ་དབང་གི་ཕོ་བྲང་དུ། །བདེ་སྟོང་སོ་སོར་རྟོགས་པའི་ཡེ་ཤེས་སྐུ། །མ་ཆགས་བདེ་ལྡན་པདྨའི་རང་བཞིན་ལས། །རྡོ་རྗི་ཉི་མ་སྣང་བ་ཆེན་པོའི་དཔལ། །" + } + }, + { + "title": "དཔལ་ལྡན་ས་གསུམ་མ།", + "text_id": "311567e5-bf41-4b7e-9e11-dd4c2089f2e9", + "image_url": null, + "first_segment": { + "id": "bb9c48c7-9cc6-4bb7-8c46-37ae03b62a28", + "content": "རྗེ་ཙོང་ཁ་པའི་གསོལ་འདེབས་དཔལ་ལྡན་ས་གསུམ་མ།\nདཔལ་ལྡན་རྩ་བའི་བླ་མ་རིན་པོ་ཆེ། །བདག་སོགས་སྙིང་གར་པདྨོའི་སྟེང་བཞུགས་ལ། །བཀའ་དྲིན་ཆེན་པོའི་སྒོ་ནས་རྗེས་བཟུང་སྟེ། །སྐུ་གསུང་ཐུགས་ཀྱི་དངོས་གྲུབ་སྩལ་དུ་གསོལ། །\nདཔལ་ལྡན་ས་གསུམ་འགྲོ་བའི་མིག་གཅིག་པུ། །ཐར་འདོད་ཡོངས་ཀྱི་བླ་ན་མེད་པའི་མགོན། །རྒྱལ་བ་ཀུན་ལས་ལྷག་པའི་ཐུགས་རྗེ་ཅན། །ཟླ་མེད་བླ་མ་མཆོག་ལ་གསོལ་བ་འདེབས། །" + } + }, + { + "title": "ཐུགས་རྗེ་ཆེན་པོའི་བསྒོམ་བཟླས་འགྲོ་དོན་མཁའ་ཁྱབ་མ།", + "text_id": "9ee29fe8-5517-4336-b897-a1c81c4985db", + "image_url": null, + "first_segment": { + "id": "66f66786-5110-4c14-af9e-c6f5c707af0d", + "content": "༄༅། །ཐུགས་རྗེ་ཆེན་པོའི་བསྒོམ་བཟླས་འགྲོ་དོན་མཁའ་ཁྱབ་མ་བཞུགས་སོ། །\nསངས་རྒྱས་ཆོས་དང་ཚོགས་ཀྱི་མཆོག་རྣམས་ལ། །བྱང་ཆུབ་བར་དུ་བདག་ནི་སྐྱབས་སུ་མཆི། །བདག་གི་སྦྱིན་སོགས་བགྱིས་པའི་བསོད་ནམས་ཀྱིས། །འགྲོ་ལ་ཕན་ཕྱིར་སངས་རྒྱས་འགྲུབ་པར་ཤོག །" + } + }, + { + "title": "གསོལ་འདེབས་ལེའུ་བདུན་མ།", + "text_id": "814e1e3a-f535-4ac7-ac4d-997bd3274633", + "image_url": null, + "first_segment": { + "id": "d7a8c7f5-633c-415b-896e-f5613cafbb7b", + "content": "༄༅། །སློབ་དཔོན་པདྨ་འབྱུང་གནས་ཀྱིས་གསུངས་པའི་གསོལ་འདེབས་ལེའུ་བདུན་མ་བཞུགས་སོ།།\n༄༅། །ཨེ་མ་ཧོ༔ སྤྲོས་བྲལ་ཆོས་ཀྱི་དབྱིངས་ཀྱི་ཞིང་ཁམས་སུ༔ ཆོས་ཉིད་དུས་གསུམ་སྐྱེ་འགག་མེད་པའི་ངང་༔ བྱ་བྲལ་ལྷུན་རྫོགས་བདེ་བ་ཆེན་པོའི་སྐུ༔ ནམ་མཁའ་བཞིན་དུ་ཐུགས་རྗེ་ཕྱོགས་རིས་མེད༔ བླ་མ་ཆོས་ཀྱི་སྐུ་ལ་གསོལ་བ་འདེབས༔\nབདེ་ཆེན་ལྷུན་གྱིས་གྲུབ་པའི་ཞིང་ཁམས་སུ༔ སྐུ་གསུང་ཐུགས་དང་ཡོན་ཏན་འཕྲིན་ལས་ཀྱི༔ ཡེ་ཤེས་ལྔ་ལྡན་བདེ་བར་གཤེགས་པའི་སྐུ༔ ཐུགས་རྗེ་བྱེ་བྲག་སྣ་ཚོགས་སོ་སོར་སྟོན༔ བླ་མ་ལོངས་སྤྱོད་རྫོགས་སྐུར་གསོལ་བ་འདེབས༔" + } + }, + { + "title": "རྟེན་འབྲེལ་བསྟོད་པ།", + "text_id": "2b15f7bb-e8ab-47b7-9f9c-214111c53d14", + "image_url": null, + "first_segment": { + "id": "e44c0943-1619-4f7d-94ea-d6e32e0d5380", + "content": "༄༅། །རྟེན་འབྲེལ་བསྟོད་པ་བཞུགས་སོ།།\nན་མོ་གུ་རུ་མཉྫུ་གྷོ་ཥཱ་ཡ།\nགང་ཞིག་གཟིགས་ཤིང་གསུང་བ་ཡི། །མཁྱེན་དང་སྟོན་པ་བླ་ན་མེད། །རྒྱལ་བ་རྟེན་ཅིང་འབྲེལ་བར་འབྱུང་། །གཟིགས་ཤིང་འདོམས་པ་དེ་ལ་འདུད། །" + } + }, + { + "title": "འཕགས་པ་ཐུགས་རྗེ་ཆེན་པོ་ལ་བསྟོད་ཅིང་གསོལ་བ་འདེབས་པ་ཕན་བདེའི་ཆར་འབེབས།", + "text_id": "6ae2962d-ec9f-4537-8c07-5e930e203c8c", + "image_url": null, + "first_segment": { + "id": "e8947866-e5a2-44ac-bbe0-27a021e11186", + "content": "༄༅། །འཕགས་པ་ཐུགས་རྗེ་ཆེན་པོ་ལ་བསྟོད་ཅིང་གསོལ་བ་འདེབས་པ་ཕན་བདེའི་ཆར་འབེབས་ཞེས་བྱ་བ་བཞུགས་སོ།\n༄༅། །ན་མོ་ཨཱརྱ་ལོ་ཀེ་ཤྭ་རཱ་ཡ། །\nཕྱོགས་བཅུའི་རྒྱལ་བ་རབ་འབྱམས་ཞིང་གི་རྡུལ་དང་མཉམ་པ་དེ་དག་གི །འགྲོ་ལ་ཆགས་པའི་ཀུན་ཏུ་ཆགས་པའི་སྙིང་རྗེ་རྒྱ་མཚོ་ལྟ་བུར་ཟབ། །དེ་ལས་འཁྲུངས་པའི་ཡོན་ཏན་ལྷུན་རྫོགས་སྤྱན་རས་གཟིགས་ཞེས་རྣམ་གྲགས་པ། །མགོན་པོ་བདག་ཅག་ཀུན་གྱིས་བསྟེན་འོས་རིན་ཆེན་རི་འདྲ་དེ་ལ་འདུད། །" + } + }, + { + "title": "སྐབས་གསུམ་པ།", + "text_id": "b3c70823-a286-41a9-b92e-ad710564d6e2", + "image_url": null, + "first_segment": { + "id": "73d9b333-1a4a-4ca7-b09b-3872368dca63", + "content": "སྐབས་གསུམ་པ།\nསྐབས་གསུམ་པ་དང་དབང་པོའི་དགྲ། །དྲི་ཟ་གདེངས་ཅན་གྲུབ་པའི་གཙོ། །ཀུན་གྱི་གཙུག་རྒྱན་ནོར་བུ་ཡིས། །གང་གི་ཞབས་པད་མཛེས་བྱས་པ། །གསེར་འོད་འཁྱུག་པའི་དཔལ་གྱིས་བརྗིད་པའི་སྐུ། །དྲི་ཟའི་དབྱངས་ཀྱིས་བསྐྲུན་དུ་མེད་པའི་གསུང༌། །ཉི་མ་བྱེ་བའི་མདངས་ལས་གསལ་བའི་ཐུགས། །མཐའ་ཡས་འགྲོ་བའི་འདྲེན་མཆོག་ཤྭ་ཀྱའི་ཏོག །དག་པའི་མཁའ་ལ་ཤར་བའི་གཟའ་སྐར་རྣམས། །ཆུ་གཏེར་དབུས་ན་ཡོངས་སུ་གསལ་བ་བཞིན། །དང་བའི་ཡིད་མཚོར་ཁྱེད་ཀྱི་ཡོན་ཏན་གྱི། །གཟུགས་བརྙན་མ་འདྲེས་གསལ་བར་རབ་བཀོད་ནས། །ཟླ་ཟེར་ཕོག་པའི་ཆུ་སྐྱེས་གཞོན་ནུ་བཞིན། །ལག་པའི་སོར་མོ་སྙིང་གའི་ངོས་སུ་ཟུམ། །རྩེ་གཅིག་ཡིད་ཀྱིས་ཁྱོད་སྐུར་མངོན་ཕྱོགས་ཏེ། །ཅུང་ཟད་བསྟོད་ལ་ཐུགས་རྗེ་ཅན་ཁྱོད་དགོངས། །སྐར་མའི་ཚོགས་ཀྱིས་མཁའ་ལ་ཟླ་བ་བཞིན། །རྒྱལ་སྲས་དཔའ་བོ་རྣམས་ཀྱིས་ཀུན་ནས་བསྐོར། །བ་ལང་\nཁྱུ་ཡི་ཐོག་མར་ཁྱུ་མཆོག་བཞིན། །དགྲ་བཅོམ་འཁོར་གྱི་ཚོགས་ཀྱིས་མདུན་དུ་བདར། །ནམ་མཁའི་ཁྱོན་ཀུན་འགེངས་པའི་འོད་ཟེར་གྱི། །དྲྭ་བ་སྟོང་གིས་ཡོངས་སུ་དཀྲིགས་པའི་སྐུ། །ནམ་མཁའི་ལམ་ལས་ཆོ་འཕྲུལ་དུ་མ་ཡིས། །ངང་པའི་རྒྱལ་པོ་བཞིན་དུ་ལྡིང་ཞིང་གཤེགས། །དེ་ཚེ་རིང་ནས་བསྐྲུན་པའི་བཟང་པོའི་ལས། །ཡོངས་སུ་སད་པའི་ལྷ་མི་སྟོང་ཕྲག་བརྒྱ། །ཁྱོད་ཞལ་མཐོང་བའི་མོད་ལ་མི་བསྲུན་ཡིད། །ཉེས་བརྒྱའི་འཆིང་བ་ཀུན་ལས་ལྷོད་པར་གྱུར། །དཔའ་བོ་ཁྱོད་ཀྱིས་བྱང་ཆུབ་ཤིང་དྲུང་དུ། །མཁྱེན་བརྩེའི་དཔུང་གིས་ཡིད་སྲུབས་བདག་པོའི་སྡེ། །མ་ལུས་ཟིལ་གྱིས་གནོན་པར་མཛད་པ་ནི། །དྲག་པོའི་རླུང་དང་ཉེ་བའི་སྤྲིན་ནག་བཞིན། །མདའ་མཚོན་གོ་ཆ་མ་བཟུང་བར། །རང་ཉིད་གཅིག་པུས་བྱེ་བའི་བདུད། །མ་ལུས་ཕམ་མཛད་གཡུལ་གྱི་ལས། །ཁྱོད་ལས་གཞན་པ་སུ་ཡིས་ཤེས། །དེ་ལྟར་ཁྱོད་ཀྱི་བྱམས་པའི་མེས། །འདོད་ལྷའི་སྙིང་ནི་རབ་གདུངས་ཀྱང་། །བརྩེ་བའི་གཏེར་ཁྱོད་ལུས་ཅན་ལ། །རིས་སུ་ཆད་པར་གྱུར་པ་མེད། །ཁྱོད་ནི་འགྲོ་བའི་དོན་གྱི་སླད། །ཅུང་ཟད་ཙམ་ཡང་མི་ངལ་ཞིང་། །འགྲོ་བ་རྣམས་ཀྱང་ཁྱོད་ཀྱི་ནི། །ཡོན་ཏན་བརྗོད་ལ་ངལ་མ་གྱུར། །རྒྱ་མཚོའི་ཀློང་ལྟར་རབ་ཏུ་ཟབ་པའི་ཐུགས། །ལྷ་ཡི་རྔ་བཞིན་ལེགས་པར་འདོམས་པའི་གསུང༌། ལྷུན་པོའི་སྤོ་ལྟར་མངོན་པར འཕགས་པའི་སྐུ། །མཐོང་ཐོས་དྲན་པ་དོན་ལྡན་མཛད་པ་ཁྱོད། །འཇིག་རྟེན་ཀུན་གྱི་སེམས་ཅན་ཐམས་ཅད་ཀྱིས། །དུས་" + } + }, + { + "title": "འཕགས་པ་སྤྱན་རས་གཟིགས་ཀྱི་མཚན་སྔགས།", + "text_id": "7dbb203f-a1f4-4994-85ce-89013d18eefd", + "image_url": null, + "first_segment": { + "id": "18ae26b7-c63d-4464-a8c3-0506360182ad", + "content": "༈ འཕགས་པ་སྤྱན་རས་གཟིགས་ཀྱི་མཚན་སྔགས།\n༄༅། །ཕྱག་སྟོང་འཁོར་ལོས་སྒྱུར་བའི་རྒྱལ་པོ་སྟོང༌། །སྤྱན་སྟོང་བསྐལ་བ་བཟང་པོའི་སངས་རྒྱས་སྟོང༌། །གང་ལ་གང་འདུལ་དེ་ལ་དེར་སྟོན་པ། །བཙུན་པ་སྤྱན་རས་གཟིགས་ལ་ཕྱག་འཚལ་ལོ། །\nཨོཾ་" + } + }, + { + "title": "ལྷ་མོར་བསྟོད་པ་ཡན་ལག་བདུན་པ།", + "text_id": "15db718e-6c0c-49f0-aad6-f31832ff1bbf", + "image_url": null, + "first_segment": { + "id": "8a1aabc5-d8db-4bc0-b731-dcca2472b402", + "content": "ལྷ་མོར་བསྟོད་པ་ཡན་ལག་བདུན་པ།\nབྷྱོཿ དཔལ་ལྡན་ལྷ་མོ་དཔག་པར་དཀའ་བ་ཁྱོད། །ཐབས་ཀྱི་ཆོ་འཕྲུལ་ནམ་མཁའི་མཐའ་ལ་སྤྱོད། །བྱང་ཆུབ་སེམས་དཔའི་སྤྲུལ་པ་ཁྱོད་ཉིད་ལ། །ལུས་ངག་ཡིད་གསུམ་གུས་པས་ཕྱག་འཚལ་ལོ། །\nཁྱོད་ནི་བདག་གི་མཆོད་པས་མ་བསྒྲིབས་ཀྱང༌། །དཔལ་ལྡན་དངོས་གྲུབ་དམ་པ་ཐོབ་བྱའི་ཕྱིར། །འདོད་པའི་ཡོན་ཏན་མཁའ་ལ་ཆར་ཕབ་ནས། །མཆོད་པའི་ཚོགས་འདིས་དབང་པོ་རྒྱས་གྱུར་ཅིག །" + } + }, + { + "title": "མཎྜལ་རྒྱས་བསྡུས་ཁག", + "text_id": "ebd8b369-b003-4993-93da-60a2544de716", + "image_url": null, + "first_segment": { + "id": "c6c4cc72-802a-4d0b-92c7-21f3ffdf87f8", + "content": "མཎྜལ་རྒྱས་བསྡུས་ཁག\nཞིང་ཁམས་འབུལ་བར་ཞུ།\nཨོྃ་བཛྲ་བྷུ་མི་ཨཱཿཧཱུྃ། དབང་ཆེན་གསེར་གྱི་ས་གཞི།" + } + }, + { + "title": "གསོལ་འདེབས་བསམ་པ་མྱུར་འགྲུབ་མ།", + "text_id": "b62b4ef0-46be-48b1-8e90-63ff9686f317", + "image_url": null, + "first_segment": { + "id": "43e58b00-8d58-4bbe-aecd-f76c1dc0c7a3", + "content": "༄༅། །བསམ་པ་མྱུར་འགྲུབ་མ་བཞུགས་སོ།།\n༄༅། །ཨེ་མ་ཧོ། མཚོ་དབུས་གེ་སར་པདྨའི་སྡོང་པོ་ལ། །སྐུ་ལྔ་ཡེ་ཤེས་ལྷུན་གྱིས་གྲུབ་པའི་ལྷ། །རང་བྱུང་ཆེན་པོ་པདྨ་ཡབ་ཡུམ་ནི། །མཁའ་འགྲོའི་སྤྲིང་ཕུང་འཁྲིགས་ལ་གསོལ་བ་འདེབས། །བསམ་པ་མྱུར་དུ་འགྲུབ་པར་བྱིན་གྱིས་རློབས། །\nལས་ངན་སྤྱད་པའི་རྣམ་སྨིན་མཐུས་བསྐྱེད་པའི། །ནད་གདོན་བར་ཆད་དམག་འཁྲུག་མུ་གེའི་ཚོགས། །ཁྱོད་ཞལ་དྲན་པའི་མོད་ལ་ཟད་བྱེད་པའི། །ཞལ་བཞེས་སྙིང་ནས་བསྐུལ་ལོ་ཨུ་རྒྱན་རྗེ། །བསམ་པ་མྱུར་དུ་འགྲུབ་པར་བྱིན་གྱིས་རློབས། །" + } + }, + { + "title": "མཆོད་སྤྲིན།", + "text_id": "60dc0fb5-b50e-4c9b-b9d7-81eb5a4cc8bb", + "image_url": null, + "first_segment": { + "id": "906e3b8b-7b71-4b57-a01d-1b60069f5f7d", + "content": "མཆོད་སྤྲིན།\nཐམས་ཅད་དུ་ནི་ས་གཞི་དག །གསེག་མ་ལ་སོགས་མེད་པ་དང༌། །ལག་མཐིལ་ལྟར་མཉམ་བཻཌཱུརྱའི། །རང་བཞིན་འཇམ་\nཔོར་གནས་གྱུར་ཅིག །ཨོཾ་ན་མོ་བྷ་ག་ཝ་ཏེ། བཛྲ་སྭཱ་ར་པྲ་མརྡ་ནི། ཏ་ཐཱ་ག་ཏ་ཡ། ཨརྷ་ཏེ། སམྱཀྶཾ་བུདྡྷ་ཡ། ཏདྱ་ཐཱ། ཨོཾ་བཛྲ་བཛྲ། མ་ཧཱ་བཛྲ། མཧཱ་ཏེ་ཛ་བཛྲ། མཧཱ་བིདྱཱ་བཛྲ། མཧཱ་བོ་ངྷི་ཙིཏྟ་བཛྲ། མཧཱ་བོ་དྷི་མཎྜོ་པ་སཾ་ཀྲ་མ་ཎ་བཛྲེ་སརྦ་ཀརྨ་ཨཱ་བ་ར་" + } + }, + { + "title": "སློབ་དཔོན་རིན་པོ་ཆེའི་མཚན་སྔགས།", + "text_id": "36d15815-6150-4abe-b594-4c327472e79d", + "image_url": null, + "first_segment": { + "id": "7c29e47b-bcb1-4528-b6c9-8362126c1b7d", + "content": "སློབ་དཔོན་རིན་པོ་ཆེའི་མཚན་སྔགས།\nརྒྱ་གར་པཎ་ཆེན་བོད་ལ་བཀའ་དྲིན་ཆེ༔ པདྨ་འབྱུང་གནས་སྐུ་ལ་འདས་འཁྲུངས་མེད༔ ད་ལྟ་ལྷོ་ནུབ་སྲིན་པོའི་ཁ་གནོན་མཛད༔ ཨོ་རྒྱན་རིན་པོ་ཆེ་ལ་ཕྱག་འཚལ་ལོ༔ ཨོཾ་ཨཱཿ ཧཱུྃ་བཛྲ་གུ་རུ་པདྨ་སིདྡྷི་ཧཱུཾ༔ དུས་གསུམ་སངས་རྒྱས་གུ་རུ་རིན་པོ་ཆེ༔ དངོས་གྲུབ་ཀུན་བདག་བདེ་བ་ཆེན་པོའི་ཞབས༔ བར་ཆད་ཀུན་སེལ་བདུད་འདུལ་དྲག་པོའི་རྩལ༔ གསོལ་བ་འདེབས་སོ་བྱིན་གྱིས་བརླབ་ཏུ་གསོལ༔ ཕྱི་ནང་གསང་བའི་བར་ཆད་ཞི་བ་དང་༔ བསམ་པ་ལྷུན་གྱིས་འགྲུབ་པར་བྱིན་གྱི་རློབས༔" + } + }, + { + "title": "སྲིད་ཞིའི་གཙུག་ནོར་ྋགོང་ས་རྒྱལ་དབང་ཐམས་ཅད་མཁྱེན་གཟིགས་ཆེན་པོ་མཆོག་བསྟན་འགྲོའི་མགོན་དུ་ཞབས་ཟུང་བརྟན་ཅིང་། རླབས་ཆེན་བཞེད་དོན་ལྷུན་འགྲུབ་ཆེད་མཆོག་གསུམ་རྒྱ་མཚོའི་ཐུགས་རྗེ་བསྐུལ་བའི་གསོལ་འདེབས་འཆི་མེད་གྲུབ་པའི་དབྱངས་སྙན་ཞེས་བྱ་བ་བཞུགས་སོ།", + "text_id": "c84511b7-821a-4950-b43f-d4d1e93ba124", + "image_url": null, + "first_segment": { + "id": "e830021f-36de-4dfa-82ec-37ee328981a9", + "content": "སྲིད་ཞིའི་གཙུག་ནོར་ྋགོང་ས་རྒྱལ་དབང་ཐམས་ཅད་མཁྱེན་གཟིགས་ཆེན་པོ་མཆོག་བསྟན་འགྲོའི་མགོན་དུ་ཞབས་ཟུང་བརྟན་ཅིང་། རླབས་ཆེན་བཞེད་དོན་ལྷུན་འགྲུབ་ཆེད་མཆོག་གསུམ་རྒྱ་མཚོའི་ཐུགས་རྗེ་བསྐུལ་བའི་གསོལ་འདེབས་འཆི་མེད་གྲུབ་པའི་དབྱངས་སྙན་ཞེས་བྱ་བ་བཞུགས་སོ། །༄༅། །\nཨོཾ་སྭ་སྟི། རབ་འབྱམས་རྒྱལ་བའི་གསང་གསུམ་མ་ལུས་པ། ། གང་འདུལ་ཅིར་ཡང་འཆར་བའི་སྒྱུ་འཕྲུལ་གར། །སྲིད་ཞིའི་དགེ་ལེགས་ཀུན་འབྱུང་ཡིད་ཞིན་ནོར། །དངོས་བརྒྱུད་དྲིན་ཅན་བླ་མའི་ཚོགས་རྣམས་ལ། །\nབདག་ཅག་གདུང་ཤུགས་དྲག་པོས་གསོལ་འདེབས་ན། །" + } + }, + { + "title": "བོད་སྲུང་ལྷ་སྲུང་གི་འཕྲིན་བསྐུལ།", + "text_id": "49f681d3-d58f-499f-ac97-c5cf8871e82f", + "image_url": null, + "first_segment": { + "id": "d4ec0c8b-c8a2-4d48-afcf-7b8fd99fb5e3", + "content": "བོད་སྲུང་ལྷ་སྲུང་གི་འཕྲིན་བསྐུལ།\nཀྱེཿ མ་སྨད་བསོད་ནམས་མཐུ་དང་སྨོན་ལམ་གིས། ། ཕྱག་ན་པདྨོའི་གདུལ་ཞིང་བསིལ་ལྡན་ལྗོངས། ། མཐའ་དབུས་ཡུལ་ཁམས་སྲུང་ཕྱིར་དེ་དང་དེར། ། གནས་ཤིང་རྒྱུ་བའི་ཡུལ་ལྷ་གཞི་བདག་དང་། །\nཁྱད་པར་ཚེ་རིང་མཆེད་ལྔ་བསྟན་མའི་ཚོགས། ། སྲིད་པའི་ལྷ་དགུ་རེ་ཡི་མགུར་ལྷ་སོགས། ། བོད་སྲུང་ལྷ་སྲུང་གཉེན་པོ་མཆེད་འཁོར་རྣམས། ། གདུང་བས་འབོད་དོ་འདིར་བྱོན་མགོན་དུ་བཞུགས། །" + } + }, + { + "title": "ཡོན་ཏན་གཞིར་གྱུར་མ།", + "text_id": "d5bd562a-c555-4a0e-9394-df90f6ba5ba8", + "image_url": null, + "first_segment": { + "id": "e968313b-835f-4028-9e0b-97a5c7b6c395", + "content": "༄༅། །ཡོན་ཏན་གཞིར་གྱུར་མ་བཞུགས་སོ། །\nཡོན་ཏན་ཀུན་གྱི་གཞིར་གྱུར་དྲིན་ཆེན་རྗེ། །ཚུལ་བཞིན་བསྟེན་པ་ལམ་གྱི་རྩ་བ་རུ། །ལེགས་པར་མཐོང་ནས་འབད་པ་དུ་མ་ཡིས། །གུས་པ་ཆེན་པོས་བསྟེན་པར་བྱིན་གྱིས་རློབས། །\nལན་ཅིག་རྙེད་པའི་དལ་བའི་རྟེན་བཟང་འདི། །ཤིན་ཏུ་རྙེད་དཀའ་དོན་ཆེ་ཤེས་གྱུར་ནས། །ཉིན་མཚན་ཀུན་ཏུ་སྙིང་བོ་ལེན་པའི་བློ། །རྒྱུན་ཆད་མེད་པར་སྐྱེ་བར་བྱིན་གྱིས་རློབས། །" + } + }, + { + "title": "སྟོན་པ་ཐུབ་པའི་དབང་པོར་བསྟོད་པ།", + "text_id": "704b917a-0c8d-4654-a59d-b57f67325ec8", + "image_url": null, + "first_segment": { + "id": "64a2d95b-7240-4c48-97c4-8d3d91705445", + "content": "སྟོན་པ་ཐུབ་པའི་དབང་པོར་བསྟོད་པ།\nསེམས་ཅན་རྣམས་ལ་བརྩེ་བ་ཅན། །ཕྲད་དང་བྲལ་བའི་དགོངས་པ་ཅན། །མི་འབྲལ་བདེ་བའི་དགོངས་པ་ཅན། །ཕན་བདེ་དགོངས་ཁྱོད་ལ་ཕྱག་འཚལ། །\nཐུབ་པ་སྒྲིབ་ཀུན་ངེས་པར་གྲོལ། །འཇིག་རྟེན་ཐམས་ཅད་ཟིལ་གྱིས་གནོན། །ཁྱེད་ཀྱི་མཁྱེན་པས་ཤེས་བྱ་ཁྱབ། །ཐུགས་གྲོལ་ཁྱོད་ལ་ཕྱག་འཚལ་ལོ། །" + } + }, + { + "title": "རྣམ་རྒྱལ་མའི་མཚན་སྔགས།", + "text_id": "76d3da6c-dd8c-4618-a908-6d284d2b947c", + "image_url": null, + "first_segment": { + "id": "e047814d-7bdb-4b11-8d0c-93b8f1f66512", + "content": "རྣམ་རྒྱལ་མའི་མཚན་སྔགས།\nཞལ་གསུམ་ཕྱག་བརྒྱད་རབ་མཛེས་ཞི་བའི་སྐུ། །ཡེ་ཤེས་དཔག་ཡས་ཚེ་ཡི་མཆོག་སྩོལ་མ། །རྣམ་པར་རྒྱལ་མའི་ཞབས་ལ་ཕྱག་འཚལ་ལོ། །ཨོཾ་ཧཱུྃ་སྭཱཧུ། ཨོཾ་ཨ་མྲྀ་ཏྭ་ཨཱ་ཡུརྡ་དེ་སྭཱཧཱ། དགེ་བ་འདི་ཡིས་མྱུར་དུ་བདག །རྣམ་པར་རྒྱལ་མ་འགྲུབ་གྱུར་ནས། །འགྲོ་བ་གཅིག་ཀྱང་མ་ལུས་པ། །དེ་ཡི་ས་ལ་འགོད་པར་ཤོག །" + } + }, + { + "title": "གངས་ཅན་ཤིང་རྟ་ཉེར་ལྔའི་གསོལ་འདེབས།", + "text_id": "c9e2ae6c-fa7b-4b5e-8cc5-ddb684298382", + "image_url": null, + "first_segment": { + "id": "34d0fb51-0e1e-4fa2-94e8-7ba5ec090f2a", + "content": "གངས་ཅན་ཤིང་རྟ་ཉེར་ལྔའི་གསོལ་འདེབས།\n༄༅། །རྩ་གསུམ་ཀུན་འདུས་སློབ་དཔོན་པདྨ་འབྱུང་། ། མཁན་ཆེན་ཞི་འཚོ་ཆོས་རྒྱལ་ཁྲི་སྲོང་ཞབས། ། གནུབས་ཆེན་སངས་རྒྱས་ཉང་སྟོན་ཉི་མ་འོད། ། བཀའ་གཏེར་ཤིང་རྟ་ལྔ་ལ་གསོལ་བ་འདེབས། །\nརྡོ་རྗེ་འཆང་དངོས་བླ་ཆེན་སྙིང་པོའི་ཞབས། ། བསོད་ནམས་རྩེ་མོ་གྲགས་པ་རྒྱལ་མཚན་དང་། ། ས་སྐྱ་པཎ་ཆེན་འཕགས་པ་རིན་པོ་ཆེ། ། རྗེ་བཙུན་གོང་མ་ལྔ་ལ་གསོལ་བ་འདེབས། །" + } + }, + { + "title": "ཞབས་བརྟན་གསོལ་འདེབས་འཆི་མེད་གྲུབ་པ་ཞེས་བྱ་བ་བཞུགས་སོ།", + "text_id": "b775e624-78b7-47dd-afac-f214974be892", + "image_url": null, + "first_segment": { + "id": "848005d3-99a8-4f0d-8f89-eb141e8d07de", + "content": "༄༅། ། སྒྲིབ་བྲལ་ཡེ་ཤེས་མཆོག་གི་རང་བཞིན་སྐུ། །\nའགྲོ་བློར་གང་འཚམས་བཞི་ལྷག་དྲུག་ཅུའི་དབྱངས། །\nགཉིས་སྣང་ཀུན་ཞི་ཆོས་ཀུན་གསལ་བའི་ཐུགས། །" + } + }, + { + "title": "༄༅། །བླ་མ་རྒྱང་འབོད་མོས་གུས་སྙིང་གི་གཟེར་འདེབས་བཞུགས་སོ། །", + "text_id": "b0b4259a-f044-4c14-9751-3c77ced84bd2", + "image_url": null, + "first_segment": { + "id": "250a75be-9f0f-4435-8ee5-1304c7a61751", + "content": "(༄༅། །ན་མོ་གུ་རུ་བེ།བླ་མ་རྒྱང་འབོད་ཀུན་ལ་གྲགས་ཆེའང༌། བྱིན་རླབས་བསྐུལ་བའི་གནད་སྐྱོ་ཤས་ངེས་འབྱུང་གིས་བསྐུལ་བའི་མོས་གུས་ཁ་ཙམ་ཚིག་ཙམ་མ་ཡིན་པར་སྙིང་གི་དཀྱིལ། རུས་པའི་གཏིང་ནས་བསྐྱེད། བླ་མ་ལས་ལྷག་པའི་སངས་རྒྱས་གཞན་ན་མེད་པར་ཐག་ཆོད་པའི་ངེས་ཤེས་དང་ལྡན་པས་དབྱངས་རྟ་སྙན་པོས། )\nབླ་མ་མཁྱེན་ནོ། །\nདྲིན་ཅན་རྩ་བའི་བླ་མ་མཁྱེན་ནོ། །" + } + }, + { + "title": "༄༅། །སྒྲོལ་མ་ཙིཏྟཱ་མ་ཎི་ལ་བརྟེན་པའི་ཐུན་མོང་མ་ཡིན་པའི་བླ་མའི་རྣལ་འབྱོར་ཐར་པར་བགྲོད་པའི་ཐེམ་སྐས་ཞེས་བྱ་བ་བཞུགས་སོ། །", + "text_id": "c2365b70-6888-4be3-87e4-dfb6f0c5f224", + "image_url": null, + "first_segment": { + "id": "61856460-9f61-403d-9ac9-6f8992302a12", + "content": "༄༅། །བདག་ལུས་ཐ་མལ་སྤྱི་བོར་པད་ཟླའི་སྟེང༌། །\nཐུགས་རྗེའི་གཏེར་ཆེན་རྒྱལ་ཡུམ་སྒྲོལ་མ་དང༌། །\nདབྱེར་མེད་དྲིན་ཅན་རྩ་བའི་བླ་མ་ནི། །" + } + }, + { + "title": "བླ་མ་མཆོད་པ་བདེ་ལེགས་རབ་རྒྱས།", + "text_id": "238f6704-d861-449a-b619-1a0c9cf90b9a", + "image_url": null, + "first_segment": { + "id": "eb29d9a9-56d8-4ef5-80bb-f165a2fb5f2f", + "content": "ན་མོ་གུ་རུ་བུདྡྷ་བོ་དྷི་ས་ཏྭ་བྷྱ༔ (བླ་མ་མཆོད་པ་བདེ་ལེགས་རབ་རྒྱས་ཞེས་བྱ་བ། བླ་མ་ལ་ཕྱག་འཚལ་ལོ།། སངས་རྒྱས་ཀུན་དངོས་རྡོ་རྗེ་འཆང་རང་བཞིན་།། རྩ་བརྒྱུད་བླ་མ་རྣམས་ལ་ཕྱག་འཚལ་ནས།། སྐྱབས་གནས་ཀུན་འདུས་བླ་མ་མཆོད་པའི་ཐབས།། ཐུན་མོང་མདོར་བསྡུས་ཙམ་ཞིག་འབྲི་བར་བྱ།། གནས་ཁང་བཟང་པོར་སྐུ་གསུང་ཐུགས་ཀྱི་རྟེན་ལ་སོགས་པ་བཞུགས་སུ་གསོལ་ཏེ།། མཆོད་པ་རྒྱ་ཆེར་བཤམས།།) ཨོཾ་བཛྲ་ཨ་མྲི་ཏ་ཀུཎྜ་ལི་ཧ་ན་ཧ་ན་ཧཱུཾ་ཕཊ། ཨོཾ་སྭ་བྷཱ་ཝ་ཤུདྡྷ་སརྦ་དྷརྨཿ་སྭ་བྷཱཝ་ཤུདྡྷོ྅་ཧཾ། གནས་ཁང་བྷྲཱུཾ་ལས་རིན་པོ་ཆེ་ལས་གྲུབ་པའི་གཞལ་མེད་ཁང་བརྩེགས་མཚན་ཉིད་ཐམས་ཅད་ཡོངས་སུ་རྫོགས་པའི་དབུས་སུ། ཨོཾ་ལས་བྱུང་བའི་རིན་པོ་ཆེ་ལས་གྲུབ་པའི་སྣོད་ཡངས་ཤིང་རྒྱ་ཆེ་བ་རྣམས་ཀྱི་ནང་དུ། ཧཱུཾ་ཞུ་བ་ལས་བྱུང་བའི་ལྷ་རྫས་ལས་གྲུབ་པའི་མཆོད་ཡོན། ཞབས་བསིལ། མེ་ཏོག ། བདུག་སྤོས། སྣང་གསལ། དྲི་ཆབ། ཞལ་ཟས། རོལ་མོ་ལ་སོགས་པ་མཆོད་རྫས་བཟང་ཞིང་དགེ་བ་ནམ་མཁའི་ཁམས་ཐམས་ཅད་གང་བར་གྱུར། ཨོཾ་བཛྲ་ཨརྒྷཾ་སྭཱ་ཧཱ། ཨོཾ་བཛྲཔཱདྱཾ་སྭཱཧཱ། ཨོཾ་བཛྲ་པུཥྤེ་ཨཱ་ཧཱུཾ། ཨོཾ་བཛྲ་དྷུ་པེ་ཨཱ་ཧཱུཾ། ཨོཾ་བཛྲ་ཨཱ་ལོ་ཀེ་ཨཱ་ཧཱུཾ ཨོཾ་བཛྲ་གནྡྷེ་ཨཱ་ཧཱུཾ། ཨོཾ་བཛྲ་ནཻ་ཝི་ཏྱ་ཨཱ་ཧཱུཾ། ཨོཾ་བཛྲ་ཤབྟ་ཨཱཿ་ཧཱུཾ། (ཞེས་རོལ་མོས་བྱིན་དབབ། དེ་ནས་སྐྱབས་འགྲོ་སེམས་བསྐྱེད་ཚད་མེད་བཞི་སྤྱི་དང་མཐུན་པར་བྱ། དེ་ནས།) ཨོཾ་སྭ་བྷཱ་ཝ་ཤུདྡྷ་སརྦ་དྷརྨཿ་སྭ་བྷཱ་བ་ཤུདྡྷོ྅་ཧཾ། སྟོང་པའི་ངང་ལས་མདུན་གྱི་ནམ་མཁར་ཧཱུཾ་ཡིག་ལས་བྱུང་བའི་བསྲུང་བའི་འཁོར་ལོ་ཡངས་ཤིང་རྒྱ་ཆེ་བའི་ནང་དུ། བྷྲཱུཾ་ལས་རིན་པོ་ཆེ་སྣ་ཚོགས་ལས་གྲུབ་པའི་གཞལ་མེད་ཁང་རྒྱན་དང་བཀོད་པ་ཕུན་སུམ་ཚོགས་པ། དེའི་དབུས་སུ་སེང་གེ་ཆེན་པོ་བརྒྱད་ཀྱིས་བཏེགས་པའི་རིན་པོ་ཆེའི་ཁྲི་ཡངས་ཤིང་རྒྱ་ཆེ་བའི་སྟེང་དུ། སྣ་ཚོགས་པདྨ་དང་ཉི་ཟླ་བརྩེགས་པའི་གདན། དེའི་སྟེང་དུ་རྩ་བའི་བླ་མ་རྡོ་རྗེ་འཆང་སྐུ་མདོག་ཨིནྡྲ་ནཱི་ལ་ལྟར་རབ་ཏུ་འབར་བའི་འོད་ཅན། ཕྱག་གཉིས་ཀྱིས་རྡོ་རྗེ་དང་དྲིལ་བུ་ཐུགས་ཀར་བསྣོལ་ནས་བཟུང་བ། ཞབས་རྡོ་རྗེའི་སྐྱིལ་མོ་ཀྲུང་གིས་བརྟན་པར་བཞུགས་པ། དར་དང་རིན་པོ་ཆེའི་རྒྱན་ཐམས་ཅད་ཀྱིས་བརྒྱན་པ། མཚན་བཟང་པོ་སུམ་ཅུ་རྩ་གཉིས་དང་། དཔེ་བྱད་བཟང་པོ་བརྒྱད་ཅུས་སྤྲས་པ། རང་སྣང་གི་ཤེས་རབ་མཆོག་གི་བདེ་བས་ཐུགས་དགྱེས་པ། འོད་དང་འོད་ཟེར་དཔག་ཏུ་མེད་པ་འཕྲོ་བ། སྐུ་དང་གསུང་དང་ཐུགས་ཀྱི་གསང་བ་བསམ་གྱིས་མི་ཁྱབ་པ། མཛད་པ་འཕྲིན་ལས་ཕྱོགས་བཅུ་དུས་གསུམ་དུ་ཁྱབ་པ། དབྱིངས་དང་ཡེ་ཤེས་གཉིས་སུ་མེད་པའི་ངོ་བོ། སྟེང་གི་ཆ་ལ་བརྒྱུད་པའི་བླ་མ། སྐུའི་མཐའ་བསྐོར་དུ་དཔལ་དུས་ཀྱི་འཁོར་ལོ་ཡབ་ཡུམ་གཙོ་བོར་གྱུར་བའི་ཡི་དམ་དཀྱིལ་འཁོར་གྱི་ལྷ་ཚོགས་རྣམས་དང་། སངས་རྒྱས་དང་བྱང་ཆུབ་སེམས་དཔའ་འཕགས་པ་ཉན་རང་གི་ཚོགས་དང་བཅས་པ། འོག་གི་ཆ་ལ་མཁའ་འགྲོ་ཆོས་སྐྱོང་བའི་སྲུང་མའི་ཚོགས་དང་བཅས་པ་ཐམས་ཅད་ཀྱང་རང་རང་གི་མཆོད་པའི་བཀོད་པས་ཉེ་བར་བརྒྱན་པ་དང་བཅས་ཏེ་བཞུགས་པར་གྱུར།\n༈ རྣམ་དག་སྐུ་ནི་སྐྱོན་བྲལ་འཁྲུལ་བ་མེད།།\nཕུན་ཚོགས་གསུང་དབྱངས་ཤེས་བྱ་མ་ལུས་ཁྱབ།།" + } + }, + { + "title": "༄༅། །བླ་མ་མཆོད་པའི་ཆོ་ག་བཞུགས་སོ།", + "text_id": "676324d5-5094-4798-897c-e0fe43e1bb2e", + "image_url": null, + "first_segment": { + "id": "173ee554-6358-41e6-bb71-0161e4636fd9", + "content": "༄༅། །རྒྱ་གར་སྐད་དུ། གུ་རུ་པཱུ་ཛ་སྱ་ཀལྤ་ནཱ་མ། བོད་སྐད་དུ། བླ་མ་མཆོད་པའི་ཆོ་ག་ཞེས་བྱ་བ། བཀའ་དྲིན་མཉམ་མེད་བླ་མ་དམ་པ་རྣམས་ཀྱི་ཞབས་ལ་ཕྱག་འཚལ་ཞིང་སྐྱབས་སུ་མཆིའོ། །བརྩེ་བ་ཆེན་པོས་དུས་དང་གནས་སྐབས་ཐམས་ཅད་དུ་རྗེས་སུ་གཟུང་དུ་གསོལ། །\nགང་ལ་བརྟེན་ན་སྐུ་གསུམ་བདེ་བ་ཆེ། །\nཐུན་མོང་དངོས་གྲུབ་དང་བཅས་སྐད་ཅིག་ལ། །" + } + }, + { + "title": "བླ་མམཆོད་པའི་ཚོགས་མཆོད་བཞུགས་སོ།།", + "text_id": "13b5639a-2ce1-418a-b1e4-66fa63fe6c62", + "image_url": null, + "first_segment": { + "id": "dd6120b5-0ca6-4539-8ef9-1a9000d8ca81", + "content": "ཨོྃཨཱཧཱུྃ། (ལན་གསུམ།) ངོ་བོ་ཡེ་ཤེས་ལ་རྣམ་པ་ནང་མཆོད་དང་མཆོད་རྫས་སོ་སོའི་རྣམ་པ། བྱེད་ལས་དབང་པོ་དྲུག་གི་སྤྱོད་ཡུལ་དུ་བདེ་སྟོང་གི་ཡེ་ཤེས་ཁྱད་བར་ཅན་བསྐྱེད་བས་ས་དང་བར་སྣང་ནམ་མཁའི་ཁྱོན་ཐམས་ཅད་ཡོངས་སུ་ཁྱབ་པས། ཕྱི་ནང་གསང་བའི་མཆོད་སྤྲིན་དམ་རྫས་སྤྱན་གཟིགས་བསམ་གྱིས་མི་ཁྱབ་པས་གང་བར་གྱུར། (རང་ལྟའི་རྣལ་འབྱོར་དང་ལྡན་པའི་སྒོ་ནས།) ཨེ་མ་ཧོ་ཡེ་ཤེས་རོལ་བ་ཆེ།།ཞ\nིང་ཁམས་ཐམས་ཅད་རྡོ་རྗེའི་ཞིང་། །ག\nནས་རྣམས་རྡོ་རྗེའི་ཕོ་བྲང་ཆེ།།ཀ" + } + }, + { + "title": "༄༅། །དཔལ་རོང་ཟོམ་པཎྜི་ཏ་ཆེན་པོའི་བླ་མའི་རྣལ་འབྱོར་བྱིན་རླབས་ཆར་འབེབས་བཞུགས།", + "text_id": "46ba2747-72a0-4b9b-a8df-2adf16f8b3dd", + "image_url": null, + "first_segment": { + "id": "c38e5088-d3f0-4022-8fe3-06e6ce4e8c9d", + "content": "༄༅། །དཔལ་རོང་ཟོམ་པཎྜི་ཏ་ཆེན་པོའི་བླ་མའི་རྣལ་འབྱོར་བྱིན་རླབས་ཆར་འབེབས་བཞུགས།\nརང་ལུས་རྡོ་རྗེ་རྣལ་འབྱོར་མར་གསལ་བའི། །\nསྤྱི་བོར་འདབ་སྟོང་ཉི་ཟླའི་དཀྱིལ་འཁོར་དབུས། །" + } + }, + { + "title": "༄༅། །རྒྱ་ནག་པོའི་སྐག་ཟློག་ཅེས་བྱ་བ་བཞུགས་སོ།།", + "text_id": "c183902b-7b3e-412d-b82b-835f9ce252a4", + "image_url": null, + "first_segment": { + "id": "57feaa64-148f-426f-a199-eb1e3df550af", + "content": "༈ འཕགས་པ་འཇམ་དཔལ་དབྱངས་ལ་ཕྱག་འཚལ་ལོ།།\nརྒྱ་ནག་པོའི་སྐག་ཟློག་ཅེས་བྱ་བ།\nའཕགས་པ་ཀུན་ལ་ཕྱག་འཚལ་ལོ། །" + } + }, + { + "title": "༈ ཙཀྲ་བཅུ་གསུམ་པའི་གཟུངས་བཞུགས་སོ། །", + "text_id": "c25b3c4a-5018-4b3a-8da2-d6601796da0b", + "image_url": null, + "first_segment": { + "id": "bf85f50f-5713-4fb5-be10-1b3cdb197316", + "content": "༄༅། །བཅོམ་ལྡན་འདས་དེ་བཞིན་གཤེགས་པ་དགྲ་བཅོམ་པ་ཡང་དག་པར་རྫོགས་པའི་སངས་རྒྱས་ཤཱཀྱ་ཐུབ་པ་ལ་ཕྱག་འཚལ་ལོ། །ཏདྱ་ཐཱ། ཨོཾ་མུ་ནི་མུ་ནི་མཧཱ་མུ་ན་ཡེ་སྭཱཧཱ། བདག་འཁོར་དང་བཅས་པ་ལ་བསྲུང་བ་དང༌། བསྐྱབ་པ་དང༌། སྦ་བ་དང་།བྱིན་གྱིས་བརླབ་པ་དམ་པ་མཛད་དུ་གསོལ། །སྲི་ངན་པ་ཐམས་ཅད་ལས་ཐར་བར་མཛད་དུ་གསོལ། །དུ་རུ་དུ་རུ་ཙཀྲ། བཛྲ་ཡ་བཛྲ་ཡ་ཙཀྲ། ཧ་ན་ཧ་ན་ཙཀྲ། མ་ར་ཡ་མ་ར་ཡ་ཙཀྲ། གྷིན་དུ་མ་ལེ་ཨེ་ནན་ཙཀྲ། ཛཱ་ལ་ཛཱ་ལ་ཙཀྲ། ཧཱུྃ་ཧཱུྃ་ཕཊ་ཕཊ་ཙཀྲ། སརྦ་ཤ་གི་ནི་ཨ་བ་ར་ཎ་ཙཀྲ། བྷ་ལ་ཡ་བྷ་ལ་ཡ་ཙཀྲ། ན་ག་ཤ་ཡ་ན་ག་ཤ་ཡ་ཙཀྲ། ཧཱུཾ་ཧཱུཾ་ཕཊ་ཕཊ་ཙཀྲ། བྷ་མ་བྷ་མ་ཙཀྲ། ས་མནྟ་ཀ་ར་ཙ་ཏྲི་ཏིག་ན་ཙཀྲ།བདག་འཁོར་དང་བཅས་པ་དང་། ཆོས་མཛད་པ་དང་། དཀར་པོའི་ཕྱོགས་མཐའ་ཡས་པ་ལ་གནོད་པའི་དགྲ་བགེགས་འབྱུང་པོ་མི་མ་ཡིན་པའི་ཕྱོགས་ཐམས་ཅད་ཟློག་ཅིག །ཐུལ་ཅིག །རྐྱེན་དང་བར་དུ་གཅོད་པ་ཐམས་ཅད་ཟློག་ཅིག །བྱེར་ཅིག།སྲུངས་ཤིག །ནད་སྲི་དང༌། འཆི་སྲི་དང༌། གོད་སྲི་དང༌། རྟ་སྲི་དང༌། ཡེར་བའི་སྲི་དང༌། ལོའི་སྲི་དང་། ཟླ་བའི་སྲི་དང༌། ཞག་དུས་ཀྱི་སྲི་དང༌། ཁ་སྨྲས་ཀྱི་སྲི་དང༌། སྲི་ངན་པ་ཐམས་ཅད་ཟློག་ཅིག །གཞན་ཡང་སྲི་ངན་པ་ཐམས་ཅད་ལས་རྣམ་པ་ཀུན་ཏུ་སྲུངས་ཤིག །ཟློག་ཅིག །ཞི་བར་གྱུར་ཅིག །བདག་ཅག་འཁོར་དང་བཅས་པ་ལ་ནད་རིམས་དང་།གདོན་དང༌། སྔགས་ཟོར་དང༌། རྦད་འདྲེ་དང་། གློ་བུར་གྱི་ནད་དང་། རྒྱལ་པོའི་ཆད་པ་དང་། ཕྱོགས་ངན་པ་དང་། གནོད་པ་དང་། ནད་སྲི་དང་། འཆི་སྲི་དང་། དུས་སྲི་དང༌། རྒན་སྲི་དང༌། གཞོན་སྲི་དང་། དར་མའི་སྲི་དང༌། ལོའི་སྲི་དང༌། ཟླ་བའི་སྲི་དང༌། ཞག་སྲི་དང༌། ཟ་མའི་སྲི་དང༌། སྐག་བདུན་ཟུར་དང་། ཕྱི་སྐྱེལ་དང་། གདོན་སུམ་བརྒྱ་དྲུག་ཅུ་དང་། རྐྱེན་བཞི་ཁྲི་ཆིག་སྟོང་དང་། ལྟས་ངན་མི་མཐུན་པ་བརྒྱད་ཅུ་རྩ་གཅིག་གི་གནོད་པ་ཐམས་ཅད་དང་། མ་རུངས་པ་གདུག་པ་ཅན་ཐམས་ཅད་འཇོམས་པར་གྱུར་ཅིག །ཟློག་པར་གྱུར་ཅིག །སྒྱུར་བར་གྱུར་ཅིག །སྲུང་བར་གྱུར་ཅིག །ཐུབ་པར་གྱུར་ཅིག །དེ་ལྟར་བརྗོད་པའི་ཡོན་ཏན་བསམ་གྱིས་མི་ཁྱབ་ཅིང་། སྲིའི་གནོད་པ་ཐམས་ཅད་ལས་གྲོལ་བར་འགྱུར་རོ། །བཅོམ་ལྡན་འདས་ཀྱིས་དེ་སྐད་ཅེས་བཀའ་སྩལ་པ་དང༌། ཚེ་དང་ལྡན་པ་ཀུན་དགའ་བོ་ཡི་རངས་ཏེ། བཅོམ་ལྡན་འདས་ཀྱིས་གསུངས་པ་ལ་མངོན་པར་བསྟོད་དོ། །ཙཀྲ་བཅུ་གསུམ་པའི་གཟུངས་རྫོགས་སོ། །" + } + }, + { + "title": "༄༅། །དཔལ་གསང་བ་འདུས་པའི་བསྒོམ་བཟླས་རྒྱུན་འཁྱེར་བཞུགས་སོ། །", + "text_id": "02fd98c7-d25b-423f-b01d-ea221d268371", + "image_url": null, + "first_segment": { + "id": "8d9a9338-330f-46b2-9883-c1ce5a08ab6c", + "content": "རྒྱལ་ཀུན་གསང་གསུམ་ནོར་བུའི་དབྱིག །\nགང་དུ་འདུས་པའི་ཟ་མ་ཏོག །\nབླ་མ་མི་བསྐྱོད་རྡོ་རྗེ་ལ། །" + } + }, + { + "title": "༄༅། །བདེ་མཆོག་ལྷ་ལྔའི་སྒྲུབ་ཐབས་མདོར་བསྡུས་བཞུགས་སོ། །", + "text_id": "948c1adc-4fdf-4bc9-9dc9-d5ab6646281e", + "image_url": null, + "first_segment": { + "id": "7c26ddcb-12c3-4c54-af00-f6a2cdee9ba7", + "content": "༄༅། །ན་མོ་གུ་རུ་ཙཀྲ་སམྦ་རཱ་ཡ། བདེ་མཆོག་ལྷ་ལྔའི་སྒྲུབ་ཐབས་མདོར་བསྡུས་ནི།\nསངས་རྒྱས་ཆོས་དང་དགེ་འདུན་ལ །\nརྟག་ཏུ་བདག་ནི་སྐྱབས་སུ་མཆི། །" + } + }, + { + "title": "བཅོམ་ལྡན་འདས་དཔལ་རྡོ་རྗེ་འཇིགས་བྱེད་དཔའ་བོ་གཅིག་པའི་སྒྲུབ་ཐབས་ཤིན་ཏུ་མདོར་བསྡུས་བཞུགས་སོ། །", + "text_id": "6b01341f-deac-4de7-acff-e13a0edd9cfe", + "image_url": null, + "first_segment": { + "id": "568de3aa-cd06-4975-96d9-b947e8ccf8df", + "content": "༄༅། །ན་མོ་གུ་རུ་བྷྱ༔\nཁྱབ་བདག་འཇམ་དཔལ་རྡོ་རྗེ་གཤིན་རྗེ་གཤེད། །\nརྒྱལ་བ་ཀུན་དངོས་རྗེ་བཙུན་ཙོང་ཁ་པ། །" + } + }, + { + "title": "༄༅། །དཔལ་འཁོར་ལོ་སྡོམ་པའི་དག་པ་གསུམ་གྱི་རྣལ་འབྱོར་བཞུགས་སོ།།", + "text_id": "bfb7ad85-4d4b-496b-b9b9-40c689cf95f9", + "image_url": null, + "first_segment": { + "id": "35cd17b4-43c5-4ae4-96c8-dda12b70e386", + "content": "༄༅༅། །སྐྱབས་འགྲོ་སེམས་བསྐྱེད་ནི།\nསངས་རྒྱས་ཆོས་དང་དགེ་འདུན་ལ། །\nརྟག་ཏུ་བདག་ནི་སྐྱབས་མཆི། །" + } + }, + { + "title": "མཐེ་པོའི་སྨན་ཆུའི་ལྷ་མོའི་གསོལ་མཆོད་བཞུགས་སོ། །", + "text_id": "e8725667-1e01-4438-97a6-3eae983bf098", + "image_url": null, + "first_segment": { + "id": "9afaea21-b457-44eb-acb9-29c8cdd2b2be", + "content": "ན་མཿསྭ་ར་སྭ་སྟྱཻ། བ\nླ་མ་ཡི་དམ་རྡོ་རྗེ་འཇིགས་བྱེད་དང༌། །ད\nབྱེར་མེད་རྒྱལ་བ་གཉིས་པའི་ཞབས་པད་ལ། །ག" + } + }, + { + "title": "༄༅། །གཙོ་རྒྱལ་མ་བཞུགས་སོ། །", + "text_id": "6883f6e1-d24a-412b-9028-9d3e13d5e498", + "image_url": null, + "first_segment": { + "id": "1eef9701-1256-4636-b32d-1d6a54e3f520", + "content": "༄༅། །གཙོ་བོ་རྒྱལ་བ་འགྲོ་བའི་མགོན། །\nའགྲོ་བ་སྐྱོབ་པའི་དོན་བརྩོན་པ། །\nསྟོབས་ཆེན་འཇིགས་པ་ཀུན་སེལ་ལ། །" + } + }, + { + "title": "༄༅། །ཐོག་མཐའ་མ་བཞུགས་སོ། །", + "text_id": "d490e163-d8fc-4324-a74f-69fde9e4f251", + "image_url": null, + "first_segment": { + "id": "deb17189-4bf1-459d-8cc5-8422bb764e6e", + "content": "༄༅། །ཕྱོགས་བཅུའི་རྒྱལ་བ་སྲས་དང་བཅས་པ་ཐམས་ཅད་ལ་ཕྱག་འཚལ་ལོ། །\nམཐའ་ཡས་འགྲོ་བ་སྲིད་ལས་བསྒྲལ་བྱའི་ཕྱིར། །\nམཐའ་མེད་སྨོན་ལམ་ལྷག་བསམ་དག་པས་གདབ། །" + } + }, + { + "title": "༄༅། །བདེ་སྨོན་བཞུགས་སོ། །", + "text_id": "ca858f19-2c96-489b-89ba-339792cbf09a", + "image_url": null, + "first_segment": { + "id": "46a2a6c6-c592-4e57-8641-eb4341a6a631", + "content": "༄༅། །བདེ་སྨོན་བཞུགས་སོ། །\n༄༅། །ཕུལ་བྱུང་མཛད་པས་འགྲོ་ལ་མི་ཟད་དཔལ་སྟེར་ཞིང་། །\nལན་ཅིག་དྲན་པས་འཆི་བདག་འཇིགས་པ་རིང་དུ་དོར། །" + } + }, + { + "title": "༄༅། །མྱུར་མཛད་རྣམ་གསུམ་བཞུགས་སོ། །", + "text_id": "889c77f6-3f91-497f-a2a4-0e7ff05ecb93", + "image_url": null, + "first_segment": { + "id": "8fd33043-3726-4e61-bb9b-bb8f1e2646c0", + "content": "ཧཱུཾ་མྱུར་མཛད་སྤྱན་རས་གཟིགས་ལ་ཕྱག་འཚལ་ལོ། །\nཞབས་གདུབ་དང་བཅས་བི་ནཱ་ཡ་ཀ་མནན། །\nནག་པོ་ཆེན་པོ་སྟག་གི་ཤམ་ཐབས་ཅན། །" + } + }, + { + "title": "ཆོས་རྒྱལ་ནང་སྒྲུབ་ཀྱི་བསྟོད་པ་བཞུགས་སོ། །", + "text_id": "b38b40f6-6314-4ced-ab18-221bde9d760f", + "image_url": null, + "first_segment": { + "id": "a36c277a-0b00-4add-a8fe-f27f93daff02", + "content": "༄༅། །ན་མཿཤྲཱི་བཛྲ་བྷེ་ར་ཝཱ་ཡ།\nབརྐྱངས་བསྐུམས་ཞབས་ནི་ཅུང་ཟད་བརྡབས་པ་ཙམ་གྱིས་དཀྱིལ་འཁོར་བཞིར་བཅས་རི་དབང་ཤིག་ཤིག་པོར་འགྱུར་ཞིང༌། །\nགཏུམ་དྲག་མ་ཧེའི་ཞལ་ནི་རབ་ཏུ་གདངས་པས་བསྒྲགས་པའི་གད་རྒྱངས་ཆེན་པོས་ས་གསུམ་ཀུན་འགེངས་པ། །" + } + }, + { + "title": "༄༅། །དཔལ་ལྡན་ལྷ་མོའི་བསྟོད་པ་བཞུགས་སོ། །", + "text_id": "a7d5c0d1-aa1c-4a84-8eeb-8dc12071f322", + "image_url": null, + "first_segment": { + "id": "4bd476c6-67f4-4e35-966b-698abb8fe9b1", + "content": "༄༅། །བྷྱོཿ སེམས་ཉིད་འཕྲིན་ལས་རྣམ་བཞིའི་ཁྱད་པར་ནི། །\nསེམས་ཉིད་གུད་ན་མེད་ཅིང་སེམས་ཀྱང་མེད། །\nདོན་དམ་དབྱེར་མེད་ཁ་དོག་གཟུགས་ཀྱང་མེད། །" + } + }, + { + "title": "༄༅། །བདེ་གཤེགས་རིགས་ལྔ་བཞུགས་སོ། །", + "text_id": "cec37dce-3176-4abf-a775-85a2e3325c65", + "image_url": null, + "first_segment": { + "id": "c3a8472c-1d45-418e-8c2a-bb038a5f2bf4", + "content": "༄༅། །བདེ་གཤེགས་རིགས་ལྔའི་ཡེ་ཤེས་སྒྱུ་འཕྲུལ་གར། །\nབོད་ཁམས་སྐྱོང་མཛད་བསྟན་སྲུང་དྲེགས་པའི་གཙོ། །\nཕྲིན་ལས་རྒྱལ་པོ་སྤྲུལ་ཡུམ་བློན་པོར་བཅས། །" + } + }, + { + "title": "༄༅། །སྲིད་ཞིའི་གཙུག་ནོར་ྋགོང་ས་ྋསྐྱབས་མགོན་ྋརྒྱལ་དབང་ཐམས་ཅད་མཁྱེན་གཟིགས་ཆེན་པ་མཆོག་བསྟན་འགྲོའི་མགོན་དུ་ཞབས་ཟུང་བརྟན་ཅིང་རླབས་ཆེན་བཞེད་དོན་ལྷུན་འགྲུབ་ཆེད་མཆོག་གསུམ་རྒྱ་མཚོའི་ཐུགས་རྗེ་བསྐུལ་བའི་གསོལ་འདེབས་འཆི་མེད་གྲུབ་པའི་དབྱངས་སྙན་ཞེས་བྱ་བ།", + "text_id": "9b181f4a-d081-4bed-ac92-a6f5c1364c60", + "image_url": null, + "first_segment": { + "id": "34cc8c9c-edcf-4f2c-b964-11cd18b5e355", + "content": "༄༅། །ཨོཾ་སྭ་སྟི།\nརབ་འབྱམས་རྒྱལ་བའི་གསང་གསུམ་མ་ལུས་པ། །\nགང་འདུལ་ཅིར་ཡང་འཆར་བའི་སྒྱུ་འཕྲུལ་གར། །" + } + }, + { + "title": "ཐུབ་རྣམས་སྤངས་རྟོགས་མ།", + "text_id": "f8179184-ab79-4f3c-b7aa-23c5f5a8723e", + "image_url": null, + "first_segment": { + "id": "af9238f9-e9e5-4807-949e-88dab3909824", + "content": "སྭ་སྟི་སིདྡྷཾ།\nཐུབ་རྣམས་སྤངས་རྟོགས་ཡོན་ཏན་རབ་མཉམ་ཡང་། །\nགདུལ་དཀའི་འགྲོ་ལ་ཉེར་བརྩེའི་སྙིང་སྟོབས་ལ། །" + } + }, + { + "title": "༄༅། །བསྔོས་ཤ་ཟོས་པའི་སྡིག་སྦྱོང་སྨོན་ལམ་ཡན་ལག་བདུན་པ་ཉེས་ལྟུང་དག་བྱེད་ཅེས་བྱ་བ་བཞུགས་སོ།", + "text_id": "f4c51502-b6af-4954-ac23-9b24382ac905", + "image_url": null, + "first_segment": { + "id": "bebe5652-726b-4abf-bc33-7c7155e689ef", + "content": "རྒྱལ་བ་རིན་ཆེན་གཙུག་ཏོར་ཅན་ལ་ཕྱག་འཚལ་ལོ། །\nདཔལ་མཆོག་དང་པོའི་སངས་རྒྱས་རྣམ་སྣང་མཛད།\nཞིང་ཁམས་རྒྱ་མཚོའི་མངའ་བདག་འོད་དཔག་མེད། །" + } + }, + { + "title": "༄༅། །ས་བདག་དང་འབྱུང་བཞིའི་ལྷ་མོར་གསོལ་མཆོད་བཞུགས་སོ།།", + "text_id": "a9335903-7254-430e-9afc-11bd3e05a40e", + "image_url": null, + "first_segment": { + "id": "649a498e-a1b7-4419-8449-87ec0ecfba0f", + "content": "(༄༅། །གཏོར་སྣོད་གཙང་མ་ཀླུ་སྨན་དང་བུམ་རྫས་ཉེར་ལྔ་སོགས་བསྲེས་པའི་ཟན་རིལ་གྱིས་དགང་བ་འོ་མ་དང་ཆུ་གཙང་གིས་སྦྲན་པ་དང་། རིན་པོ་ཆེ་སྨན་འབྲུ་སོགས་བཏབ་པའི་འོ་ཆབ་ཀྱི་གསེར་སྐྱེམས་བཅས་བཤམས་ལ་བྱིན་གྱིས་བརླབས། )\nས་བདག་རྒྱལ་པོ་གསེར་མདོག་ཅན། །\nས་ཡི་ལྷ་མོ་བརྟན་མ་དང༌། །" + } + }, + { + "title": "༈ ཐབས་མཁས་ཐུགས་རྗེ་མ་བཞུགས་སོ།།", + "text_id": "b2a5a20d-ec99-4273-890e-8295015f4387", + "image_url": null, + "first_segment": { + "id": "d6948f76-4790-472b-bda4-401cb8f53bd9", + "content": "ཐབས་མཁས་ཐུགས་རྗེས་ཤཱཀྱའི་རིགས་འཁྲུངས་ཤིང་། །\nགཞན་གྱིས་མི་ཐུབ་བདུད་ཀྱི་དཔུང་བཅོམ་པ། །\nགསེར་གྱི་ལྷུན་པོ་ལྟ་བུར་བརྗིད་པའི་སྐུ། །" + } + }, + { + "title": "༄༅། །སློབ་དཔོན་ཐུགས་རྗེ་ཅན་གྱི་ཐུགས་དམ་གནད་ནས་སྐུལ་བའི་གདུང་དབྱངས་གསོལ་འདེབས་བཞུགས་སོ། །", + "text_id": "4225a589-eed5-49b5-970d-963f1e2ffcc3", + "image_url": null, + "first_segment": { + "id": "ab0dd545-1aa5-44ad-985b-f28d8fcb50ef", + "content": "མཐའ་བྲལ་དོན་དམ་འོད་གསལ་གཉུག་མའི་གཤིས། །\nལྷུན་གྲུབ་མཚན་དཔེའི་གཟི་འབར་ལོངས་སྤྱོད་རྫོགས། །\nའགྲོ་ཁམས་ཇི་བཞིན་སྣ་ཚོགས་སྤྲུལ་པའི་སྐུ། །" + } + }, + { + "title": "༄༅། །གཅོད་ཡུལ་རྒྱ་མཚོའི་སྙིང་པོ་སྟན་ཐོག་གཅིག་ཏུ་ཉམས་སུ་ལེན་པའི་ཚུལ་ཟབ་མོའི་ཡང་ཞུན་ཅེས་བྱ་བ་བཞུགས་སོ། །", + "text_id": "b1bfc066-d505-450b-82cb-9636568ec8d9", + "image_url": null, + "first_segment": { + "id": "527ac5ad-7c9f-42b2-ad64-81b61b661b31", + "content": "༄༅། །ན་མོ་གུ་རུ།\nའཁྲུལ་རྟོག་ཆོས་སྐུ་དག་པ་ཆེར། །\nགཅོད་པའི་ཐབས་མང་སྟོན་མཛད་པ། །" + } + }, + { + "title": "༄༅། །བདུད་འཇོམས་གཏེར་གསར་སྔོན་འགྲོའི་ངག་འདོན་བསྡུས་པ་བཞུགས་སོ། །", + "text_id": "c57d91b3-9e63-41da-867a-54928a24d4b6", + "image_url": null, + "first_segment": { + "id": "fe49d4ac-4be6-4cfe-b8c0-bf731bb09c98", + "content": "བདུད་འཇོམས་གཏེར་གསར་སྔོན་འགྲོའི་ངག་འདོན་བསྡུས་པ་འདིའི་གོ་དོན་ས་བཅད་ལ།\n(དང་པོ་སྦྱོར་བ་བློ་ལྡོག་རྣམ་བཞིའི་ངག་འདོན་ནི།)\nན་མོ། བསླུ་མེད་གཏན་གྱི་མགོན་པོ་བླ་མ་མཁྱེན། །" + } + }, + { + "title": "༈ ཚེ་རབས་རྗེས་འཛིན་བཞུགས་སོ། །", + "text_id": "0a79f62a-2dc3-418d-b3e3-68fd12ee6a9b", + "image_url": null, + "first_segment": { + "id": "aac07eae-acf6-4c1e-b929-ff98032aa5fc", + "content": "༄༅། །འགྲོ་ལ་ཕན་བཞེད་ཐུགས་རྗེ་གོམས་པ་ལས། །\nགཅིག་ཏུ་དགེ་བ་རྗེས་བསྟན་ཆོ་འཕྲུལ་གྱིས། །\nཞི་བསིལ་ཕན་བདེའི་འདོད་དོན་སྒྲུབ་མཁས་པ། །" + } + }, + { + "title": "༈ དགོངས་གཅིག་ལྷན་ཐབས་བཞུགས་སོ།།", + "text_id": "cea0bed1-20f8-47ef-9160-0a7e188a344a", + "image_url": null, + "first_segment": { + "id": "a951d762-c1f2-4724-9c86-b1f5157e771f", + "content": "ན་མོ་གུ་རུ།\nབྱང་ཆུབ་སྙིང་པོ་རྡུལ་བྲལ་ཞིང་ཁམས་སུ། །\nརང་བྱུང་རྒྱལ་བ་ཡང་དག་རྫོགས་སངས་རྒྱས། །" + } + }, + { + "title": "༄༅། །རྗེ་བཙུན་རས་པ་ཆེན་པོ་ལ་བརྟེན་པའི་བླ་མའི་རྣལ་འབྱོར་ཚོགས་མཆོད་དང་བཅས་པ་ཡེ་ཤེས་དཔལ་འབར་ཞེས་བྱ་བ་བཞུགས་སོ། །", + "text_id": "a6e8c1c6-c28f-4384-af30-fd3140b2bbab", + "image_url": null, + "first_segment": { + "id": "b48b9c00-92b1-4e62-ac2b-f8c8d849b49c", + "content": "རྡོ་རྗེ་འཆང་གི་གོ་འཕང་ནི། །\nསྐུ་ཚེ་གཅིག་གིས་མངོན་མཛད་པ། །\nགངས་ཅན་གྲུབ་པའི་དབང་ཕྱུག་ལ། །" + } + }, + { + "title": "རྗེ་གསང་བའི་རྣམ་ཐར།", + "text_id": "157ed003-1b25-40d9-8ff8-35e138f41ca9", + "image_url": null, + "first_segment": { + "id": "6d6f496d-158b-4a6f-8286-46c2b7edae42", + "content": "༄༅། །ཆོས་ཀྱི་རྒྱལ་པོ་ཙོང་ཁ་བ་ཕྱག་འཚལ་ལོ། །\nགང་གི་མཁྱེན་པ་རབ་ཡངས་ནམ་མཁའ་ལ། །\nཇི་ལྟ་ཇི་སྙེད་མཁྱེན་པའི་འོད་ཟེར་ཅན། །" + } + }, + { + "title": "༄༅། །རི་བོ་བསངས་མཆོད་བཞུགས་སོ༔", + "text_id": "8a9d081f-d79f-4c18-a514-b7b11fbb04aa", + "image_url": null, + "first_segment": { + "id": "8595ef29-11ee-499c-b15f-6345af2cd3aa", + "content": "བྷྲཱུྃ༔ རིན་ཆེན་སྣ་ཚོགས་དྭངས་མའི་སྣོད་ཡངས་སུ༔\nའཇིག་རྟེན་སྲིད་པའི་འདོད་རྒུ་དམ་ཚིག་རྫས༔\nའབྲུ་གསུམ་ཡེ་ཤེས་བདུད་རྩིར་བྱིན་བརླབས་པས༔" + } + }, + { + "title": "༄༅། །ཁྱུང་པོའི་སྨོན་ལམ།", + "text_id": "bd462c21-7d4d-41cd-a305-3d4a50b24999", + "image_url": null, + "first_segment": { + "id": "8621ad1c-bc1a-473c-8b1b-2e7183793ec0", + "content": "སྐུ་གསུམ་མཁྱེན་བཅུ་ཆོས་ཀྱི་རྗེ། །\nབླ་མ་རྣམས་དང་ཡི་དམ་ལྷ། །\nམཁའ་འགྲོ་ཆོས་སྐྱོང་ལ་སོགས་ཀུན། །" + } + }, + { + "title": "༈ བོད་སྐྱོང་ལྷ་སྲུང་གི་འཕྲིན་བསྐུལ།", + "text_id": "fd5cf765-1c04-46a3-b70c-0ee262d7fcd4", + "image_url": null, + "first_segment": { + "id": "83fbb85c-19b5-4d42-919a-b683a6bcdfef", + "content": "༄༅། །ཀྱེ་མ་རྨད་བསོད་ནམས་མཐུ་དང་སྨོན་ལམ་གྱིས། །\nཕྱག་ན་པདྨོའི་གདུལ་ཞིང་བསིལ་ལྡན་ལྗོངས། །\nམཐའ་དབུས་ཡུལ་ཁམས་སྐྱོང་ཕྱིར་དེ་དང་དེར། །" + } + }, + { + "title": "༄༅། །རླུང་རྟ་ཡར་བསྐྱེད་བསངས་མཆོད་བཞུགས་སོ།།", + "text_id": "0600bb25-8f17-44fa-a809-15b710bce538", + "image_url": null, + "first_segment": { + "id": "34566818-c89a-44d1-bad3-e2b44f80f23f", + "content": "རྃ་ཡྃ་ཁཾ། བཟང་མཆོད་ཟག་པ་མེད་པ་འདོད་ཡོན་བདུད་རྩིའི་སྤྲིན་ཆེན་པོར་གྱུར་བར་བསམ་ཞིང་། ཨོཾ་ཨཱཿཧཱུྃ༔གིས་རྒྱ་ཆེར་སྤེལ་ལ།\nཀྱེེ༔ བསངས་མཆོད་ཕུན་སུམ་ཚོགས་པ་འདི༔\nསངས་རྒྱས་ཆོས་དང་དགེ་འདུན་དང་༔" + } + }, + { + "title": "༄༅། །ཐུབ་ཆོག་བྱིན་རླབས་གཏེར་མཛོད་བཞུགས་སོ། །", + "text_id": "8eff3cc0-aef1-4dbd-b85c-0217606ea338", + "image_url": null, + "first_segment": { + "id": "035dd0a3-caf5-4272-bd57-6d69627e42df", + "content": "༄༅། །ཐུབ་ཆོག་བྱིན་རླབས་གཏེར་མཛོད་བཞུགས་སོ། །\nན་མོ་གུ་རུ་ཤཱཀྱ་མུ་ན་ཡེ།\nདེ་ཡང་མདོ་ཏིང་འཛིན་རྒྱལ་པོ་ལས། འཆག་དང་འདུག་དང་འགྲེང་དང་ཉལ་བ་ན། །མི་གང་ཐུབ་པའི་ཟླ་བ་དྲན་བྱེད་པ། །དེ་ཡི་མདུན་ན་རྟག་ཏུ་སྟོན་པ་བཞུགས། །དེ་ནི་རྒྱ་ཆེན་མྱ་ངན་འདའ་བར་འགྱུར། ཞེས་དང་། སྐུ་ལུས་དག་ནི་གསེར་གྱི་མདོག་འདྲ་བས། །འཇིག་རྟེན་མགོན་པོ་ཀུན་ནས་རབ་ཏུ་མཛེས། དམིགས་པ་འདི་ལ་གང་གི་སེམས་འཇུག་པ། །བྱང་ཆུབ་སེམས་དཔའ་དེ་ནི་མཉམ་བཞག་ཡིན། ཞེས་གསུངས་པ་བཞིན་དུ། བདག་ཅག་རྣམས་ཀྱི་སྟོན་པ་མཚུངས་པ་མེད་པ་ཐུབ་པའི་དབང་པོ་རྗེས་སུ་དྲན་པའི་རྣལ་འབྱོར་དུ་བྱ་བ་ནི། འདི་ལྟ་སྟེ།" + } + }, + { + "title": "༄༅། །ཨོ་རྒྱན་པདྨས་མཛད་པའི་བཀའ་བསྡུས་བཞུགས་སོ། །", + "text_id": "988b110e-d29f-4516-86e3-885c46dfa465", + "image_url": null, + "first_segment": { + "id": "d6aa5287-e4e1-4003-ae7c-2fd83bb52514", + "content": "ཨྱོན་སྐད་དུ༔ བུདྡྷ་དྷརྨ་སཾ་གྷ་ཡ༔ བོད་སྐད་དུ༔\nསངས་རྒྱས་ཆོས་དང་དགེ་འདུན་ལ༔\nརྟག་ཏུ་གུས་པས་ཕྱག་འཚལ་ལོ༔" + } + }, + { + "title": "རྗེ་གྲུབ་ཐོབ་ཆེན་པོས་ལྷ་ལྡན་ཇོ་བོ་རིན་པོ་ཆེའི་དྲུང་དུ་གསོལ་འདེབས་གནང་བའི་རྡོ་རྗེའི་གསུང་བྱིན་རླབས་བདུད་རྩིའི་སྤྲིན་ཕུང་འཕྲོ་བ་བཞུགས་སོ། །", + "text_id": "e3174f4b-1281-4c96-af8e-221262b8e470", + "image_url": null, + "first_segment": { + "id": "4adc6311-59fa-490f-bfc0-a9193f251838", + "content": "མཐའ་ཡས་འགྲོ་བ་ངེས་པར་སྒྲོལ་སླད་དུ། །\nབླ་མེད་བྱང་ཆུབ་མཆོག་ཏུ་ཐུགས་བསྐྱེད་ནས། །\nཚོགས་གཉིས་རྫོགས་མཛད་རྒྱལ་བ་ཐུགས་རྗེ་ཅན། །" + } + }, + { + "title": "༄༅། །དཔལ་ས་སྐྱ་པའི་བསྟན་པ་དར་ཞིང་རྒྱས་པའི་སྨོན་ལམ་འཇམ་དབྱངས་བླ་མ་དགྱེས་པའི་ཞལ་ལུང་ཞེས་བྱ་བ་བཞུགས་སོ། །", + "text_id": "517ff30a-c6e6-48f8-9204-1adaafc58659", + "image_url": null, + "first_segment": { + "id": "ab0e2a41-769e-4e2b-a48d-86b98d369e82", + "content": "ན་མོ་གུ་རུ་མཉྫུ་གྷོ་ཥཱ་ཡ། །\nཕྱོགས་དུས་རྒྱལ་བ་ཀུན་གྱི་སྐུ་ཡེ་ཤེས། །\nགཅིག་བསྡུས་བླ་མ་མགོན་པོ་འཇམ་པའི་དབྱངས། །" + } + }, + { + "title": "༄༅། །རྗེ་བཙུན་བློ་བཟང་ཆོས་ཀྱི་རྒྱལ་མཚན་གྱིས་མཛད་པའི་གཏོར་མ་བརྒྱ་རྩ་བཞུགས་སོ།།", + "text_id": "0ef89afc-2c72-4dd4-a1e7-de0499a28ae3", + "image_url": null, + "first_segment": { + "id": "d771aa81-63a7-408c-8907-f599c2625d98", + "content": "༄༅། །ན་མོ་གུ་རུ་ལོ་ཀེ་ཤྭ་རཱ་ཡ། གཏོར་མ་བརྒྱ་རྩ་གཏོང་བར་འདོད་པས། སྐྱབས་འགྲོ་སེམས་བསྐྱེད་ཚད་མེད་བཞི་བསྒོམས་པ་རྣམས་སྔོན་དུ་བཏང་། སྔོན་དུ་བཏང་ནས། ཁྱད་པར་མ་སེམས་ཅད་ཀྱི་དོན་དུ་མྱུར་བ་མྱུར་བར་ཡང་དག་པར་རྫོགས་པའི་སངས་རྒྱས་ཀྱི་གོ་འཕང་རིན་པོ་ཆེ་ཅི་ནས་ཀྱང་ཐོབ་པར་བྱ། དེའི་ཕྱིར་དུ་མཆོད་སྤྲིན་གྱི་གཏོར་མའི་རིམ་པ་ལ་འཇུག་པར་བགྱིའོ། (ཞེས་ལན་མང་དུ་བརྗོད།)\nསྭ་བྷཱ་ཝས་སྟོང་པར་སྦྱངས། སྟོང་པའི་ངང་ལས་པད་དཀར་ཟླ་བའི་དཀྱིལ་འཁོར་གྱི་སྟེང་དུ་རང་གི་སེམས་ཉིད་ཧྲཱི་དཀར་པོ་དེ་ལས་འོད་འཕྲོས་དོན་གཉིས་བྱས་འདུས་ཡོངས་སུ་གྱུར་པ་ལས། རང་ཉིད་འཕགས་པ་སྤྱན་རས་གཟིགས་སྐུ་མདོག་དཀར་པོ་ཞལ་གཅིག་ཕྱག་བཞི་པ། དང་པོ་གཉིས་ཐུགས་ཀར་ཐལ་མོ་སྦྱར་བ། གཡས་འོག་མས་ཤེལ་འཕྲེང་དང༌།གཡོན་འོག་མས་པདྨ་འཛིན་པ། མཚན་དཔེའི་འོད་འབར་ཞིང༌། རིན་པོ་ཆེའི་རྒྱན་དང་དར་གྱི་ན་བཟས་བཀླུབས་པ། རྡོ་རྗེའི་སྐྱིལ་ཀྲུང་གིས་བཞུགས་པའི་སྤྱི་བོར་ༀ།མགྲིན་པར་ཨཱཿ ཐུགས་ཀར་ཧྲཱིཿ ཧཱུཾ་གིས་མཚན་པ། དེ་ལས་འོད་ཟེར་འཕྲོས་པོ་ཏ་ལ་ནས་ཡེ་ཤེས་པ་དང་དབང་གི་ལྷ་རྣམས་སྤྱན་དྲངས།\nཛཿཧཱུཾ་བཾ་ཧོཿ རང་དང་གཉིས་སུ་མེད་པར་གྱུར། དབང་གིས་ལྷས་དབང་བསྐུར། འོད་དཔག་མེད་ཀྱིས་དབུ་བརྒྱན་པར་གྱུར། བདག་བསྐྱེད་ཀྱི་ཉེར་སྤྱོད་དངོས་སུ་བཤམས་པ་མེད་ན་གཏོར་སྣོད་ཟན་རིལ་བཅུ་ཆུ་གཙང་དང་བཅས་པས་བཀང་ལ། ཨམྲྀ་ཏས་བསངས། སྭ་བྷཱ་ཝས་སྦྱངས། སྟོང་པའི་ངང་ལས་རང་གི་མདུན་དུ་བྷཱུཾ་ལས་རིན་པོ་ཆེའི་སྣོད་ཡངས་ཤིང་རྒྱ་ཆེ་བའི་ནང་དུ། ༀ་ལས་མཆོད་རྫས་སམ་གཏོར་མ་དྭངས་ཤིང་ཐོགས་པ་མེད་པ་ཟག་པ་མེད་པའི་བདུད་རྩིའི་རྒྱ་མཚོ་ཆེན་པོར་གྱུར། ༀ་ཨཱཿཧཱུཾ། ལན་གསུམ་གྱིས་བྱིན་གྱིས་བརླབ། ཨཱརྱ་ལོ་ཀེ་ཤྭ་ར་ཨརྒྷཾ། པཱདྱཾ། པུཥྤེ། དྷཱུ་པཱེ། ཨཱ་ལོ་ཀེ། གནྡྷེ། ནཻཝིདྱེ། ཤཔྟ་པྲ་ཏཱིཙྪ་སྭཱ་ཧཱ།" + } + }, + { + "title": "༄༅། །ཇ་མཆོད་གཙུག་གི་ནོར་བུ་བཞུགས་སོ།།", + "text_id": "feb5540b-713d-4715-ab7c-ac5ee09b4e24", + "image_url": null, + "first_segment": { + "id": "29d19939-f7a2-4d60-933f-164cf5cff414", + "content": "རྣམ་དག་གཙུག་གི་ནོར་བུ་ལྷག་པའི་ལྷ། །\nའཇིག་རྟེན་དབང་ཕྱུག་མཆོག་གི་ཞིང་ཁམས་འདིར།།\nབཟའ་བཏུང་བདུད་རྩིའི་བཏུང་བ་ཀུན་གྱི་མཆོག །" + } + }, + { + "title": "༄༅། །བཅོམ་ལྡན་འདས་མགོན་པོ་ཚེ་དཔག་མེད་ཀྱི་བསྟོད་པ་འཆི་བ་མེད་པའི་རྒྱལ་པོ་ཞེས་བྱ་བ་བཞུགས་སོ།", + "text_id": "bed75151-000b-49c8-b54b-4b843de354a2", + "image_url": null, + "first_segment": { + "id": "ac404614-7f83-4a87-932c-34e7405d7767", + "content": "༄༅། །ན་མོ་གུ་རུ་ཨ་མི་དེ་ཝཱ་ཡ། །\nདཔལ་ལྡན་མཆོག་ཏུ་དགེ་བའི་ཞིང་གི་ཆར། །\nབདེ་ཆེན་མགོན་ཞེས་རྒྱལ་བ་ཀུན་གྱིས་བསྒྲགས། །" + } + }, + { + "title": "༄༅། །མཁས་གྲུབ་ཁྱུང་པོ་རྣལ་འབྱོར་ལ་བསྔགས་པ་ཨུ་དུམྦ་རའི་འཛུམ་ཟེར་ཅེས་བྱ་བ་བཞུགས་སོ། །", + "text_id": "1e9140b5-f137-443a-9940-957cd6881983", + "image_url": null, + "first_segment": { + "id": "256ab475-8500-4404-a056-b4abe05a5f2c", + "content": "ཉན་ཐོས་རྫུ་འཕྲུལ་ཆེན་པོར་ལུང་བསྟན་ཅིང་། །\nཤེས་རབ་ཌཱ་ཀི་རྣམ་གཉིས་ཐུགས་ཀྱི་སྲས། །\nམཁས་གྲུབ་བརྒྱ་དང་ལྔ་བཅུའི་གདུང་འཚོབ་པ། །" + } + }, + { + "title": "༄༅། །གཏོར་མ་ཆ་གསུམ་གྱི་ཆོག་ག་བཞུགས་སོ།", + "text_id": "088a6628-04cc-4538-b412-d752ba6a880e", + "image_url": null, + "first_segment": { + "id": "2be8c179-7b89-45f3-be64-8063a31cb9e2", + "content": "བླ་མ་དང་དཔལ་རྡོ་རྗེ་སེམས་དཔའ་ལ་ཕྱག་འཚལ་ལོ། །\n(རྡོ་རྗེ་ཐེག་པའི་དཀྱིལ་འཁོར་ཆེན་པོའི་ཆོ་གའི་སྐབས་སུ་ཆ་གསུམ་གཏོར་མ་རྒྱས་འབྲིང་བསྡུས་པའི་ཚུལ་ཇི་སྙེད་ཅིག་གསུངས་པ་ལས་འདི་ལྟར་ཤེས་པར་བྱ་སྟེ། རིན་པོ་ཆེ་ལ་སོགས་པའི་སྣོད་གསུམ་གྱི་དང་པོ་གཉིས་སུ། ཕྱོགས་སྐྱོང་དང་འབྱུང་པོ་སྤྱིའི་གཏོར་ཟླུམ་མཐེབ་ཀྱུ་ཅན་འབྲས་ཆན་ལ་ཕུག་སོགས་དཀར་ཞིང་གཙང་བའི་བཟའ་བཅའ་སྣ་ཚོགས་ཀྱིས་བརྒྱན་པ་དང་། གསུམ་པར། བགེགས་ཀྱི་གཏོར་མ་ཏིང་ལོ་ཆངས་བུ་ཅན་ཤ་ཆང་སོགས་ཀྱིས་སྦགས་པ་རྣམས་གྲལ་རིམ་གྱིས་བཤམས་ལ། བསང་ཆུས་སྦྲན་ཏེ། རྡོ་རྗེ་གནོད་སྦྱིན་གྱི་ཕྱག་རྒྱས།)\nཨོཾ་བཛྲ་ཡཀྴ་ཧཱུྃ། (གིས་བསང་། རྡོ་རྗེ་མེའི་ཕྱག་རྒྱས།) ཨོཾ་བཛྲ་ཛྭ་ལ་ཨ་ན་ལ་ཧ་ན་ད་ཧ་པ་ཙ་མ་ཐ་བྷཉྫ་ར་ཎ་ཧཱུྃ་ཕཊཿ ཞེས་དང་། སྭ་བྷཱ་ཝས་སྦྱང༌། སྟོང་པའི་ངང་ལས་བྷྲཱུྃ་ལས་རིན་པོ་ཆེའི་སྣོད་ཟབ་ཅིང་རྒྱ་ཆེ་བའི་ནང་དུ་གཏོར་མ་ཁ་དོག་དྲི་རོ་ནུས་པ་ཕུན་སུམ་ཚོགས་པ་བདུད་རྩིའི་རྒྱ་མཚོ་ཆེན་པོར་གྱུར་པར་བསམ། ཨོཾ་ཨཱཿཧཱུྃ་ཧོ། (ལན་གསུམ་གྱིས་ཐུན་མོང་དུ་བྱིན་གྱིས་བརླབ་བོ། །དེ་ནས་གཏོར་མ་དང་པོ་ཕྱོགས་སྐྱོང་བཅུ་ལ་འབུལ་ཏེ།)" + } + }, + { + "title": "༄༅། །ཛམྦྷ་ལའི་ཆུ་སྦྱིན་ལ་བཞུགས། །", + "text_id": "3ed3b2fc-cb7d-4248-9bef-e1a32980a3f7", + "image_url": null, + "first_segment": { + "id": "a33a95b7-347c-434e-a3ef-173b23dc6c27", + "content": "(ཛམ་བཤོས་ལྔ་བཤམས་པ་ཆབ་ཀྱིས་བྲན་ཏེ།) ཨོཾ་བཛྲ་ཨ་མྲྀ་ཏ་ཀུནྡ་ལི་ཧ་ན་ཧ་ན་ཧཱུྃ་ཕཊཿ (ཀྱིས་བསངས།) ཨོཾ་སྭ་བྷ་ལྦ་ཤུདྡྷཿསརྦ་དྷརྨཱ་སྭ་བྷཱ་ལྦ་ཤུདྡྷོ྅ཧཾ། (ཞེས་སྦྱངས།) ཨོཾ་ཨཱཿཧཱུྃ། (ཞེས་ལན་གསུམ་གྱིས་ཛམ་གཏོར་བྱིན་གྱིས་བརླབས་པས། ཛམ་བཤོས་ཀྱི་རྒྱབ་སོ་སོར་ཛམྦྷ་ལ་རིགས་ལྔ་བསྐྱེད་པ་ནི།) བྷྲཱུྃ་ལས་རིན་པོ་ཆེའི་སྣོད་ཡངས་ཤིང་རྒྱ་ཆེ་བའི་ནང་དུ་པཾ་ལས་པདྨ་འདབ་མ་བཞི་པའི་ལྟེ་བར་ཉི་ཟླའི་སྟེང་དུ་ཧཱུྃ་ལས་མགོན་པོ་ཛམྦྷ་ལ་སྐུ་མདོག་མཐིང་ནག་ཞལ་གཅིག་ཕྱག་གཉིས་ཐོད་ཁྲག་དང་ནེའུ་ལེ་འཛིན་པ། དེའི་ཕྱི་རིམ་པདྨ་འདབ་བཞིའི་སྟེང་དུ་ཨ་ལས་ཟླ་བའི་སྟེང་དུ་ཛཾ་ཡིག་རིགས་བཞི་ལས། ཤར་དུ་འཕགས་པ་ཛམྦྷ་ལ་སྐུ་མདོག་དཀར་པོ་ཕྲེང་བ་དང་ནེའུ་ལེ་འཛིན་པ། ལྷོར་ཡོན་ཏན་ཛམྦྷ་ལ་སྐུ་མདོག་སེར་པོ་ཤིང་ཏོག་དང་ནེའུ་ལེ་འཛིན་པ། ནུབ་ཏུ་གསུང་མཆོག་ཛམྦྷ་ལ་སྐུ་མདོག་དམར་པོ་ནོར་བུ་དང་ནེའུ་ལེ་འཛིན་པ། བྱང་དུ་ཕྲིན་ལས་ཛམྦྷ་ལ་སྐུ་མདོག་ལྗང་གུ་རྒྱལ་མཚན་དང་ནེའུ་ལེ་འཛིན་པ། ཐམས་ཅད་ཀྱང་དར་དང་རིན་པོ་ཆེའི་རྒྱན་སྣ་ཚོགས་ཀྱིས་བརྒྱན་པ། དེ་རྣམས་ཀྱི་ཐུགས་ཀའི་ཛཾ་ཡིག་ལས་འོད་ཟེར་འཕྲོས་པས་ལྕང་ལོ་ཅན་ནས་ཛམྦྷ་ལ་རིགས་ལྔ་འཁོར་དང་བཅས་པ་སྤྱན་དྲངས་ཏེ་བྱོན་པར་གྱུར། ཨོཾ་ཛམྦྷ་ལ་ཛ་ལེནྡྲ་ཡ་ས་མ་ཡ་ཛཿ ཛཿཧཱུྃ་བྃ་ཧོཿ ཨོཾ་ཧཱུྃ་ཏྲཱཾ་ཧྲཱིཿཨཱཿ (ཞེས་དབང་བསྐུར་བས་དབང་གི་ཆུས་སྐུ་གང། དྲི་མ་དག ཆུའི་ལྷག་མ་ཡར་ལུད་པ་ལས་ཛམྦྷ་ལ་རིགས་ལྔ་ལ་རྒྱལ་བ་རིགས་ལྔས་དབུ་བརྒྱན་པར་གྱུར།) ཨོཾ་ཛམྦྷ་ལ་ཛ་ལེནྡྲ་ཡ་ཨཱཿཧཱུཾ་སྭཱ་ཧཱ། (ཞེས་བུམ་ཆུ་ཨ་ལས་བདུད་རྩིར་བསམས་ཏེ་གཏོར་ཆ་རེ་རེར་ཉེར་གཅིག་རེ་ཆུ་སྦྱིན་འབུལ་བ་ནི། ཐོག་མར་དབུས། དེ་ནས་གཡས། གཡོན། གཡས་མཐའ་མ། གཡོན་མཐའ་མ་རྣམས་སོ།) ཨོཾ་ཛམྦྷ་ལ་ཛ་ལེནྡྲ་ཡ་ས་པ་རི་ལྦཱ་ར་ཨི་དཾ་བ་ལིཾ་ཏ་ཁ་ཁ་ཁཱ་ཧི་ཁཱ་ཧི། (བདུན་ནམ་གསུམ་གྱིས་གཏོར་མ་འབུལ།) ཨོཾ་ཛམྦྷ་ལ་ཛ་ལེནྡྲ་ཡ་ས་པ་རི་ལྦཱ་ར་ཨརྒྷཾ་ (སོགས་ནས་) ཤཔྟ་ཨཱཿཧཱུཾ། (བར་གྱིས་མཆོད།)\nནོར་གྱི་དབང་ཕྱུག་རིན་ཆེན་གཏེར་མངའ་ཞིང། །\nགནོད་སྦྱིན་ནོར་འཆང་མང་པོ་ཀུན་གྱི་རྗེ། །" + } + }, + { + "title": "འཇམ་དཔལ་རྫོགས་པ་ཆེན་པོའི་སྨོན་ལམ།", + "text_id": "449dfb22-7893-4bed-9d37-47dc93cb59a6", + "image_url": null, + "first_segment": { + "id": "f97d683b-d2f1-4e93-819f-e43d88c742b7", + "content": "འཇམ་དཔལ་རྫོགས་པ་ཆེན་པོའི་སྨོན་ལམ།\nཕྱོགས་བཅུ་དུས་བཞིའི་བདེ་གཤེགས་སྲས་བཅས་ཀྱི། །\nཡེ་ཤེས་སྐུར་གྱུར་གཉིས་མེད་ཚུལ་འཆང་བ། །" + } + }, + { + "title": "༄༅། །ཞབས་བརྟན་ནུབ་ཕྱོགས་བདེ་ལྡན་མ་བཞུགས་སོ།།", + "text_id": "af6b3391-115e-482d-86a6-60e1e263b252", + "image_url": null, + "first_segment": { + "id": "d2a91788-d6b6-4b99-8e85-f29b36c8a4f1", + "content": "༄༅། །ཞབས་བརྟན་ནུབ་ཕྱོགས་བདེ་ལྡན་མ་བཞུགས་སོ།།\n༼གནས་ཆུང་ཆོས་སྐྱོང་ཆེན་པོའི་བཀའ་ལུང་།༽\n༄༅། །ཧྲཱིཿ ནུབ་ཕྱོགས་བདེ་ལྡན་ཞིང་གི་མགོན་པོ་དང་། །" + } + }, + { + "title": "༈ ཀྱེ་རྡོར་ལམ་དུས་མ་ནོར་དོན་གསལ།", + "text_id": "e8e1e76b-e27f-49ed-a08b-be66031146a0", + "image_url": null, + "first_segment": { + "id": "b2b389cc-4ef9-44a5-8edf-f532d3ab871d", + "content": "༈ ཀྱེ་རྡོར་ལམ་དུས་མ་ནོར་དོན་གསལ།\n༄༅། །ན་མོ་གུ་རུ་ ༔ དཔལ་ཀྱེ་རྡོ་རྗེ་གཙོ་རྐྱང་གི་མངོན་རྟོགས་ལ་ལུས་དཀྱིལ་དང་ལམ་དུས་ཀྱི་དབང་སྦྱར་བའི་ངག་འདོན་ནི། (སྐྱབས་ཡུལ་རྣམས་མངོན་སུམ་ལྟ་བུར་དམིགས་ཏེ།) བདག་དང་འགྲོ་བ་ནམ་མཁའི་མཐའ་དང་མཉམ་པའི་སེམས་ཅན་ཐམས་ཅད་དུས་འདི་ནས་བཟུང་སྟེ་ཇི་སྲིད་བྱང་ཆུབ་སྙིང་པོ་ལ་མཆིས་ཀྱི་བར་དུ། ཕྱོགས་བཅུ་དུས་གསུམ་གྱི་དེ་བཞིན་གཤེགས་པ་ཐམས་ཅད་ཀྱི་སྐུ་གསུང་ཐུགས་ཡོན་ཏན་ཕྲིན་ལས་ཐམས་ཅད་ཀྱི་ངོ་བོ། ཆོས་ཀྱི་ཕུང་པོ་སྟོང་ཕྲག་བརྒྱད་ཅུ་རྩ་བཞིའི་འབྱུང་གནས། འཕགས་པའི་དགེ་འདུན་ཐམས་ཅད་ཀྱི་མངའ་བདག །རྗེ་བཙུན་རྩ་བ་དང་བརྒྱུད་པར་བཅས་པའི་དཔལ་ལྡན་བླ་མ་དམ་པ་རྣམས་ལ་སྐྱབས་སུ་མཆིའོ། །རྫོགས་པའི་སངས་རྒྱས་བཅོམ་ལྡན་འདས་རྣམས་ལ་སྐྱབས་སུ་མཆིའོ། །དམ་པའི་ཆོས་རྣམས་ལ་སྐྱབས་སུ་མཆིའོ། །འཕགས་པའི་དགེ་འདུན་རྣམས་ལ་སྐྱབས་སུ་མཆིའོ། ། (ཞེས་ཅི་ནུས་ཀྱི་མཐར་ཐལ་མོ་སྦྱར་ཏེ།)\nབླ་མ་དང་དཀོན་མཆོག་རིན་པོ་ཆེ་རྣམ་པ་གསུམ་ལ་བདག་ཕྱག་འཚལ་ཞིང་སྐྱབས་སུ་མཆིའོ། །ཁྱེད་རྣམས་ཀྱིས་བདག་གི་རྒྱུད་བྱིན་གྱིས་བརླབ་ཏུ་གསོལ། (སྐྱབས་ཡུལ་རྣམས་འོད་དུ་ཞུ་ནས་རང་ལ་ཐིམ་པར་བསམ།) སེམས་ཅན་ཐམས་ཅད་ཀྱི་དོན་དུ་རྫོགས་པའི་སངས་རྒྱས་ཀྱི་གོ་འཕང་ཐོབ་པར་བྱ། དེའི་ཆེད་དུ་བདག་གིས་ལམ་ཟབ་མོ་ཉམས་སུ་བླང་བར་བགྱིའོ། ། (ལན་གསུམ།)" + } + }, + { + "title": "༄༅། །ཀརྨ་པའི་གསོལ་འདེབས་རྒྱལ་ཀུན་ཕྲིན་ལས་གཟུགས་ཅན་མ་བཞུགས་སོ། །", + "text_id": "f938d979-fc01-40a1-b1a7-cbda15b29272", + "image_url": null, + "first_segment": { + "id": "f9c2975c-4ecb-4048-812d-d15dab5d92f0", + "content": "རྒྱལ་བ་ཀུན་གྱི་ཕྲིན་ལས་གཟུགས་ཅན་ལ། །\nཀརྨ་པ་ཞེས་གྲགས་པའི་ཇོ་བོ་རྗེས། །\nཞིང་ཁམས་རྒྱ་མཚོའི་མཐའ་གཏིང་ཡོངས་ཁྱབ་ལ། །" + } + }, + { + "title": "༄༅། །ཀླུ་བསངས་ནོར་བུ་དགོས་འདོད་ཀུན་འབྱུང་ཞེས་བྱ་བ་བཞུགས་སོ།།", + "text_id": "cbc540ec-5112-4196-b917-9cc535dbe252", + "image_url": null, + "first_segment": { + "id": "21cd3a6f-15f3-4129-bc5b-18141061a474", + "content": "ཀྱེ། རྒྱ་མཚོ་ཆེ་དང་རོལ་མཚོ་བདུན། །\nཀླུ་མཚོ་ཆུ་མིག་དམ་པ་ནས། །\nཀླུ་རྒྱལ་གཙུག་ན་རིན་ཆེན་ནི། །" + } + }, + { + "title": "༄༅། ། རྒྱལ་བའི་དབང་པོ་ཀློང་ཆེན་རབ་འབྱམས་ལ་བསྟོད་པ།", + "text_id": "a61ba1f4-13fc-4356-bd05-7d53b03a52b4", + "image_url": null, + "first_segment": { + "id": "9b0f51ac-5bbe-4224-8e85-a6654fca1a80", + "content": "དཔལ་ངག་གི་དབང་པོ་ལ་ཕྱག་འཚལ་ལོ། །\nརྟ་མཆོག་གྲུབ་པའི་རིགས་ཀྱི་ཐིག་ལེར་པདྨའི་ཞལ་རས་བཞད་པ་དང་། །\nལྷན་ཅིག་གྲགས་པའི་རྟ་སྐད་དབྱངས་སྙན་འོག་མིན་བ་གམ་རྩེར་སོན་པས། །" + } + }, + { + "title": "འཆི་ཁར་ཕན་པའི་གདམས་ངག་ཉམས་ལེན་གྱི་སྙིང་པོ་ཞེས་བྱ་བ་བཞུགས་སོ།།", + "text_id": "1cc7dd3e-eabc-400f-a56a-5c3baf2189bb", + "image_url": null, + "first_segment": { + "id": "772aec12-5f26-4fd3-954c-14f11816c1dd", + "content": "བདེ་བ་ཅན་གྱི་མགོན་པོ་འོད་དཔག་མེད།།\nམཐོན་མཐིང་བརྩེ་བའི་སྤྱན་གྱིས་བདག་ལ་གཟིགས།།\nདགྱེས་འཛུམ་ཟླ་བའི་ཞལ་རས་བདག་ལ་སྟོན།།" + } + }, + { + "title": "༄༅། །སྨོན་ལམ་རྡོ་རྗེའི་རྒྱ་མདུད་ནི། །", + "text_id": "da83f0ea-4dd2-498b-bd02-0e18968bb92c", + "image_url": null, + "first_segment": { + "id": "a36aed2a-f494-42c7-8737-87285c4d78f9", + "content": "བླ་མ་ཡི་དམ་ལྷ་ཚོགས་དགོངས་སུ་གསོལ། །\nདེང་འདིར་བརྩོན་པས་བསྒྲུབས་པའི་དགེ་བ་དང༌། །\nདུས་གསུམ་བསགས་དང་ཡོད་པའི་དགེ་བ་རྣམས། །" + } + }, + { + "title": "༄༅། །གནས་དང་མཆོད་པའི་ཡོ་བྱད་བྱིན་རླབས།", + "text_id": "8efa6504-bf2f-4a72-a539-41a9d89976be", + "image_url": null, + "first_segment": { + "id": "dad15156-b267-4cd4-a0b5-bef5b193e105", + "content": "དེ་ནས་གནས་དང་མཆོད་པའི་ཡོ་བྱད་རྣམས་བསང་སྦྱངས། སྟོང་པའི་ངང་ལས་གནས་ཁང་བྷྲཱུྃ་ལས་རིན་པོ་ཆེ་སྣ་ཚོགས་ལས་གྲུབ་པའི་ཐར་\nཔ་ཆེན་པོའི་གཞལ་ཡས་ཁང་གྲུ་བཞི་པ་སྒོ་བཞི་པ་རྟ་བབས་བཞིས་མཛེས་ཤིང་རྒྱན་ཐམས་ཅད་ཀྱིས་བརྒྱན་པ། མཚན་ཉིད་ཐམས་ཅད་ཡོངས་སུ་རྫོགས་པའི་ནང་དུ་མཆོད་རྫས་རྣམས་ཨ་ལས་བྱུང་བའི་ཡེ་ཤེས་ཀྱི་ཀ་པཱ་ལ་ཡངས་ཤིང་རྒྱ་ཆེ་བ་རྣམས་ཀྱི་ནང་དུ་ཧཱུྃ་ཞུ་བ་ལས་བྱུང་བའི་ལྷ་རྫས་ལས་གྲུབ་པའི། ཞབས་བསིལ། ཞལ་བསིལ། འཐོར་འཐུང་། མཆོད་ཡོན། མེ་ཏོག །བདུག་སྤོས། མར་མེ། དྲི་མཆོག །ཞལ་ཟས། རོལ་མོ་རྣམས་དྭངས་ཤིང་ཐོགས་པ་མེད་པ་རྒྱ་འཇིག་རྟེན་དང་འཇིག་རྟེན་ལས་འདས་པའི་དགེ་བ་ལས་གྲུབ་པའི་ངོ་བོ་བཟང་ཞིང་གཙང་བ་ཡུལ་ནམ་མཁའི་མཐའ་ཁྱབ་པ་དུས་འཁོར་བ་ཇི་སྲིད་མ་སྟོངས་ཀྱི་བར་དུ་རྒྱུན་མི་འཆད་པར་གྱུར(་ཞེས་དང་སོ་སོའི་སྔགས་རྒྱ་བྱས།)\nཨོཾ་ནི་རི་ཧཱུྃ་ཁཾ་སྭཱ་ཧཱ། ཨོཾ་བཛྲ་ནི་ཝི་དེ་ཧཱུྃ་ཁཾ་སྭཱ་ཧཱ། ཨོཾ་སརྦ་སཾ་ཤོ་དྷ་ནི་སྭཱ་ཧཱ། ཨོཾ་ཛ་ཧཱུྃ་བཾ་ཧོཿ ཁཾ་རཾ་རྒུན་ཡུཾ་སྭཱ་ཧཱ། ཨོཾ་བཛྲ་པུཥྤེ་ཨཱཿཧཱུྃ། (དེ་བཞིན་དུ་ཐོག་མཐའ་གཉིས་ཐོག་མ་རྣམས་ལ་འང་སྦྱར་ཏེ།) དྷུ་པེ། ཨཱ་ལོ་ཀེ །གནྡྷེ། ནཻ་ཝི་དྱེ། ཤབྡ(འི་བར་དང། མཐར་རྡོ་རྗེ་སྙིང་གར་བཟུང་དྲིལ་བུ་དཀྲོལ་ཡང་རོལ་མོའི་སྒྲ་དང་བཅོས་བྱ་སྟེ།) ཨོཾ་བཛྲ་གྷཎྜེ་ར་ཎི་ཏ། པྲ་ར་ཎི་ཏ། སཾ་པྲ་ར་ཎི་ཏ། སརྦ་བུདྡྷ་ཀྵེ་ཏྲ། པྲ་ཙ་ལི་ཏེ། པྲཛྙཱ་པཱ་ར་མི་ཏ། ནཱ་ད་སཾ་བྷ་ཝེ། བཛྲ་དྷརྨ་ཧྲྀ་ད་ཡ། སནྟོ་ཥ་ནི་ཧཱུྃ་ཧཱུྃ་ཧཱུྃ་ཧོ་ཧོ་ཨ་བཾ་སྭཱ་ཧཱ། (ཞེས་ལན་གསུམ་བརྗོད་དེ་གནས་དང་མཆོད་པའི་ཡོ་བྱད་རྣམས་བསང་རྒྱས་དང་བྱང་སེམས་ཐམས་ཅད་ཀྱིས་ཀྱང་བྱིན་གྱིས་བརླབས་པར་བསམ་མོ)། །" + } + }, + { + "title": "༄༅། །གནས་བཅུའི་བསྟོད་པ་བཞུགས་སོ། །", + "text_id": "cf4c360e-dce0-41e6-ab8a-4366f98e7d80", + "image_url": null, + "first_segment": { + "id": "0561c1f1-4194-4d38-b4e7-1bd736ad54ae", + "content": "ཕྱོགས་དུས་ཀུན་གནས་སྐྱབས་གསུམ་བཀའ་སྡོད་བཅས། །\nཉོན་མོངས་འགྲོ་ལ་མཁྱེན་བརྩེས་རབ་དགོངས་ནས། །\nདཔག་མེད་ཞིང་ནས་ཐོགས་མེད་རྫུ་འཕྲུལ་གྱིས། །" + } + }, + { + "title": "༄༅། །རྡོ་རྗེ་སེམས་དཔའ་གསང་བ་དྲི་མེད་ལས་བཤགས་པའི་སྙིང་པོ་སྦྱངས་པའི་རྒྱལ་པོ་བསྟན་པ་བཞུགས་སོ། །", + "text_id": "4e3311e9-02d3-4223-8f4f-f6aede02a2ab", + "image_url": null, + "first_segment": { + "id": "8732e8be-1567-494b-b7f9-f0a2a2b1c1f7", + "content": "ཨོཾ་གཞི་ཐོག་དང་པོའི་སངས་རྒྱས་ཀུན་ཏུ་བཟང༌། །\nརྡོ་རྗེ་སེམས་དཔའ་རྡོ་རྗེ་དེ་བཞིན་གཤེགས། །\nདཔའ་བོ་ཆེན་པོ་འགྲོ་བ་སྐྱོབ་པའི་མགོན། །" + } + }, + { + "title": "སྟོབས་ལྡན་འོད་འབར་རྒྱལ་མཚན་གསོལ་མཆོད་དགེ་ལེགས་རྒྱ་མཚོའི་སྤྲིན་ཕུང་།", + "text_id": "a660abe7-11ff-49f0-9652-cae5c1d21e72", + "image_url": null, + "first_segment": { + "id": "b79528f3-ff25-409f-a1cd-33d68452cf89", + "content": "ཧཱུཾ་ལྷུན་གྲུབ་བདེ་ཆེན་འབར་བའི་གཞལ་ཡས་ནས། །\nརྡོ་རྗེ་འཆང་དང་རྒྱལ་བ་གཉིས་པའི་ཞབས། །\nརྡོ་རྗེ་འཇིགས་བྱེད་ཧེ་རུ་ཀ་དཔལ་སོགས། །" + } + }, + { + "title": "༄༅། །གཤིན་བསྔོ་ནི།", + "text_id": "05927066-2612-4ed2-a8c6-801f22207111", + "image_url": null, + "first_segment": { + "id": "d6cfef8a-a731-4a7e-afe8-e5e952485ba0", + "content": "(ཉེ་དྲུང་མཆེད་གྲོགས་ལ་སོགས་གང་འཐད་པ་ཞིག་གིས་རྗེས་ཟློས་བྱེད་དུ་བཅུག་ལ༔)\nབླ་མ་ཡི་དམ་མཁའ་འགྲོ་ལ་སོགས་ཕྱོགས་བཅུ་ན་བཞུགས་པའི་སངས་རྒྱས་དང་བྱང་ཆུབ་སེམས་དཔའ་ཐམས་ཅད། ། ཚེ་འདས་ཆེ་གེ་མོ་ལ་དགོངས་སུ་གསོལ༔ ཚེ་འདས་ཆེ་གེ་མོ་ཞེས་བྱ་བ་སྐྱེ་བ་འདི་དང་སྐྱེ་བ་གཞན་དག་ཏུ༔ དགེ་བའི་རྩ་བ་སྦྱིན་པ་ལས་བྱུང་བ་དང༔ ཚུལ་ཁྲིམས་ལས་བྱུང་བ་དང༔ སྒོམ་པ་ལས་བྱུང་བ་ལ་སོགས་བསོད་ནམས་དང་ཡེ་ཤེས་ཀྱི་ཚོགས་སུ་གྱུར་པ་བགྱིས་པ་དང༔ བགྱིད་དུ་བསྩལ་བ་དང༔ གཞན་གྱིས་བགྱིས་པ་ལ་རྗེས་སུ་ཡི་རང་བ་ལ་སོགས་པ་རྣམས་དང༔ གཞན་ཡང་ཉེ་དུ་འབྲེལ་པ་མཆེད་གྲོགས་ཤུལ་ན་མཆིས་པ་རྣམས་ཀྱིས༔ ཚེ་འདས་ཆེ་གེ་མོའི་དོན་དུ༔ བླ་མ་ལ་བསྙེན་བཀུར་བགྱིས་པ་དང༔ དཀོན་མཆོག་གསུམ་ལ་མཆོད་པ་ཕུལ་པ་དང༔ ཡི་དམ་གྱི་སྒོམ་བཟླས་བྱས་པ་དང༔ མགྲོན་རྣམ་བཞི་ལ་གཏོར་མ་བཏང་བ་དང༔ སཱཙྪ་བཏབ་པ་དང༔ ཡུལ་གོང་མ་ལ་མཆོད་པ་ཕུལ་པ་དང༔ འོག་མ་ལ་སྦྱིན་གཏང་བགྱིས་པ་དང༔ བདེ་བར་གཤེགས་པའི་གསུང་རབ་བཀླགས་པ་དང༔ སྒྲིབ་སྦྱོང་གི་ཆོ་ག་བགྱིས་པ་དང༔ མ་འོངས་པའི་དུས་སུ་ཡང་བགྱིད་པར་འགྱུར་བ་ལ་སོགས་པའི་དགེ་བའི་རྩ་བ་འདི་དག་རྣམས་ལ་བརྟེན་ནས༔ ཚེ་འདས་ཆེ་གེ་མོ་ཞེས་བགྱི་བ༔ མྱུར་དུ་བླ་ན་མེད་པ་ཡང་དག་པར་རྫོགས་པའི་སངས་རྒྱས་ཀྱི་གོ་འཕང་རིན་པོ་ཆེ་ཐོབ་པར་གྱུར་ཅིག༔\nདེ་ལྟར་མ་གྱུར་གྱི་བར་དུ་ཡང༔ ཚེ་རབས་ནས་ཚེ་རབས་ཐམས་ཅད་དུ་མཐོ་རིས་ལྷ་དང་མིའི་གོ་འཕང་ཐོབ་པར་གྱུར་ཅིག༔ དེར་ཡང་རིགས་མཆོག་ཏུ་གྱུར་པ་ལ་སོགས་པ་མཐོ་རིས་ཀྱི་ཡོན་ཏན་བདུན་དང་ལྡན་པར་གྱུར་ཅིག༔ བླ་མ་དམ་པ་ཡོངས་སུ་འཛིན་པ་དགེ་བའི་བཤེས་གཉེན་རྣམས་ཀྱིས་རྗེས་སུ་འཛིན་པར་གྱུར་ཅིག༔ ཐེག་མཆོག་བླ་ན་མེད་པའི་ཆོས་ལ་ལོངས་སྤྱོད་པར་གྱུར་ཅིག༔ རང་གཞན་ཐམས་ཅད་ལ་ཕན་ཐོགས་པར་གྱུར་ཅིག༔ ལེ་ལོ་དང་གཡེང་བ་ཐམས་ཅད་རབ་ཏུ་སྤངས་ཏེ་བསྒྲུབ་པ་ཚུལ་བཞིན་དུ་བྱེད་པར་གྱུར་ཅིག༔ ཏིང་ངེ་འཛིན་གསལ་ཞིང་རྟོགས་པ་ཁྱད་པར་ཅན་རྒྱུད་ལ་སྐྱེ་བར་གྱུར་ཅིག༔ ལམ་གྱི་བར་ཆད་དང་འགལ་རྐྱེན་མེད་ཅིང་མཐུན་རྐྱེན་ཕུན་སུམ་ཚོགས་པར་གྱུར་ཅིག༔ མཐར་ཐུག་བླ་ན་མེད་པ་ཡང་དག་པར་རྫོགས་པའི་བྱང་ཆུབ་རིན་པོ་ཆེ་མྱུར་དུ་ཐོབ་པར་གྱུར་ཅིག༔ དེ་ལྟར་གྱུར་ནས་ཀྱང་ལྷུན་གྱིས་གྲུབ་པའི་ཕྲིན་ལས་རྒྱ་མཚོ་ལྟ་བུ་ལ་མངའ་བརྙེས་ཏེ། འགྲོ་བ་ཐ་དག་གི་དོན་ཕུན་སུམ་ཚོགས་པ་འབྱུང་བར་གྱུར་ཅིག༔" + } + }, + { + "title": "༄༅། །རྗེ་བཙུན་སྒྲོལ་མའི་ཐུགས་དམ་གནད་ནས་བསྐུལ་བའི་གསོལ་འདེབས་བཞུགས་སོ།། །", + "text_id": "cfcb7b69-7164-4d1c-b348-af74eaf93f0d", + "image_url": null, + "first_segment": { + "id": "6550f411-8f7c-42e7-a0ee-2a78c76bdc09", + "content": "༈ ན་མོ་གུ་རུ་བྷྱ༔ རྗེ་བཙུན་སྒྲོལ་མ་ལ་ཐུགས་དམ་གནད་ནས་བསྐུལ་ཞིང་སྨྲེ་ངག་གིས་གསོལ་བ་བཏབ་པ་ནི༔ རྗེ་བཙུན་འཕགས་མའི་རྟེན་ གྱི་དྲུང་དུ་ཐལ་སྦྱར་པུས་བཙུགས་ལུས་བ་སྤུ་གཡོ་བས༔ གདོང་མཆི་མ་འབྱིན་ཅིང་བུ་མ་ལ་དུང་ནས་ངུ་བའི་ཚུལ་དུ་འདི་ལྟར་བྱའོ༔\nཀྱེ་ཧུད་འཕགས་མ་བྱམས་ལྡན་ཐུགས་རྗེ་ཅན༔\nརྒྱལ་བའི་ཡུམ་ཁྱོད་གནས་མཆོག་གང་ན་བཞུགས༔" + } + }, + { + "title": "ལྷ་ཆེན་རབ་རྒོད་དགེ་བསྙེན་མཉེས་པའི་མཆོད་སྤྲིན།", + "text_id": "7fc704ee-8c46-4292-8c2f-8ed5f3a17cd9", + "image_url": null, + "first_segment": { + "id": "822b8d9e-ad6e-46a3-be48-f74b61171402", + "content": "ན་མཿཤྲཱི་བཛྲ་བྷཻ་ར་ཝཱ་ཡ།\nརྩ་བརྒྱུད་བླ་མ་ཡི་དམ་ལྷ་ཚོགས་དང༌། །\nསངས་རྒྱས་བྱང་སེམས་མཆོག་གསུམ་སྲུང་མའི་མཐུས། །" + } + }, + { + "title": "༄༅། །བདེ་ཆེན་ལྷུན་གྲུབ་མ་བཞུགས་སོ།། ༈", + "text_id": "d34812a9-7494-4a18-9991-fabbd6260fad", + "image_url": null, + "first_segment": { + "id": "825cb6b5-3850-4f81-ab4f-6b2a8bd72bcc", + "content": "བདེ་ཆེན་ལྷུན་གྲུབ་ཆོས་སྐུའི་ནམ་མཁའ་ལས། །\nབྱམས་བརྩེའི་སྤྲིན་གྱིས་འཇིག་རྟེན་ཁྱབ་མཛད་ཅིང་། །\nརྒྱུན་མི་ཆད་དུ་མཛད་པའི་ཆར་འབེབས་པ། །" + } + }, + { + "title": "ཇ་མཆོད་བདེ་ཆེན་ཀུན་བཟང་མ་བཞུགས་སོ།", + "text_id": "ffea33fe-8e10-4346-8cfd-da17f439966a", + "image_url": null, + "first_segment": { + "id": "93bfafa3-f344-41f8-97ad-aab9d3002391", + "content": "༄༅། ། རང་ལྷར་གསལ་བདེ་ཆེན་འཁོར་ལོའི་སྟེང། །\nལྷ་ཞི་ཁྲོ་རབ་འབྱམས་འདུས་པའི་དབུས། །\nགདན་སེང་ཁྲི་པད་ཟླ་ཉི་དཀྱིལ་ཁར། །" + } + }, + { + "title": "གསང་འདུས་སྨོན་ལམ་བཞུགས་སོ། །", + "text_id": "f94c71dc-1244-4978-a1a9-04b76f7d06a9", + "image_url": null, + "first_segment": { + "id": "f42060dc-262e-4955-acca-c1b8b58eda02", + "content": "༄༅། །དགེ་བ་འདི་ཡིས་མྱུར་དུ་བདག །\nསངས་རྒྱས་ཀུན་གྱི་ཀུན་བདག་ཉིད། །\nརྡོ་རྗེ་འཆང་དབང་ཐོབ་གྱུར་ཅིག །" + } + }, + { + "title": "མཁའ་འགྲོ་སྤྱི་གཏོར།", + "text_id": "0c89f6ae-b299-43c5-af3f-d6521bdb3cb0", + "image_url": null, + "first_segment": { + "id": "3a37ef8c-ebec-4fba-bb06-68368f262a5b", + "content": "ཧ་ཧོ་ཧྲཱི།ལན་གསུམ།ཕཻཾ། རང་གི་ཐུགས་ཀར་ཉི་གདན་ལ་གནས་པའི་ཧཱུཾ་ཡིག་ལས་འཕྲོས་པའི་འོད་ཟེར་གྱིས་དུར་ཁྲོད་བརྒྱད་ན་གནས་པའི་ཕྱོགས་སྐྱོང་དང་ཞིང་སྐྱོང་ལ་སོགས་པ་ཐམས་ཅད་སྤྱན་དྲངས་ཏེ།ཕྱོགས་མཚམས་བརྒྱད་དུ་འཁོད་པ་སྐད་ཅིག་གིས་འོད་གསལ་དུ་བཅུག་པ་ལས།བདེ་མཆོག་གི་ལྷ་ཡབ་ཡུམ་གྱི་སྐུར་བཞེངས་པའི་མགྲོན་རྣམས་ཀྱི་ལྗགས་ལ་ཧཱུཾ་དཀར་པོ་ལས་བྱུང་བའི་རྡོ་རྗེ་རྩེ་གསུམ་པ་དཀར་བོ་ནས་འབྲུ་ཙམ་འཁོད་པའི་རྡོ་རྗེའི་འོད་ཟེར་གྱི་སྦུ་གུས་དྲངས་ནས་གསོལ་བར་གྱུར།\nཨོཾ་ཁ་ཁ་ཁཱ་ཧི་ཁཱ་ཧི།སརྦ་ཡཀྵ།རཀྵ་ས།བྷུ་ཏ། པྲེ་ཏ། བི་ཤཱ་ཙ།ཨུནྨ་ད། ཨ་པསྨར། བཛྲ་ཌཱཀཌཱ་ཀི་ནྱཱ་ད་ཡ། ཨི་མཾ་བ་ལིཾ་གྲྀཧྣཾ་ཏུ།ས་མ་ཡ་རཀྵནྟུ། མཱ་མ་སརྦ་སིདྡྷི་མྨེཔྲ་ཡ་ཙྪནྟུ། ཡ་ཐེ་པཾཡ་ཐེ་ཥྚཾ།བྷུ་ཛ་ཐ།པི་པ་ཐ།ཛི་གྷྲ་ཐ།མཱ་ཏི་ཀྲ་མ་ཐ།མཱ་མ་སརྦ་ཀརྟ་ཡ།སད་སུ་ཁཾ་བི་ཤུདྡྷ་ཡེ།ས་ཧ་ཡི་ཀ་བྷ་བནྟུ།ཧཱུཾ་ཧཱུཾ་ཕཊ་ཕཊ་སྭཱཧཱ།ལན་གཉིས།\nཨོཾ་ཨརྒྷཾ་པྲ་ཏི་ཙྪ་ཧཱུཾ་སྭཱཧཱ།དེ་བཞིན་དུ་སྦྱར་ཏེ།པཱདྱཾ། པུཥྤེ། དྷཱུ་པེ། དཱི་པེ། གནྡྷེ། ནཻ་ཝི་ཏྱཱ། ཤཔྟ་པྲ་ཏི་ཙྪ་ཧཱུཾ་སྭཱ་ཧཱ།ཕྱོགས་སྐྱོང་དང་ཞིང་སྐྱོང་ལ་སོགས་པ་རྣམས་ཀྱི་ཞལ་དུ་ཨོཾ་ཨཱཿཧཱུཾ།" + } + }, + { + "title": "བསུར་མཆོད་འདོན་ཡོན་སྤྲིན་ཕུང་བཞུགས་སོ།།", + "text_id": "59f62cc1-f035-476e-b8be-fa4255c63e3e", + "image_url": null, + "first_segment": { + "id": "29fbbe2b-1207-4a9d-918f-1a7235c092e3", + "content": "༄༅། །སངས་རྒྱས་ཆོས་དང་ཚོགས་ཀྱི་མཆོག་རྣམས་ལལ། །\nབྱང་ཆུབ་བར་དུ་བདག་ནི་སྐྱབས་སུ་མཆི། །\nབདག་གིས་སྦྱིན་སོགས་བགྱིས་པའི་བསོད་ནམས་ཀྱིས། །" + } + }, + { + "title": "༄༅། །གཤིན་བསྔོ་དང་གསོན་བསྔོ་བཞུགས། གཤིན་བསྔོ་ནི།", + "text_id": "92a4f34f-3146-420c-8ec7-6c693ecf47bd", + "image_url": null, + "first_segment": { + "id": "145334b4-c09c-40fb-bcec-cb605c508707", + "content": "ཕྱོགས་བཅུ་ན་བཞུགས་པའི་དཀོན་མཆོག་རྩ་གསུམ་སྐྱབས་ཡུལ་རྒྱ་མཚོ་ཐམས་ཅད་ཚེ་ལས་འདས་པའི་ཤེས་རྒྱུད་ལ་དགོངས་སུ་གསོལ། འདི་ཉིད་ཀྱི་དུས་གསུམ་གྱི་དགེ་བ་དང་འཁོར་འདས་རྣམ་དཀར་ལེགས་བྱས་ཀྱི་མཐུས། སྲིད་པ་བར་དོ་དང་ངན་འགྲོའི་གནས་ལས་ཐར་བར་གྱུར་ཅིག །ནུབ་ཕྱོགས་བདེ་བ་ཅན་གྱི་ཞིང་ཁམས་རིན་པོ་ཆེ་པདྨའི་སྙིང་པོ་ལ་བརྫུས་ཏེ་སྐྱེ་བ་ལེན་པར་གྱུར་ཅིག །སངས་རྒྱས་ཞལ་མཐོང་དམ་པའི་ག་གཅིག་པུའོ་ནོ་ཆོས་འཛིན་ཅིང་ས་ལམ་གྱི་ཡོན་ཏན་རྫོགས་ནས་མྱུར་དུ་མངོན་པར་རྫོགས་པར་སངས་རྒྱས་པར་གྱུར་ཅིག །(གསོན་བསྔོ་ནི།)\nཕྱོགས་བཅུ་རབ་འབྱམས་ན་བཞུགས་པའི་རྒྱལ་བ་སྲས་བཅས་སྐྱབས་ཡུལ་ཐམས་ཅད་ཚེ་དང་ལྡན་པས་ཐོག་དྲངས་ནས་ནམ་མཁའ་དང་མཉམ་པའི་སེམས་ཅན་ཐམས་ཅད་ལ་དགོངས་སུ་གསོལ། རྒྱུ་སྦྱོར་དགེ་བའི་རྩ་བ་འདིས་མཐུ་ལ་བརྟེན་ནས་སྦྱིན་བདག་འཁོར་དང་བཅས་པའི་འགལ་རྐྱེན་བར་ཆད་མི་མཐུན་པའི་ཕྱོགས་ཐམས་ཅད་ཞི་བར་གྱུར་ཅིག །\nཚེ་དང་བསོད་ནམས་ཡེ་ཤེས་ཐམས་ཅད་འཕེལ་ཞིང་རྒྱས་པར་གྱུར་ཅིག །གསང་སྔགས་རྡོ་རྗེ་ཐེག་པའི་སྨིན་གྲོལ་ས་ལམ་མཐར་ཕྱིན་སྟེ་མྱུར་དུ་མངོན་པར་རྫོགས་པར་སངས་རྒྱས་པར་གྱུར་ཅིག །(ཅེས་གཤིན་བསྔོ་དང་གསོན་བསྔོ་གཉིས་པོ་འདི་ཀརྨ་རབ་རྒྱས་ཀྱིས་བསྐུལ་བའི་ངོར་”རྡོ་རྗེ་དྲག་པོས་བརྗོད་པའོ། །དགེའོ།། )།།" + } + }, + { + "title": "༄༅། ། དྭགས་པོ་བཀའ་བརྒྱུད་སྤྱི་ལ་གསོལ་བ་འདེབས་པ་ནི།", + "text_id": "4802b744-44d7-427a-8986-f87fde61a5e8", + "image_url": null, + "first_segment": { + "id": "34a27e05-7682-42d5-b98b-96e4bd58afca", + "content": "༄༅། ། དྭགས་པོ་བཀའ་བརྒྱུད་སྤྱི་ལ་གསོལ་བ་འདེབས་པ་ནི།\nརྡོ་རྗེ་འཆང་ཆེན་ཏཻ་ལོ་ནཱ་རོ་དང༌། །\nམར་པ་མི་ལ་ཆོས་རྗེ་སྒམ་པོ་པ། །" + } + }, + { + "title": "༄༅། །རྩ་བའི་བླ་མའི་གསོལ་འདེབས་དངོས་གྲུབ་འབྱུང་གནས་ཞེས་བྱ་བ་བཞུགས་སོ།།", + "text_id": "a05fccfe-b05a-4e22-a733-a4be940098fd", + "image_url": null, + "first_segment": { + "id": "eb2b12ba-8b55-4af9-bc0e-72061e0726ca", + "content": "ན་མཿཤྲཱི་གུ་རུ་ཝེ།\nདངོས་གྲུབ་འབྱུང་གནས་བླ་མ་རིན་པོ་ཆེ།།\nའགྲོ་ཀུན་སྤྱི་གཙུག་པད་ཟླའི་གདན་བཞུགས་ནས།།" + } + }, + { + "title": "ཕྱག་ཆེན་བརྒྱུད་པའི་གསོལ་འདེབས་ནི། །", + "text_id": "9ce9ecbf-a4cd-4b63-b9b3-5beb184e23e0", + "image_url": null, + "first_segment": { + "id": "a69e5c42-955a-42a7-b022-68892515f430", + "content": "ན་མོ་མ་ཧཱ་མུ་དྲཱ་ཡེ། །\nལྷུན་གྲུབ་སྐུ་གསུམ་གྱི་གཞལ་ཡས་སུ། །\nདཔལ་དང་པོའི་སངས་རྒྱས་རིགས་ཀུན་གཙོ། །" + } + }, + { + "title": "སྨོན་ལམ་ཕྱོགས་བཅུ་དུས་བཞི་མ།", + "text_id": "e6f499b2-e9b6-43a3-964b-f787be2e7ea7", + "image_url": null, + "first_segment": { + "id": "d5392c6a-409a-426c-99bb-f07076605e16", + "content": "ན་མོ་གུ་རུཿ སྤྲེལ་ལོ་སྤྲེལ་ཟླ་ར་བའི་ཚེས་བཅུ་ལ། བསམ་ཡས་བར་ཁང་གཡུ་ཞལ་ཅན་དུ་རྡོ་རྗེ་དབྱིངས་ཀྱི་དཀྱིལ་འཁོར་ཞལ་ཕྱེས་ཚེ་ཨོ་རྒྱན་གྱིས་སྨོན་ལམ་འདི་གསུངས་པས། རྗེ་འབངས་ཐམས་ཅད་ཀྱིས་ཐུགས་དམ་ནར་མར་མཛད། ཕྱི་རབས་རྣམས་ཀྱིས་ཀྱང་འདི་ལ་ཐུགས་དམ་རྩེ་གཅིག་ཏུ་མཛོད།\nཕྱོགས་བཅུ་དུས་བཞིའི་རྒྱལ་བ་སྲས་དང་བཅས༔\nབླ་མ་ཡི་དམ་མཁའ་འགྲོ་ཆོས་སྐྱོང་ཚོགས༔" + } + }, + { + "title": "བདུད་བཟློག་གསང་བའི་མན་ངག", + "text_id": "11066ed6-121b-4160-8e89-0ac08ea88c13", + "image_url": null, + "first_segment": { + "id": "7567e130-9415-4b3b-a62f-c40210da0559", + "content": "འཇམ་དཔལ་བདུད་དགྲ་འདུལ་ལ་བདུད། །\nབདུད་བཟློག་གསང་བའི་མན་ངག་བཤད། །\nཕྱི་རུ་གང་སྣང་དབང་མི་འཁོལ། །" + } + }, + { + "title": "འཇིགས་བྱེད་དཔའ་བོ་གཅིག་པའི་བླ་བརྒྱུད་གསོལ་འདེབས་བཞུགས་སོ།", + "text_id": "a79fbedd-ad46-4842-a391-3363c0247e86", + "image_url": null, + "first_segment": { + "id": "c10dfbe4-fc98-48e6-bd28-a0154e842f37", + "content": "༄༅།།ན་མོ་གུ་རུ་བཛྲ་བྷཻ་ར་ཝ་ཡ།\nཁྱབ་བདག་འཇམ་པའི་རྡོ་རྗེ་གཤིན་རྗེའི་གཤེད། །\nམགོན་དེའི་དགྱེས་པ་ཀུན་བསྐྱེད་མཁའ་འགྲོའི་གཙོ། །" + } + }, + { + "title": "བདེ་སྨོན་བསྡུས་པ།།", + "text_id": "7041aea7-ab6e-4dcd-914e-d3ce4f995eed", + "image_url": null, + "first_segment": { + "id": "ef8c161d-d4a8-458c-8a0c-d89d0189638a", + "content": "༈ ཨེ་མ་ཧོ༔\nངོ་མཚར་སངས་རྒྱས་སྣང་བ་མཐའ་ཡས་དང༔\nགཡས་སུ་ཇོ་བོ་ཐུགས་རྗེ་ཆེན་པོ་དང༔" + } + }, + { + "title": "བདེ་སྨོན་བསྡུས་པ་ཤེས་བྱ་མ།", + "text_id": "f2c687ae-a815-4b71-a043-02f1b8267212", + "image_url": null, + "first_segment": { + "id": "9ef3edc3-4921-4fef-935b-f42c1add235f", + "content": "༄༅།། མངྒ་ལཾ་སྭ་སྟི་བྷ་ཝནྟུ།\nཤེས་བྱ་ཐམས་ཅད་མངོན་སུམ་གཟིགས་པ་པོ། །\nམཆོག་གི་ཡོན་ཏན་མ་ལུས་མཐར་ཕྱིན་པའི། །" + } + }, + { + "title": "༄༅། །བདེན་ཚིག་འགྲུབ་པའི་པྲ་ཎི་དྷ་རྣམ་མཁྱེན་གྲོང་འཇུག་ཅེས་བྱ་བ་བཞུགས་སོ། །", + "text_id": "0960fdd9-4af7-4567-badb-1528566a3497", + "image_url": null, + "first_segment": { + "id": "24d5bcbd-9962-4b78-91e6-b5c070e87872", + "content": "དངོས་གྲུབ་རྒྱ་མཚོའི་འབྱུང་གནས་བླ་མ་དང་མཆོག་གསུམ་བྱང་ཆུབ་སེམས་དཔའ་རྣམས་ལ་ཕྱག་འཚལ་ཞིང་སྐྱབས་སུ་མཆིའོ། །བྱིན་གྱིས་བརླབ་ཏུ་གསོལ།\nམི་ངའི་སྐྱེ་བ་ནས་ཚེ་རབས་ཐམས་ཅད་དུ་དལ་འབྱོར་བཅོ་བརྒྱད་ཚང་བའི་མི་ལུས་རིན་པོ་ཆེ་ཐོབ་སྟེ་རྗེ་བཙུན་བླ་མ་མཚན་ཉིད་དང་ལྡན་པའི་གདུལ་བྱར་འགྱུར་བར་ཤོག་ཅིག\nམངོན་མཐོ་དང་ངེས་ལེགས་མཐའ་དག་གི་འབྱུང་ཁུངས་ཐོས་བསམ་སྒོམ་གསུམ་གྱིས་རང་རྒྱུད་བཏུལ་ཏེ་རྒྱལ་བའི་བསྟན་པ་རིན་པོ་ཆེའི་རྗེས་སུ་སློབ་པར་ཤོག་ཅིག" + } + }, + { + "title": "༄༅། །བཅོམ་ལྡན་འདས་སྨན་བླའི་མདོ་ཆོག་སྙིང་པོ་བསྡུས་པ་ཡིད་བཞིན་ནོར་བུ་ཞེས་བྱ་བ་བཞུགས་སོ།། ༈", + "text_id": "0d649162-0537-411e-b7a8-ebca6dca97c4", + "image_url": null, + "first_segment": { + "id": "f931d451-bb2f-4e0a-895e-ea39c77f4a56", + "content": "ན་མོ་གུ་རུ་མུ་ནེ་ཨིནྡྲ་ཡ།\nགང་གི་མཚན་ཙམ་ཐོས་པའི་ཆུ་རྒྱུན་གྱིས། །\nམནར་མེད་མེ་ཡང་རབ་བསིལ་པད་མཚོ་ལྟར། །" + } + }, + { + "title": "གོང་མ་རྣམ་གསུམ་གྱི་སྐུ་བསྟོད།", + "text_id": "c5a839ba-e88f-425e-94e2-c24d2e9d598a", + "image_url": null, + "first_segment": { + "id": "3bb8eeb4-afa9-4d42-ab25-f93c8993b26b", + "content": "ལ་ལཱ་རཱ་སཱ་དྷུ་ཏཱིར་དག་པའི་མཐུས། །\nདབེན་གསུམ་བདུད་རྩིས་བརླན་པའི་པདྨ་ཅན། །\nམི་ཤིག་སྙིང་པོའི་འཆར་ཡང་རྡོ་རྗེའི་སྲོག །" + } + }, + { + "title": "༄༅། །མཁས་གྲུབ་རཱ་ག་ཨ་སྱས་མཛད་པའི་རྣམ་དག་བདེ་ཆེན་ཞིང་གི་སྨོན་ལམ་བཞུགས་སོ།།", + "text_id": "a088fa93-de08-434f-8f78-bb5f06fbfc31", + "image_url": null, + "first_segment": { + "id": "0b6c865e-574e-45e4-bb61-7d69700383a8", + "content": "༄༅། ། འདི་ཉིད་ཆགས་མེད་ཐུགས་དམ་མཛོད། །\nལག་པ་ན་ཡང་འབད་ནས་བྲིས། །\nམང་པོ་འགའ་ལ་ཨེ་ཕན་བསམ།" + } + }, + { + "title": "ཆོ་འཕྲུལ་གྱི་བསྟོད་པ་ནི།།", + "text_id": "9ac09136-ea50-4ce4-85e4-040ecb558eb7", + "image_url": null, + "first_segment": { + "id": "148e8e43-657f-40d6-87b4-4f697627e7a8", + "content": "དང་པོ་བྱང་ཆུབ་མཆོག་ཏུ་སེམས་བསྐྱེད་ནས། །\nབསྐལ་པ་གྲངས་མེད་གསུམ་དུ་ཚོགས་བསགས་ཤིང༌། །\nབར་དུ་གཅོད་པའི་བདུད་བཞི་གཅིག་འཇོམས་མཛད་པ། །" + } + }, + { + "title": "རྡོ་རྗེ་ཆོས་སྐྱོང་བའི་སྲུང་མ་རྣམས་ལ་མངོན་པར་བསྟོད་པ་ཚངས་པའི་གླུ་དབྱངས་ཞེས་བྱ་བ་བཞུགས་སོ།", + "text_id": "7b7be44e-c53a-4c53-814f-3c2241901a46", + "image_url": null, + "first_segment": { + "id": "8e8179d7-9668-4d0e-87b5-2e690b16f286", + "content": "ན་མཿཤྲཱི་བཛྲ་དྷརྨ་བཱ་ལ་ཡེ།\nཚངས་པ་མགྲིན་སྔོན་ཁྱབ་འཇུག་དང་ནི་དབང་པོ་ཉེ་དབང་བཙུན་མོར་བཅས། །\nལྷ་ཆེན་ཀུན་གྱིས་མངོན་པར་བསྟོད་པ་དཔལ་ལྡན་རྡོ་རྗེ་ནག་པོ་ཆེ། །" + } + }, + { + "title": "ཇ་མཆོད་སོགས་ཀྱི་སྐོར་ནི།", + "text_id": "e411acda-2f67-4b9b-a0b0-11327c13d0a4", + "image_url": null, + "first_segment": { + "id": "5ecfe42c-fc3f-4ef0-b569-bdea85a22b1f", + "content": "དྲུག་པ་སེང་གེའི་རྣམ་འཕྲུལ་དུས་གསུམ་མཁྱེན། །\nའཛམ་གླིང་བསྟན་པའི་མངའ་བདག་་ཀརྨ་པ། །\nརང་བྱུང་ཆོས་ཀྱི་སྤྱན་ལྡན་རྡོ་རྗེའི་ཐུགས། །" + } + }, + { + "title": "ཇོ་དར་རབ་གནས་བཞུགས་སོ།", + "text_id": "e0cfd0e3-26c4-4820-91a0-4181c275f0aa", + "image_url": null, + "first_segment": { + "id": "6dd6995d-be03-4c4b-89ef-01ba4bcd80c8", + "content": "སྐྱབས་སེམས་སྔོན་དུ་འགྲོ་བས། ཨོཾ་སྭ་བྷཱ་ཝས་སྦྱང༌། རང་ཉིད་སྐད་ཅིག་གིས་ཕུང་པོ་ཁམས་དང་སྐྱེ་མཆེད་རྣམས་མི་དམིགས་ཏེ་སྟོང་པ་ཉིད་དུ་གྱུར།\nསྟོང་པའི་ངང་ལས། པདྨ་དང་ཟླ་བའི་གདན་ལ། ཧྲཱིཿལས་པདྨ་དཀར་པོ་ཧྲཱིཿས་མཚན་པ། དེ་ཡོངས་སུ་གྱུར་པ་ལས་རང་ཉིད་སྐད་ཅིག་གིས་འཕགས་པ་འཇིག་རྟེན་དབང་ཕྱུག་བསིལ་ཟེར་བྱེད་པའི་སྙིང་པོ་ལྟ་བུ་ཞལ་གཅིག་ཞིང་ཞིང་འཛུམ་པ། རལ་པའི་ཟུར་ཕུད་འོད་དཔག་ཏུ་མེད་པས་བརྒྱན་ཅིང་། ཕྱག་བཞི་དང་པོ་གཉིས་ཐལ་མོ་སྦྱར་བ་རྒྱལ་བའི་དབང་པོ་ཀུན་གྱི་ཕྱག་རྒྱ་ཐུགས་ཀར་གནས་པ། འོག་མ་གཉིས་ཀྱིས་གཡས་ན་བགྲང་ཕྲེང་དང་། གཡོན་ཆུ་སྐྱེས་ཀྱི་སྡོང་བུ་འཛིན་པ། རིན་པོ་ཆེའི་རྒྱན་ཐམས་ཅད་ཀྱིས་བརྒྱན་ཅིང་མཛེས་སྡུག་གི་དཔྱིད། དར་གྱི་ན་བཟའ་ཀླུབས་པ། ཀུནྡ་འབར་བ་ལྟ་བུའི་འོད་ཟེར་གྱི་ཁོང་ན་རྡོ་རྗེ་སྐྱིལ་མོ་ཀྲུང་གིས་པད་ཟླའི་གདན་ལ་བཞུགས་པར་གྱུར།\n(མ་ཎི་ཅི་རིགས་བཟླ། )སུམྦྷ་ནི་ཅི་རིགས་ཡུངས་ཐུན་བྲབ་ལ། ཡེ་དྷརྨ་ཅི་རིགས་བཀྲ་ཤིས་འབྲུ་ལ་རྒྱབ།བགེགས་གཏོར་བཤམས་ནས་ཆབ་བྲན། ཨོཾ་ཨཱཿཧཱུྃ། ཨ་ཀཱ་རོ་(ལན་གསུམ།) འབྱུང་པོ་འདུལ་བྱེད་ཀྱི་ཕྱག་རྒྱ་སྙིང་ཁར་བཅའ།མཆོད་སྦྱིན་གྱི་གཏོར་མ་འདོད་པའི་ཡོན་ཏན་ལྔ་དང་ལྡན་པ་འདི་ཉིད་བགེགས་ཀྱི་རྒྱལ་པོ་བཱི་ན་ཡ་ཀ། བདུད་ཀྱི་རྒྱལ་པོ་དགའ་རབ་དབང་ཕྱུག་ལ་སོགས་པ་ས་སྟེང་ས་འོག་ས་བླ་གསུམ་ན་གནས་པའི་གནོད་བྱེད་གདོན་བགེགས་འབྱུང་པོའི་ཚོགས་དང་བཅས་པ་ཐམས་ཅད་མཆོད་སྦྱིན་འདིས་ཚིམ་པར་གྱིས་ལ་རང་རང་སོ་སོའི་གནས་སུ་དེངས་ཤིག །(དེ་ནས་རོལ་ཆེན་དང་བཅས་ངག་ཏུ་སུམྦྷ་ནི་བཟླས་ནས་བགེགས་རྣམས་རིང་དུ་བསྐྲད།" + } + }, + { + "title": "༄༅། །ཉིན་ཞག་ཕྲུགས་གཅིག་ལ་ཉམས་ལེན་བྱ་ཚུལ་ནོར་བུ་རྣམ་པར་བཀྲ་བའིམཛེས་རྒྱན།", + "text_id": "5913e790-892f-4b86-b23c-aade5e844916", + "image_url": null, + "first_segment": { + "id": "3aedfe1b-c483-4704-b6bd-6bd50fb4b413", + "content": "༄༅། །ན་མོ་རཏྣ་ཏྲ་ཡཱ་ཡ། སྡོམ་གསུམ་ལྡན་པས་ཉིན་ཞག་ཕྲུགས་གཅིག་ལ་ཉམས་ལེན་ཇི་ལྟར་བྱ་བའི་ཚུལ་ནོར་བུ་རྣམ་པར་བཀྲ་བའི་མཛེས་རྒྱན་ཅེས་བྱ་བ། ཆོས་སྤྱོད་རྣམ་བཅུ་ལས་གྲུབ་གསེར་སྦྱངས་ཀྱི།འཁོར་ལོ་ཉིན་བཞིན་བསྐོར་མཛད་མཐའ་དག་ལ། །བཏུད་ནས་སྐལ་བཟང་རྣམ་དཔྱོད་ལྡན་པ་རྣམས། །\nཚུལ་དེར་ཇི་བཞིན་འཇུག་པའི་རིམ་པ་བྲི། །སྡོམ་གསུམ་ལྡན་པའི་དགེ་སློང་གིས།\nཉིན་ཞག་ཕྲུགས་གཅིག་ལ་དགེ་སྦྱོར་ཇི་ལྟར་བྱ་བའི་ཚུལ་ནི། འགྲོ་འཆག་ཉལ་འདུག་བདེ་སྡུག་ཅི་འདྲ་ཞིག་གི་གནས་སྐབས་སུ་ཡང༌། ངོ་ཚ་ཁྲེལ་ཡོད་དྲན་ཤེས་བག་དང་ལྡན་པས་སྡོམ་གསུམ་སོ་སོའི་བཅས་མཚམས་ཕྲ་ཞིང་ཕྲ་བ་ལས་ཀྱང་འདའ་བ་མེད་པ་གཞིར་བཟུང༌། རྒྱུད་སྡེ་བཞི་པོ་རིགས་དང་འབྲེལ་བའི་བསྙེན་སྒྲུབ་ཆིག་དྲིལ་དུ་ཐུན་བཞི་འམ། དྲུག་ཏུ་བྱེད་པདང༌། དེ་དག་གི་ཐུན་མཚམས་རྣམས་སུ་ནི་རང་རང་གི་སྒྲུབ་ཐབས་རྣམས་ནས་འབྱུང་བ་ལྟར་བྱ། དེ་མིན་རྒྱུན་པར་ནམ་གྱི་སུམ་ཆ་ལུས་པ་ན་འོད་གསལ་གྱི་ངང་ལས་བཞེངས་པར་མོས་ཏེ། མ་ཉལ་བར་རིག་པ་དྭངས་" + } + }, + { + "title": "དེ་ནས་ཏཱ་ཡི་སི་ཏུ་རིམ་པར་བྱོན་པ་རྣམས་ཀྱི་གསོལ་འདེབས་ནི།", + "text_id": "80a60fc8-4fa1-4abb-9391-61f8ae90bc06", + "image_url": null, + "first_segment": { + "id": "3094b9ed-54b9-4455-a347-94fe6dde7667", + "content": "དེ་ནས་ཏཱ་ཡི་སི་ཏུ་རིམ་པར་བྱོན་པ་རྣམས་ཀྱི་གསོལ་འདེབས་ནི།\nདགེ་ལེགས་དུ་མའི་རྫུ་འཕྲུལ་མི་ཟད་པ། །\nཞིང་ཁམས་རྒྱ་མཚོར་རྨད་བྱུང་རོལ་པ་ཅན། །" + } + }, + { + "title": "ཐུན་དྲུག་གི་རྣལ་འབྱོར་མེ་ཏོག་ཕྲེང་མཛེས་ཞེས་བྱ་བ་བཞུགས་སོ། །", + "text_id": "2a458c1e-0c37-434e-964d-8e1560169e87", + "image_url": null, + "first_segment": { + "id": "bac0cdb6-3041-449b-ba9a-c02b1d99c8fa", + "content": "༄༅། །ན་མོ་གུ་རུ་མཉྫུ་གྷོ་ཥཱ་ཡ། ར\nྡོ་རྗེ་འཆང་དབང་དཔལ་ལྡན་བླ་མ་ཡི། ། ཞ\nབས་ཀྱི་པདྨོར་གུས་པས་ཕྱག་བྱས་ཏེ། ། ད" + } + }, + { + "title": "༈ ངག་བྱིན་རླབས།", + "text_id": "88a97ec2-144d-4c4a-8588-2b6357fe8a61", + "image_url": null, + "first_segment": { + "id": "9de72374-9f90-4b19-b1cf-66001004b1a9", + "content": "༄༅། །ན་མོ་གུ་རུ་ཝེ།\nཐོ་རངས་ངག་བྱིན་བརླབ་ཚུལ་ནི།\nདཀོན་མཆོག་གསུམ་ལ་སྐྱབས་སུ་མཆི། །" + } + }, + { + "title": "ཐུན་བཞི་བླ་མའི་རྣལ་འབྱོར།", + "text_id": "9cf6e752-052c-4550-9f96-f01fe8468d82", + "image_url": null, + "first_segment": { + "id": "54ab9949-e8c5-4dfe-a7db-43bf82a51c3c", + "content": "དེ་ནས་དུས་ཐམས་ཅད་དང་ཁྱད་པར་ཐུན་ཆེན་པོ་བཞིར་བྱ་བའི་བླ་མའི་རྣམ་འབྱོར་ནི། ད་ནི་ཁོ་བོ་མི་བསྐྱོད་རྡོ་རྗེ་ཁོ་ན་མིན་པ་བསམ་རྒྱུ་མེད་པ་ཀུན་རང་གི་ལུས་ཡེ་ཤེས་ཀྱི་མཁའ་འགྲོ་གཅེར་བུ་ལང་ཚོ་ཕུན་སུམ་ཚོགས་པ། དབུ་སྐྲ་སིལ་མས་རྒྱབ་མནོན་པ། འོད་ཕུང་གི་དབུས་ན་འཆི་མེད་བདུད་རྩིས་གང་བའི་ཀ་པཱ་ལ་ཐོགས་པ། མེ་ཏོག་དམར་པོའི་རྒྱན་ཅན། གཞན་རྒྱན་སྤང་པ། དེའི་མདུན་དུ་རྗེ་བཙུན་མི་བསྐྱོད་རྡོ་རྗེ་སྟག་ལྤགས་ཀྱི་ཤམ་ཐབས། གླང་ཆེན་གྱི་པགས་པའི་བླ་གོས་ཅན། ཕྱག་རྒྱ་དྲུག་གིས་སྤྲས་པ།\nདབུ་སྐྲ་ཐོར་ཚུགས་སྣ་ཚོགས་རྡོ་རྗེ་ཟླ་བས་བརྒྱན་པ། ཕྱག་གཉིས་ཆོས་འབྱུང་གི་ཕྱག་རྒྱ་སྤྱི་བོར་དཀོད་པ། ཞབས་གཡོན་སྐྱིལ་ཀྲུང་ཕྱེད་པ་དང་། གཡས་བརྐྱང་པས་ཡེ་ཤེས་ཀྱི་མེ་དཔུང་གི་དབུས་ན་བཞུགས་པ། དེ་ལ་ཡུམ་ཡེ་ཤེས་ཀྱི་མཁའ་འགྲོས་བདུད་རྩི་བརྟབས་པ་ཙམ་གྱིས་ཡེ་ཤེས་ཀྱི་མེ་དམར་འུར་གྱིས་འབར་བ། རང་ཉིད་ཡེ་ཤེས་ཀྱི་མཁའ་འགྲོའི་བྷ་ག་ནས་སྤྱན་དྲངས། ཐུགས་ཀར་ཞུགས་པར་མོས་ལ། དྲག་སྦྱོར་གྱི་ངང་ནས་གསོལ་བ་ཐོབ་ཅིག་དང༌། ཁོ་བོ་མི་བསྐྱོད་རྡོ་རྗེས་བྱིན་གྱིས་རློབས་པ་ཡིན་ནོ་ཞེས་བྱིན་དབབ་པར་བྱའོ། །\nམ་ནམ་མཁའ་དང་མཉམ་པའི་སེམས་ཅན་ཐམས་ཅད་བླ་མ་སངས་རྒྱས་རིན་པོ་ཆེ་ལ་གསོལ་བ་འདེབས་སོ། །" + } + }, + { + "title": "༄༅། །ཐུབ་མཆོག་བྱིན་རླབས་གཏེར་མཛོད་བཞུགས་སོ།", + "text_id": "491317e3-b202-40a8-a420-1e24ec087572", + "image_url": null, + "first_segment": { + "id": "bd280509-c53f-41b8-b9ab-274b6d9fdd62", + "content": "ན་མོ་གུ་རུ་ཤཱ ཀྱ་མུ་ན་ཡེ།\nདེ་ཡང་མདོ་ཏིང་འཛིན་རྒྱལ་པོ་ལས།\nའཆག་དང་འདུག་དང་འགྲེང་དང་ཉལ་བ་ན། །" + } + }, + { + "title": "༄༅། དགོངས་གཏེར་སྒྲོལ་མའི་ཟབ་ཏིག་ལས། མཎྜལ་ཆོ་ག་ཚོགས་གཉིས་སྙིང་པོ་ཞེས་བྱ་བ་བཞུགས་སོ། །", + "text_id": "529fb554-fc6e-4aa7-a3b5-6887b1891610", + "image_url": null, + "first_segment": { + "id": "5bfe86c7-296c-4e6f-89fe-23560c3870e0", + "content": "ན་མོ་གུ་རུ་ཨཱརྻ་ཏཱ་ར་ཡེ།\nདུས་གསུམ་འདྲེན་པ་སྲས་བཅས་ལས། །\nཐུགས་བསྐྱེད་ཕྲིན་ལས་རྨད་བྱུང་བ། །" + } + }, + { + "title": "དཔལ་ཀྱཻ་རྡོ་རྗེའི་མངོན་པར་རྟོགས་པ་འབྲིང་དུ་བྱ་བ་ཡན་ལག་དྲུག་པའི་མཛེས་རྒྱན་ཞེས་བྱ་བ་བཞུགས་སོ། །", + "text_id": "eb268d1e-1d08-484c-b474-232d1de03531", + "image_url": null, + "first_segment": { + "id": "413e132b-e57e-4bad-854d-9a566a41418b", + "content": "ན་མཿཤྲཱི་ཙཀྲ་ནཱ་ཐ་རཏྣ་ཝརྡྷཱ་ཡ།\nཀུན་ཁྱབ་ཐབས་ལ་མཁས་པའི་སྙིང་རྗེ་དང་། །\nརྣམ་ཀུན་མཆོག་ལྡན་འོད་གསལ་ཤེས་རབ་ཆེ། །" + } + }, + { + "title": "༄༅། །དབྱངས་ཅན་མའི་བསྟོད་པ་བཞུགས་སོ།", + "text_id": "7537f820-8df2-4611-9c5c-c7553c935406", + "image_url": null, + "first_segment": { + "id": "763b430a-0202-410b-9b4d-ce280a428438", + "content": "༄༅།ཨོཾ་བདེ་ལེགས་སུ་གྱུར་ཅིག།\nཆུ་འཛིན་དཀར་པོའི་གློག་ཕྲེང་དྲ་བ་ཅན། །\nམཁའ་ཡི་མཛེས་བྱེད་འདྲ་བའི་ཡིད་འཕྲོག་མ། །" + } + }, + { + "title": "དབྱངས་ཅན་མའི་སྒྲུབ་ཐབས།", + "text_id": "8bccb9e7-5576-4cab-8be9-94ff00162747", + "image_url": null, + "first_segment": { + "id": "26c34745-8b40-475b-ad7d-f5aabe911be0", + "content": "དབྱངས་ཅན་མའི་སྒྲུབ་ཐབས།\nཨོཾ་སྭསྟི།\nའཇིག་རྟེན་མགོན་པོའི་ཐུགས་མཚོ་ལས། །" + } + }, + { + "title": "༄༅། །དམ་ཚིག་རྡོ་རྗེའི་སྒྲུབ་ཐབས་སྡིག་སྒྲིབ་ཀུན་འཇོམས་བཞུགས་སོ།།", + "text_id": "52aacbd8-4bf3-4622-bb26-4588533af16b", + "image_url": null, + "first_segment": { + "id": "86ddc405-b355-4b61-aeb0-f5de5f34d0c7", + "content": "ན་མོ་གུ་རུ་བྷྱཿ\nབླ་མ་རྗེ་བཙུན་མཆོག་ལ་ཕྱག་འཚལ་ནས། །\nཡང་སྙིང་རྡོ་རྗེ་ཐེག་པའི་རྣལ་འབྱོར་པས། །" + } + }, + { + "title": "སྔགས་རིམ་སྨོན་ལམ་བཞུགས་སོ།།", + "text_id": "20ab380a-f5c3-4109-a35a-4fb793ccba5c", + "image_url": null, + "first_segment": { + "id": "6394a3bb-41a3-4da4-80a7-a5a9225d1307", + "content": "གསང་ཆེན་ལམ་གྱི་རིམ་པ་འདིར་འབད་པས། །ར\nླབས་ཆེན་ཚོགས་གཉིས་མང་དུ་བསགས་པ་རྣམས། །མ\nར་གྱུར་འགྲོ་བ་ཀུན་གྱི་རེ་སྐོང་བའི། །ཟ" + } + }, + { + "title": "བྱང་ཤམྦྷ་ལར་སྐྱེ་བའི་སྨོན་ཚིག་བཞུགས་སོ།།", + "text_id": "8666acf7-a646-42ba-8c20-e77021468d62", + "image_url": null, + "first_segment": { + "id": "e6931aa8-d58e-430b-a3a6-7657c1287781", + "content": "༄༅། །ན་མོ་གུ་རུ།\nདཔལ་ལྡན་དང་པོའི་སངས་རྒྱས་རིང་ལུགས་ཆེ།།\nཤམྦྷ་ལ་ཡི་གྲོང་དུ་རྒྱས་མཛད་པ། །" + } + }, + { + "title": "༄༅། །བསྟན་པའི་སྙིང་པོ་བྱང་སྡོམ་བརྒྱུད་འདེབས་རྒྱལ་སྲས་དཔལ་སྟེར།།", + "text_id": "7bd6b919-7cb3-4694-ab03-7e90d3d83559", + "image_url": null, + "first_segment": { + "id": "60ffb7c0-ccc5-4b30-9a29-26bbbf73ee71", + "content": "སྟོན་པ་སངས་རྒྱས་རྗེ་བཙུན་འཇམ་དཔལ་དབྱངས། །\nཡོད་མེད་ཕྱོགས་འཇིག་ཀླུ་སྒྲུབ་འཕགས་པ་ལྷ། །\nཟླ་བ་གྲགས་པ་རིག་པའི་ཁུ་བྱུག་སོགས། །" + } + }, + { + "title": "༄༅། །འཇམ་མགོན་བླ་མ་ཙོང་ཁ་པ་ཆེན་པོས་མཛད་པའི་བློ་སྦྱོང་སྙན་ངག་སྒྲ་རྒྱན་བཞུགས་སོ།", + "text_id": "8c255cdf-9599-4c91-bc2f-6cb427f3e753", + "image_url": null, + "first_segment": { + "id": "d1c3a4a0-7cd7-41dd-b690-f2973c52c7e8", + "content": "༄༅། །འཇམ་མགོན་བླ་མ་ཙོང་ཁ་པ་ཆེན་པོས་མཛད་པའི་བློ་སྦྱོང་སྙན་ངག་སྒྲ་རྒྱན་བཞུགས་སོ།\n༑ན་མོ་གུ་རུ་མཉྫུ་གྷོ་ཥ་ཡ།\nརྒྱལ་བ་སྲས་དང་བཅས་ལ་ཕྱག་འཚལ་ལོ། །" + } + }, + { + "title": "༄༅། །ཟངས་མདོག་དཔལ་རིར་བགྲོད་པའི་སྨོན་ལམ་སྐལ་བཟང་དགའ་བའི་ཤིང་རྟ་བཞུགས།།", + "text_id": "e9942f0b-8d78-4512-be34-064508f24dbb", + "image_url": null, + "first_segment": { + "id": "4e514438-7c65-4357-8c2d-1ba91e9f79b3", + "content": "རང་སྣང་དག་པ་བདེ་ཆེན་རྡོ་རྗེ་དབྱིངས། །\nལྷུན་གྲུབ་འོག་མིན་སྒྱུ་འཕྲུལ་དྲྭ་བའི་གར། །\nརབ་འབྱམས་རྒྱལ་བའི་ཞིང་ཁམས་རྒྱ་མཚོའི་ཕུལ། །" + } + }, + { + "title": "དུས་མཁྱེན་གྱི་རྣམ་ཐར་བསྟོད་པ་ནི།", + "text_id": "2c18e8cb-6158-411f-ae4b-290ecf869a06", + "image_url": null, + "first_segment": { + "id": "701db386-247a-45c3-a142-59af0b01d52c", + "content": "ཨེ་མ་ཧོ། །\nབསམ་པ་ཐམས་ཅད་འགྲུབ་མཛད་པ། །\nམཐའ་དབུས་མེད་པ་ནམ་མཁའི་བདག །" + } + }, + { + "title": "༄༅། །གསུར་མཆོད་འདོད་ཡོན་སྤྲིན་ཕུང་བཞུགས།།", + "text_id": "18a226cf-4417-42d6-8cb0-64b88901e4fe", + "image_url": null, + "first_segment": { + "id": "92baab68-23d4-4570-8c46-cb252fe4de2b", + "content": "ཨོཾ་སྭཱ་སྟི།\nཡེ་ཤེས་གཅིག་ཉིད་དུ་མའི་རྣམ་རོལ་ནི། །\nགདུལ་བྱའི་མོས་ངོར་ཤར་བ་རྩ་གསུམ་ལྷར། །" + } + }, + { + "title": "འགྲོ་མགོན་ཆོས་རྒྱལ་འཕགས་པ་ལ་བསྟོད་པ།།", + "text_id": "767591e9-f388-4969-8b39-789611648890", + "image_url": null, + "first_segment": { + "id": "8d874ed3-f266-4161-9115-064bb3ac0d3d", + "content": "༈ །ཨོཾ་སྭསྟི། སངས་རྒྱས་ཀུན་གྱི་ཐུགས་རྗེ་གཅིག་བསྡུས་པ། །\nརྡོ་རྗེ་འཆང་ཆེན་དཔལ་ལྡན་འཇམ་པའི་དབྱངས། །\nཀུན་མཁྱེན་བསྟན་པའི་སྒྲོན་མེ་སྐྱེ་དགུའི་མགོན། །" + } + }, + { + "title": "༄༅། །བྱམས་པའི་སྐུ་གཟུགས་མ་བཞུགས་སོ།།", + "text_id": "075efda8-f4bb-44c6-ba2c-630bfc1a97b5", + "image_url": null, + "first_segment": { + "id": "2df1054e-160d-4187-9b29-2a535b3e7e4b", + "content": "༈ བྱམས་པའི་སྐུ་གཟུགས་ཕུལ་བྱུང་བཞེངས་པ་ལ།།\nམཐུན་རྐྱེན་སྒྲུབ་པར་བྱེད་པའི་ལུས་ཅན་རྣམས། །\nརྗེ་བཙུན་བྱམས་པ་མགོན་པོའི་ཞབས་དྲུང་དུ། །" + } + }, + { + "title": "༄༅། །བོད་ཡུལ་བདེ་བའི་སྨོན་ལམ། །", + "text_id": "acc0c9c9-85f5-4b36-b7a0-db2bbf7aaa89", + "image_url": null, + "first_segment": { + "id": "99e160c6-2539-49d7-bdbb-c4ca6ad1a796", + "content": "སྐྱབས་གནས་བསླུ་མེད་དཀོན་མཆོག་རྩ་བ་གསུམ། །\nཁྱད་པར་གངས་ཅན་མགོན་པོ་སྤྱན་རས་གཟིགས། །\nརྗེ་བཙུན་སྒྲོལ་མ་གུ་རུ་པདྨ་འབྱུང་། །" + } + }, + { + "title": "འདོད་གསོལ་སྨོན་ཚིག་ཕུན་ཚོགས་ཡོན་ཏན་མ།", + "text_id": "a3207058-b1ed-4515-85bd-385e317ef03f", + "image_url": null, + "first_segment": { + "id": "b546331c-70de-47f9-8561-b60a303b523a", + "content": "༄༅། ན་མོ་གུ་རུ་མཉྫུ་གྷོ་ཥཱ་ཡ།\nཕུན་ཚོགས་ཡོན་ཏན་ཀུན་གྱི་དཔལ་འབར་ཞིང་། །\nདྲན་པ་ཙམ་གྱིས་སྲིད་ཞིའི་གདུང་སེལ་བ། །" + } + }, + { + "title": "༈ །རྗེ་བཙུན་རིན་པོ་ཆེ་གྲགས་པ་རྒྱལ་མཚན་ལ་བསྟོད་པ་ཡོན་ཏན་རྒྱ་མཚོ་མ །།", + "text_id": "79033537-f91c-4186-a35c-e4a69e94c45c", + "image_url": null, + "first_segment": { + "id": "e15e51a8-3397-4950-9959-dc6318707906", + "content": "བླ་མ་དང་འཇམ་པའི་དབྱངས་ལ་ཕྱག་འཚལ་ལོ།།\nསྲིད་པའི་འཁྲི་ཤིང་དྲྭ་བ་བྲལ་མཛད་ཅིང་། །\nཡོན་ཏན་རྒྱ་མཚོ་ཞིང་ལམ་མཆོག་སྒྲོགས་པ། །" + } + }, + { + "title": "རྗེ་བསོད་ནམས་རྩེ་མོ་ལ་བསྟོད་པ། །", + "text_id": "a0eb3433-6ab9-4b2c-aba3-7430640bbc70", + "image_url": null, + "first_segment": { + "id": "595c51c1-13d9-467d-a977-ba89cd3a4551", + "content": "བླ་མ་དམ་པ་རྣམས་ལ་ཕྱག་འཚལ་ལོ།། ༈\nཁྱོད་ཉིད་དམ་པ་རྣམས་ཀྱི་རིགས་འཁྲུངས་ནས། །\nའགྲོ་ཀུན་དགའ་བའི་དཔལ་གྱིས་ཉེར་འཚོ་བ། །" + } + }, + { + "title": "ལྷ་མོའི་ཡན་ལག་བདུན་པ་དཔག་དཀའ་མ། །", + "text_id": "3b3924c1-3655-4a1f-9db1-0680b2f7373a", + "image_url": null, + "first_segment": { + "id": "f5b6f3b0-563c-4b01-af4a-256511ef5f0c", + "content": "༈ བྷྱོཿ དཔལ་ལྡན་ལྷ་མོ་དཔག་པར་དཀའ་བ་ཁྱོད།།\nཐབས་ཀྱི་ཆོ་འཕྲུལ་ནམ་མཁའི་མཐའ་ལ་སྤྱོད། །\nབྱང་ཆུབ་སེམས་དཔའི་སྤྲུལ་པ་ཁྱོད་ཉིད་ལ། །" + } + }, + { + "title": "མགོན་པོ་ཕྱག་དྲུག་པའི་བསྟོད་པ་མྱུར་མཛད་མ།", + "text_id": "91924b19-73bf-42ed-9032-460e5a9ebe1a", + "image_url": null, + "first_segment": { + "id": "80643d23-c0fa-47e3-a651-354efe5066a8", + "content": "༈ ༑ མྱུར་མཛད་སྤྱན་རས་གཟིགས་ལ་ཕྱག་འཚལ་ལོ༑ ༑\nཞབས་གདུབ་དང་བཅས་བི་ནཱ་ཡ་ཀ་མནན། །\nནག་ པོ་ཆེན་པོ་སྟག་གི་ཤམ་ཐབས་ཅན། །" + } + }, + { + "title": "༄༅། །མཁའ་འགྲོའི་རྒྱལ་མོ་ཡེ་ཤེས་མཚོ་རྒྱལ་ལ་གསོལ་བ་འདེབས་པ་དད་པའི་གདུང་དབྱངས་ཞེས་བྱ་བ།", + "text_id": "45cfce30-e90e-4756-bb1e-6941155433fc", + "image_url": null, + "first_segment": { + "id": "7f839e1b-d403-40f3-a4a0-c33da3da9275", + "content": "ཨེ་མ་ཧོ། རྒྱལ་བ་ཀུན་ཡུམ་རྡོ་རྗེ་རྣལ་འབྱོར་མ། །\nསྒྲ་དབྱངས་རྒྱ་མཚོའི་མངའ་བདག་དབྱངས་ཅན་མ། །\nཐུགས་རྗེས་འགྲོ་ཀུན་སྒྲོལ་མཛད་རྗེ་བཙུན་མ། །" + } + }, + { + "title": "༄༅། །པདྨ་མཁའ་འགྲོའི་རིགས་བྱེད་རྩལ་གྱི་བསང་མཆོད་པདྨའི་དྲྭ་བ་བཞུགས་སོ།།", + "text_id": "adb2df66-52b5-4c11-b60a-53759bfabac5", + "image_url": null, + "first_segment": { + "id": "39d38665-3747-4223-94b9-49e53905e55d", + "content": "(དྲི་བཟང་གི་ཤིང་དང་འོ་སྐམ་སྨན་སྣ་འབྲུ་སྣ་སོགས་གཙང་མ་མེར་བསྲེག་པ་ཆབ་ཀྱིས་བྲན་ལ།)རྃ་ཡྃ་ཁྃ། སྟོང་པའི་ངང་ལས་ལྷ་རྫས་བསང་གི་དུད་སྤྲིན་ཟག་མེད་བདེ་བ་ཆེན་པོ་འདོད་ཡོན་རྣམ་པ་ཐམས་ཅད་པར་འཆར་བའི་ཀུན་ཏུ་བཟང་པོའི་མཆོད་པའི་སྤྲིན་ཆེན་པོས་ནམ་མཁའི་ཁམས་གང་བར་གྱུར།\nཨོཾ་ཨཱཿཧཱུྃ་ཧོཿ(ཞེས་ལན་གསུམ་བརྗོད་པས་བྱིན་གྱིས་བརླབས་ལ།)\nཧྲཱིཿ རང་བཞིན་འོད་གསལ་དག་པའི་དབྱིངས་ཉིད་ལས། །" + } + }, + { + "title": "༄༅། །རྗེ་བཙུན་སྒྲོལ་དཀར་གྱི་བསྟོད་པ་མཁྱེན་བརྩེ་དྲི་མེད་མ།", + "text_id": "0fdc7114-4548-4700-8954-a7a374b180e2", + "image_url": null, + "first_segment": { + "id": "dc020e7c-4af8-4318-b866-96f4e7d301f4", + "content": "༄༅། །ན་མོ་ཨཱཪྻ་ཏཱ་ར་ཡེ། །\nམཁྱེན་བརྩེ་དྲི་མེད་རྒྱས་པའི་རང་བཞིན་གྱི། །\nཔད་དཀར་བསིལ་ཟེར་ཅན་གྱི་གདན་སྟེང་ན། །" + } + }, + { + "title": "མཁས་གྲུབ་རྗེའི་བསྟོད་པ།", + "text_id": "2681e527-3d9d-4fc5-9f13-8aee0b06e76c", + "image_url": null, + "first_segment": { + "id": "723432c8-8ebb-42e0-8658-067c191ada39", + "content": "མཁས་གྲུབ་རྗེའི་བསྟོད་པ།\nལྷ་དང་བཅས་པའི་འཇིག་རྟེན་ཐམས་ཅད་ལ། །\nཆོས་ཀྱི་སྒོ་མོ་བརྒྱད་ཁྲི་བཞི་སྟོང་གི །" + } + }, + { + "title": "མགོན་པོ་འོད་དཔག་མེད་ཀྱི་བསྟོད་པ་ཞིང་མཆོག་སྒོ་འབྱེད།", + "text_id": "d300b094-b362-41e6-b552-d98cecb38c80", + "image_url": null, + "first_segment": { + "id": "4880c5f9-66a2-452d-9700-1ae8473e8f8b", + "content": "ན་མཿཤྲཱི་གུ་རུ་མཉྫུ་གྷོ་ཥཱ་ཡ།\nབདེ་གཤེགས་ཀུན་གྱིས་བསྔགས་པའི་བདེ་བ་ཅན། །\nརྣམ་དག་ཞིང་གི་དབང་ཕྱུག་བཅོམ་ལྡན་འདས། །" + } + }, + { + "title": "ལྷ་མོའི་འཕྲིན་བསྐུལ་སེམས་ཉིད་འཕྲིན་ལས་མ།", + "text_id": "95c30832-a64b-4f42-a6d8-a4a221da8796", + "image_url": null, + "first_segment": { + "id": "90c17d55-6328-4b5a-825a-c599bc471dfc", + "content": "༈ བྷྱོཿ སེམས་ཉིད་འཕྲིན་ལས་རྣམ་བཞིའི་ཁྱད་པར་ནི།།\nསེམས་ཉིད་གུད་ན་མེད་ཅིང་སེམས་ཀྱང་མེད། །\nདོན་དམ་དབྱེར་མེད་ཁ་དོག་གཟུགས་ཀྱང་མེད། །" + } + }, + { + "title": "༄༅། །བསྟན་སྐྱོང་རྡོ་རྗེ་དྲག་རྒྱལ་མའི་གཏོར་འབུལ་ཞེས་བྱ་བ་བཞུགས་སོ། །", + "text_id": "7c4609db-244f-445e-b4f9-264369d72c66", + "image_url": null, + "first_segment": { + "id": "3bb09675-59ce-47ce-82b4-aab4cf696bf7", + "content": "ཨོཾ༑ གངས་རིས་བསྐོར་བའི་བོད་ཡུལ་རྡོ་རྗེ་གདན། །\nདཔལ་ལྡན་འབྲས་སྤུངས་མཁས་གྲུབ་འདུས་སྡེ་ཆེའི། །\nརྒྱབ་རི་ལྷུན་ཆགས་མཛེས་སྡུག་ཕོ་བྲང་ནས། །" + } + }, + { + "title": "༄༅། །སྟོན་པའི་མཛད་བཅུའི་བསྟོད་པ་ཡོངས་འདུའི་སྙེ་མ་ཞེས་བྱ་བ་བཞུགས་སོ། །", + "text_id": "11cc4e69-1705-4d7c-b4b4-a40bfd4ee22e", + "image_url": null, + "first_segment": { + "id": "92b9c423-c8bb-4883-a863-8667fb6f8e4e", + "content": "སྭསྟི་སིདྡྷཾ།ཐུབ་རྣམས་སྤངས་རྟོགས་ཡོན་ཏན་རབ་མ་ཉམ་ཡང།།\nགདུལ་དཀའ་འགྲོ་ལ་ཉེར་བརྩེའི་སྙིང་སྟོབས་ལ།།\nཕྱོགས་བཅུའི་རྒྱལ་ཀུན་མགྲིན་གཅིག་བསྔགས་པའི་ཡུལ།།" + } + }, + { + "title": "༄༅། །མར་མེའི་སྨོན་ལམ།", + "text_id": "30e67433-19dc-4a96-abb4-64a00e11bc27", + "image_url": null, + "first_segment": { + "id": "5bef1cc1-7c7e-4690-a823-025aa073b8d7", + "content": "འོད་ཀྱིས་སྟོང་གསུམ་གསལ་བར་ནུས་པ་ཡི། །\nལྷ་རྫས་མར་མེའི་ཕྲེང་བ་དཔག་མེད་ཀྱིས། །\nརབ་འབྱམས་ཕྱོགས་བཅུའི་ཞིང་ན་བཞུགས་པ་ཡི། །" + } + }, + { + "title": "མི་ཁ་བཟློག་གཟུངས་མདོར་བསྡུས།", + "text_id": "1cabd5b6-7231-4468-aa94-88f59855c66a", + "image_url": null, + "first_segment": { + "id": "1ec0f36e-2b74-4cb2-b4c8-7d887a4d2ecc", + "content": "༄༅། །མི་ཁ་བཟློག་གཟུངས་མདོར་བསྡུས་བཞུགས་སོ། །\nརང་ཉིད་ཐུགས་རྗེ་ཆེན་པོའི་ཐུགས་ཀ་ནས་འོད་ཟེར་ལྗང་གུ་འཕྲོས་པས་གཏམ་ངན་མི་ཁ་ཐམས་ཅད་བཟློག་པར་བསམ། ཨོཾ་མ་ཎི་པདྨེ་ཧཱུྃ་ཧྲཱིཿ ཧུར་ཐུཾ་མི་ཁ་གཏམ་ངན་ཐམས་ཅད་ཟློག། རི་ལི་རི་ལི། ཨོཾ་མ་ཧཱ་དྷེ་ལ་དགྲ་བོ་ཎྲྀ་ཏྲི་ནི་སྲོག་ལ་རྦད་ཆོད། བྷྱོ་མི་ཁ་ངན་པ་བསྒྱུར། མི་ཁ་ངན་པ་དགྲ་བོ་མཱ་ར་ཡ། ཐུཾ་ཐུཾ། ཤིག་ཤིག། རྦད་རྦད། དྲིལ་དྲིལ། བྷྱོ་བྷྱོ། ཟློག་ཟློག། བརྒྱ་ཡི་མི་ཁ་ཟློག། སྟོང་གི་ཁ་མཆུ་ཟློག། བརྒྱ་ཁ་ཡེར་ཡེར་ཟློག། སྟོང་ཁ་མེར་མེར་ཟློག། ནད་སྣ་ཟློག། གདོན་སྣ་ཟློག། ཡམས་སྣ་ཟློག དགྲ་སྣ་ཟློག། གྱོད་སྣ་ཟློག། རྐུན་སྣ་ཟློག། དམག་སྣ་ཟློག། ཉི་མ་ཕྱོགས་བཞི་མཚམས་ཀྱི་མི་ཁ་གཏམ་ངན་ཐམས་ཅད་ཟློག། ངན་སེམས་ཚུར་ལ་འཆང་བའི་དགྲ་བོ་ཐམས་ཅད་རྦད་རྦད། དྲིལ་དྲིལ། ཐུཾ་ཐུཾ། ཤིག་ཤིག བྷྱོ་བྷྱོ། ཟློག་ཟློག། བྷྱོ་ཟློག་ཅིག།(ཅེས་པ་འདི་ཀརྨ་རཱ་ག་ཨ་སྱས་སྦྱར་བའོ)།།" + } + }, + { + "title": "༄༅། །བཅོམ་ལྡན་འདས་མི་འཁྲུགས་པའི་ཞིང་བཀོད་དོན་བསྡུས་ཀྱི་སྨོན་ལམ་རིན་པོ་ཆེའི་ཕྲེང་བ་བཞུགས་སོ། །", + "text_id": "666c59c0-362e-4a67-8d28-a1736a149687", + "image_url": null, + "first_segment": { + "id": "130fe36d-c56a-430d-ad2b-00918087ffa9", + "content": "མངོན་དགའི་ཞིང་ན་བྱང་ཆུབ་ཤིང་དབང་དྲུང་། །མ\nི་འཁྲུགས་རྒྱལ་བ་དཔལ་གྱིས་ལྷམ་མེར་བཞུགས། །ཉ\nན་ཐོས་བྱང་ཆུབ་སེམས་དཔའ་འཁོར་བཅས་ལ། །ལ" + } + }, + { + "title": "༄༅། །ལུམྦི་ནཱི་ལ་བསྟོད་པ་པདྨ་དཀར་པོ།", + "text_id": "4eb4054e-ff88-4dc3-aae5-b4aed2b3184e", + "image_url": null, + "first_segment": { + "id": "db772098-75de-4c11-b206-35ff2c26f2b5", + "content": "ཨོཾ་སྭསྟི།\nཀུན་ཏུ་སྙན་པའི་བ་དན་ཆེ། །\nསྲིད་པ་གསུམ་ན་རབ་སྒྲེངས་པ། །" + } + }, + { + "title": "༄༅། །མི་ཁའི་བཟློག་བསྒྱུར། །", + "text_id": "34c6e88c-c16d-48e6-bccd-4f4f8a9a1ee8", + "image_url": null, + "first_segment": { + "id": "d65ab60d-f043-492d-a37b-9eaaa54cb6e2", + "content": "༈ ཨོཾ་ཨཱཿཧཱུྃ་བཛྲ་གུ་རུ་པདྨ་སིདྡྷི་ཧཱུྃ། ཧཱུྃ་ཧཱུྃ་བློ་སྦྱོ་བཟློག་བཟློག །\nམི་ཁའི་བུ་མོ་ཤ་ཟ་མ། །\nམི་ཁའི་བུ་མོ་ཁྲག་འཐུང་མ༑ ༑" + } + }, + { + "title": "༄༅། །ཚེས་བཅུའི་ཕན་ཡོན་གསོལ་འདེབས་བཞུགས།", + "text_id": "a180373c-1534-43a0-93af-05d11c832802", + "image_url": null, + "first_segment": { + "id": "2c8a77e9-7cd5-4bcb-a2ea-bbf7a43cd607", + "content": "༄༅།སངསརྒྱས་གཉིས་པ་སློབ་དཔོན་ཆེན་པོ་ལ་རྣམ་པ་ཀུན་ཏུ་ཕྱག་འཚལ་ཞིང་སྐྱབས་སུ་མཆིའོ། །\nབསྐལ་བཟང་འདིར་བྱོན་སངས་རྒྱས་སྟོང་རྩ་ཀུན། །\nསྤངས་རྟོགས་མཉམ་ཞིང་འགྲོ་ལ་ཐུགས་བརྩེ་ཡང་། །" + } + }, + { + "title": "༄༅། །ཚོགས་མཆོད་ཤིན་ཏུ་བསྡུས་པ་བཞུགས་སོ། །", + "text_id": "677d88c4-c072-4a6f-b8ff-154c60e85e45", + "image_url": null, + "first_segment": { + "id": "94ab4c28-607a-49bd-9c87-6c5aee7eab68", + "content": "(གང་ཞིག་གསང་སྔགས་ཀྱི་ཆོ་ག་སོགས་དང་མ་འབྲེལ་བར་ཚོགས་མཆོད་མདོར་བསྡུས་བྱ་བར་འདོད་ན། ཚོགས་ཀྱི་དངོས་པོ་གང་ཡོད་རྣམས་བཤམས་ལ་ཆུ་གཙང་འཐོར་ནས། )\nཨོྂ་ཨཱཿཧཱུྂ། སྣང་སྲིད་སྣོད་བཅུད་བདག་ལུས་ཕུང་པོ་ཁམས། །\nསྐྱེ་མཆེད་ཐམས་ཅད་ཡེ་ནས་ཆོས་ཀྱི་དབྱིངས། །" + } + }, + { + "title": "ཨོ་རྒྱན་རིན་པོ་ཆེའི་ཞལ་ཆེམས་གསོལ་འདེབས་བཞུགས༔", + "text_id": "c37013a9-5b66-4d92-bcc5-8e504edf9602", + "image_url": null, + "first_segment": { + "id": "c649a2a4-13eb-4475-99a1-4f2516450055", + "content": "ཨེ་མ་ཧོཿ ཆོས་སྐུ་ཀུན་བཟང་དྲུག་པ་རྡོ་རྗེ་འཆང་༔\nསྟོན་པ་རྡོར་སེམས་བཅོམ་ལྡན་ཤཱཀྱའི་རྒྱལ༔\nམགོན་པོ་ཚེ་དཔག་མེད་དང་སྤྱན་རས་གཟིགས༔" + } + }, + { + "title": "དཔལ་ཡེ་ཤེས་ཡོན་ཏན་བཟང་པོ་ཞེས་བྱ་བའི་བསྟོད་པ།", + "text_id": "df5c0f5a-444c-473e-b1c1-4b4d1e091548", + "image_url": null, + "first_segment": { + "id": "115d7065-ec83-426f-a83b-961695786d57", + "content": "༄༅། །རྒྱ་གར་སྐད་དུ། ཤྲཱི་ཛྙཱ་ན་གུ་ཎ་བྷ་དྲ་ནཱ་མ་སྟུ་སྟི། བོད་སྐད་དུ། དཔལ་ཡེ་ཤེས་ཡོན་ཏན་བཟང་པོ་ཞེས་བྱ་བའི་བསྟོད་པ།\nབཅོམ་ལྡན་འདས་འཇམ་དཔལ་དབྱངས་ལ་ཕྱག་འཚལ་ལོ། །\nགང་གི་བློ་གྲོས་སྒྲིབ་གཉིས་སྤྲིན་བྲལ་ཉི་ལྟར་རྣམ་དག་རབ་གསལ་བས། །" + } + }, + { + "title": "༄༅། །མཁས་གྲུབ་རཱ་ག་ཨ་སྱས་མཛད་པའི་འཇམ་དཔལ་གྱི་སྒྲུབ་ཐབས་རྨོངས་པ་མུན་སེལ་བླ་མ་པདྨ་ཕྲིན་ལས་ལ་སྩལ་བ་བཞུགས་སོ།", + "text_id": "2a112194-f034-497f-bf30-1079f350c3c1", + "image_url": null, + "first_segment": { + "id": "177f71fb-ef19-40da-b2ae-cf6f027c0e93", + "content": "ན་མོ། དཀོན་མཆོག་གསུམ་འདུས་བླ་མ་འཇམ་དབྱངས་ལ། །\nདུས་རྣམས་རྟག་ཏུ་བདག་ནི་སྐྱབས་སུ་མཆིའོ།། །\nའགྲོ་བའི་དོན་དུ་བཅོམ་ལྡན་འཇམ་དཔལ་བསྒྲུབ། །" + } + }, + { + "title": "༄༅། །ཇ་མཆོད་འདྲེན་པ་མཉམ་མེད།", + "text_id": "301b2d47-f0ee-4d29-ba3b-2296bdd41a0e", + "image_url": null, + "first_segment": { + "id": "2e4a2089-b33c-4ebf-9070-0f68d1405dc1", + "content": "༄༅། །འདྲེན་པ་མཉམ་མེད་ཟས་གཙང་སྲས།\n།དེ་སྲས་མཁྱེན་དང་བརྩེ་བའི་བདག །\nའཇམ་པའི་དབྱངས་དང་མི་ཕམ་མགོན། །" + } + }, + { + "title": "༄༅། །འཇམ་མགོན་ས་སྐྱ་པཎྜི་ཏའི་བླ་མའི་རྣལ་འབྱོར་ཉམས་སུ་ལེན་ཚུལ་མཁྱེན་བརྩེ་ནུས་མཐུའི་ཆར་འབེབས་ཞེས་བྱ་བ་བཞུགས་སོ།།", + "text_id": "bf0f8db0-f07f-41e1-97a8-1297486fd3d4", + "image_url": null, + "first_segment": { + "id": "29307cbb-222f-40f5-97f3-2a2ba9969930", + "content": "༄༅། །འཇམ་མགོན་ས་སྐྱ་པཎྜི་ཏའི་བླ་མའི་རྣལ་འབྱོར་ཉམས་སུ་ལེན་ཚུལ་མཁྱེན་བརྩེ་ནུས་མཐུའི་ཆར་འབེབས་ཞེས་བྱ་བ་བཞུགས་སོ།།\nབླ་མ་དང་མགོན་པོ་འཇམ་པའི་དབྱངས་ལ་ཕྱག་འཚལ་ལོ། ། འདིར་འཇམ་མགོན་ས་སྐྱ་པཎྜི་ཏའི་སྒོ་ནས་འཇམ་དབྱངས་བླ་མའི་རྣམ་འབྱོར་ཉམས་སུ་ལེན་པར་འདོད་པས་བདེ་བའི་སྟན་ལ་བསམ་གཏན་གྱི་སྤྱོད་ལམ་གྱིས་འདུག་ལ། མདུན་གྱི་ནམ་མཁར་པད་ཟླའི་གདན་ལ་ས་སྐྱ་པཎྜི་ཏའི་ངོ་བོར་གྱུར་པའི་འཇམ་དབྱངས་དམར་སེར་ཆོས་འཆད་ཨུཏྤལ་གླེགས་རལ་ཅན་དར་དང་རིན་པོ་ཆེས་བརྒྱན་པ་རྡོར་སྐྱིལ་གྱིས་བཞུགས་པའི་སྐུ་དགེ་འདུན། གསུང་དམ་ཆོས། ཐུགས་བླ་མ་དང་སངས་རྒྱས་སུ་མོས་པའི་དྲུང་བདག་དང་མར་གྱུར་སེམས་ཅན་རྣམས་འཁོད་ནས་འཇིགས་དད་སྙིང་རྗེ་གསུམ་གྱི་ཀུན་ནས་བླངས་ཏེ་བྱང་ཆུབ་མ་ཐོབ་ཀྱི་བར་དུ་སྐྱབས་སུ་འགྲོ་བར་བསམ་པས། ངག་ཏུ་བདག་དང་འགྲོ་བ་སོགས་དང་། དཔལ་ལྡན་བླ་མ་དམ་པ་རྣམས་ལ་སྐྱབས་སུ་མཆིའོ་ནས། དགེ་འདུན་རྣམས་ལ་སྐྱབས་སུ་མཆིའོ་འདི་བར་གསུམ་སོགས་ཅི་ནུས་བརྗོད།\nཐལ་མོ་སྦྱར་ནས་བླ་མ་དང་དཀོན་མཆོག་རིན་པོ་ཆེ་ཞེས་སོགས་བརྗོད་པས་སྐྱབས་ཡུལ་དེའི་ཐུགས་ཀ་ནས་འོད་ཟེར་འཕྲོས་སྐྱབས་སུ་འགྲོ་བ་པོ་རྣམས་ལ་ཕོག་པས་སྡིག་སྒྲིབ་དགསངས་རྒྱས་སུ་གྱུར་ནས་རང་རང་གི་ཞིང་དུ་གཤེགས། སྐྱབས་ཡུལ་རང་ལ་ཐིམ་པའམ་མི་དམིགས་པའི་ངང་དུ་བཞག སེམས་ཅན་ཐམས་ཅད་ཀྱི་དོན་དུ་རྫོགས་པའི་སངས་རྒྱས་ཀྱི་གོ་འཕང་ཐོབ་པར་བྱ། དེའི་ཆེད་དུ་འཇམ་དབྱངས་བླ་མའི་རྣལ་འབྱོར་ཉམས་སུ་བླང་བར་བགྱིའོ། །" + } + }, + { + "title": "འགྲོ་ལ་བདེ་སྐྱིད་མ་བཞུགས་སོ།།", + "text_id": "a7360342-e64d-43ca-882d-075c67471fa7", + "image_url": null, + "first_segment": { + "id": "e6b3a604-a2da-46b5-82b9-a25033c4f269", + "content": "འགྲོ་ལ་བདེ་སྐྱིད་འབྱུང་བའི་སྒོ་གཅིག་པུ།།\nཀུན་མཁྱེན་རྒྱལ་བའི་བསྟན་པ་རིན་པོ་ཆེ།།\nཡུལ་དུས་གནས་སྐབས་ཀུན་ཏུ་མི་ཉམས་པར།།" + } + }, + { + "title": "༄༅།། རྗེ་རིན་པོ་ཆེའི་བསྟན་པ་དང་མཇལ་བའི་སྨོན་ལམ་མདོར་བསྡུས་མཁྱེན་བརྩེའི་འདྲེན་ལྡན་ཞེས་བྱ་བ་བཞུགས་སོ།།", + "text_id": "6553dea6-3aeb-485f-bd87-c0624a89fe49", + "image_url": null, + "first_segment": { + "id": "b06b5ae6-b470-4e1d-82d1-0e969b7d947c", + "content": "༈ ན་མོ་མཉྫ་གྷོ་ཥཱ་ཡ།\nརབ་འབྱམས་རྒྱལ་བ་ཀུན་གྱི་ མཁྱེན་བརྩེའི་བཅུད། །\nགཅིག་ཏུ་བསྡུས་པ་རྗེ་བཙུན་འཇམ་དཔལ་དབྱངས། །" + } + }, + { + "title": "༄༅། །བློ་བཟང་རྒྱལ་བསྟན་མ་བཞུགས་སོ།།", + "text_id": "e33f18c0-697f-4c37-b2d7-1183241e83e2", + "image_url": null, + "first_segment": { + "id": "bb9056fe-8967-42b9-bde5-fedb31cc3679", + "content": "༈ རྒྱལ་བ་མ་ལུས་སྐྱེད་པའི་ཡབ་གྱུར་ཀྱང་། །\nརྒྱལ་སྲས་ ཚུལ་གྱིས་ཞིང་ཁམས་རབ་འབྱམས་སུ། །\nརྒྱལ་བའི་ཆོས་ འཛིན་ཐུགས་བསྐྱེད་བདེན་པའི་མཐུས། །" + } + }, + { + "title": "རྒྱན་དྲུག་མཆོག་གཉིས་དང་བསྟན་པའི་རྒྱུན་བཞིར་བསྟོད་པ།", + "text_id": "53216ff6-2f88-4efa-a916-e6b4497e0fa7", + "image_url": null, + "first_segment": { + "id": "8fe9d693-1b13-4a35-9a70-f18d903bb9be", + "content": "འཕགས་ཡུལ་གྱི་རྒྱན་དྲུག་མཆོག་གཉིས་དང་གངས་ཅན་བསྟན་པའི་རྒྱན་བཞི་ལ་བསྟོད་པ་ནི།\nཚོགས་གཉིས་ཆུ་གཏེར་གཏིང་མཐའ་ཡས། །\nརྣམ་དག་ཡོན་ཏན་ནོར་བུས་གང་། །" + } + }, + { + "title": "༄༅། །འཕགས་པ་བཀྲ་ཤིས་བརྒྱད་པའི་ཚིགས་སུ་བཅད་པ་བཞུགས་སོ།།", + "text_id": "0b2806d1-ab41-4cec-9fcb-0c02c61c0711", + "image_url": null, + "first_segment": { + "id": "625b36ad-9d11-467f-a6d8-92839a3b82b0", + "content": "(༄༅། །ལས་གང་ཞིག་རྩོམ་པ་ན་ཐོག་མར་འདི་ཚར་གཅིག་བརྗོད་ན་གྲུབ་པ་བདེ་བ་ཡིད་བཞིན་དུ་བྱེད་པར་འགྱུར་བས་ཅི་ནས་ཡིད་ལ་བྱའོ།།)\nཨོྃ་སྣང་སྲིད་རྣམ་དག་རང་བཞིན་ལྷུན་གྲུབ་པའི། །\nབཀྲ་ཤིས་ཕྱོགས་བཅུའི་ཞིང་ན་བཞུགས་པ་ཡི། །" + } + }, + { + "title": "རྗེ་བཙུན་འདན་མ་གྲུབ་ཐོབ་ལ་གསོལ་བ་འདེབས་པ།", + "text_id": "f53da554-b65d-4f6d-947c-f93bd2e89316", + "image_url": null, + "first_segment": { + "id": "01575ff6-e1b9-4e5d-b8c6-4f148a63e5eb", + "content": "རྙེད་བཀུར་ཆོས་བརྒྱད་གྲགས་པའི་རོ་རྣམས་ལ། །\nརི་དྭགས་དགྲ་ལ་འཇིགས་བཞིན་རབ་ཏུ་འབྲོས། །\nམནོག་ཆུང་ཚེ་འདིས་བསླུ་བར་མ་ནུས་པའི། །" + } + }, + { + "title": "རྒྱལ་བ་རྒྱ་མཚོ་ལྷ་ལྔའི་གསོལ་འདེབས།", + "text_id": "17f308ab-dcd6-4d3d-873f-a76b46318ef3", + "image_url": null, + "first_segment": { + "id": "98e23ec0-2469-48c7-8c37-f9b8b226a6b0", + "content": "རྒྱལ་བ་རྒྱ་མཚོ་ལྷ་ལྔའི་གསོལ་འདེབས།\nརྒྱལ་བ་རྒྱ་མཚོ་ལྷ་ལྔའི་གསོལ་འདེབས་ནི།\nགནས་སྤྱི་གཙུག་པད་ཟླའི་གདན་སྟེང་ན། །" + } + } + ], + "skip": 0, + "limit": 197, + "total": 197 +} \ No newline at end of file diff --git a/pecha_api/recitations/order/order_recitations_en.json b/pecha_api/recitations/order/order_recitations_en.json new file mode 100644 index 000000000..a2161f39e --- /dev/null +++ b/pecha_api/recitations/order/order_recitations_en.json @@ -0,0 +1,196 @@ +{ + "recitations": [ + { + "title": "Refuge and Bodhichitta", + "text_id": "13b9c0af-4b51-4330-8388-8b22b664fd73", + "image_url": null, + "first_segment": { + "id": "d5b72145-7ebe-4a1c-939f-a853615b0028", + "content": "In the Buddha, Dharma and Sangha, Until enlightenment is reached, I take refuge,\nBy my generosity and other virtuous deeds, May all sentient beings attain Buddhahood." + } + }, + { + "title": "Four Boundless Thoughts Prayer", + "text_id": "0775e939-5752-4c79-9341-e9986847d549", + "image_url": null, + "first_segment": { + "id": "d38eb347-5bfe-4672-8a9b-72194cac2d1e", + "content": "Four Boundless Thoughts Prayer\nMay all sentient beings enjoy happiness and the cause of happiness.\nMay they be free from suffering and the cause of suffering." + } + }, + { + "title": "The Seven Preliminaries for Purifying the Mind", + "text_id": "dfb98b3d-0745-4972-ac3f-0da3d1c7c134", + "image_url": null, + "first_segment": { + "id": "cbe6de0b-fb39-4547-ac58-dd00e0bfba27", + "content": "The Seven Preliminaries for Purifying the Mind\nTo all the buddhas, the lions of the human race, In all directions of the universe, through past and present and future: To every single one of you, I bow in homage; Devotion fills my body, speech and mind.\nThrough the power of this prayer, aspiring to Good Action, All the victorious ones appear, vivid here before my mind, And I multiply my body as many times as atoms in the universe, Each one bowing in prostration to all the buddhas." + } + }, + { + "title": "The Sūtra of the Heart of Transcendent Wisdom", + "text_id": "6d9a82b9-8d9d-4b71-830e-b80538d3cf54", + "image_url": null, + "first_segment": { + "id": "1a13517f-64fb-4b88-aa84-e35d6a27a949", + "content": "The Sūtra of the Heart of Transcendent Wisdom\nIn the language of India: Bhagavatī prajñāpāramitā hṛdaya In the language of Tibet: Chomden dema sherab kyi parol tu chinpé nyingpo In the English language:The Blessed Mother, the Heart of the Transcendent Perfection of Wisdom\nIn a single segment." + } + }, + { + "title": "Praise to Bhagavatī Tārā", + "text_id": "6df6cd46-2b03-40c7-b312-f71bf809ffa3", + "image_url": null, + "first_segment": { + "id": "2bcb5a38-fa9c-442a-bd9e-569b82acbcce", + "content": "In the language of India: Tārā-namas-kāraika-viṃśati-stotra-guṇa-hita-sahita\nOṃ. Homage to the noble lady Tārā!\nHomage to Tārā, swift and valiant, Whose glance flashes like flares of lightning; Born on the heart of a blossoming lotus, That rose from the tears of the Triple World’s Lord." + } + }, + { + "title": "The King of Aspiration Prayers", + "text_id": "9bccef29-44f6-4e2d-8926-52a24751d63a", + "image_url": null, + "first_segment": { + "id": "17aa78f5-ef28-4190-8db5-fbf018f43edc", + "content": "In the language of India:Ārya-Bhadracaryā-Praṇidhāna-Rāja. In the language of Tibet:Pakpa Zangpo Chöpé Mönlam gyi Gyalpo. In the English language:The King of Aspiration Prayers: Samantabhadra’s “Aspiration to Good Actions”.\nHomage to Mañjuśrī, the youthful!\nTo all the buddhas, the lions of the human race, In all directions of the universe, through past and present and future: To every single one of you, I bow in homage; Devotion fills my body, speech and mind." + } + }, + { + "title": "The Bodhisattva’s Confession of Downfalls", + "text_id": "f28314f5-dd6f-4a88-8f52-402fcef82494", + "image_url": null, + "first_segment": { + "id": "f53b2ffb-bf5a-4398-bf7d-e10fad5f4d44", + "content": "The Bodhisattva’s Confession of Downfalls\nNamo! I with this name (your name)\nTake refuge in the Guru; Take refuge in the Buddha; Take refuge in the Dharma; and Take refuge in the Saṅgha." + } + }, + { + "title": "The Praise to Mañjuśrī: Glorious Wisdom’s Excellent Qualities", + "text_id": "8a854f75-ceb2-4ffd-9821-8260236bdf05", + "image_url": null, + "first_segment": { + "id": "b853006c-ac8f-460c-9dc1-addd19a9838f", + "content": "The Praise to Mañjuśrī: Glorious Wisdom’s Excellent Qualities\nHomage to the Lord Mañjughoṣa!\nYour wisdom is brilliant and pure like the sun, free from the clouds of the two obscurations. You perceive the whole of reality, exactly as it is, and so hold the book of Transcendental Wisdom at your heart. You look upon all beings imprisoned within saṃsāra, enshrouded by the thick darkness of ignorance and tormented by suffering, With the love of a mother for her only child. Your enlightened speech, endowed with sixty melodious tones," + } + }, + { + "title": "A Praise to the Exalted Avalokiteśvara", + "text_id": "2c2b723f-f13c-49cd-b607-049aca1c756a", + "image_url": null, + "first_segment": { + "id": "f205393f-4b07-4043-a398-cde28eff2153", + "content": "A Praise to the Exalted Avalokiteśvara\nNamo Ārya Lokeśvaraya!\nDeep as ocean is the compassion of the Victors (Buddhas) of the ten directions, Equal to particles of the infinite realms, who are passionate and ever compassionate of the wandering beings. From that [ocean] You were born, renowned as, “Eyes Looking [with Compassion]” (Avalokita, Chenrezig), all attributes spontaneously complete; O Protector, worthy of devotion by us all, [towering] like a jewel mountain, I bow to You!" + } + }, + { + "title": "Maitreya's Aspiration", + "text_id": "7e0e7d86-3fc5-4481-b46d-5281a60323ef", + "image_url": null, + "first_segment": { + "id": "edb7489e-c86b-47ea-a9f8-420cc9e766cc", + "content": "In the language of India:ārya-maitrī-praṇidhānarāja, In the language of Tibet:pakpa champé mönlam gyi gyalpo, In the English language:The Noble Maitreya’s King of Aspirations\nHomage to all buddhas and bodhisattvas!\nĀnanda, in the past, when the bodhisattva mahāsattva Maitreya was engaged in the bodhisattva practices, he would drape his upper robes over one shoulder, bend his right knee to the ground, and join his palms together, three times a day and three times a night. He would then concentrate on all the buddhas and make the following aspiration:" + } + }, + { + "title": "Seven-Line Prayer", + "text_id": "10a2d79e-a171-4a83-94ef-665bbb1f5cd5", + "image_url": null, + "first_segment": { + "id": "032446f9-8749-4c1e-b564-f1cddfb1edec", + "content": "Seven-Line Prayer\nHūṃ! In the north-west of the land of Oḍḍiyāna\nIn the heart of a lotus flower," + } + }, + { + "title": "The Prayer Which Removes All Obstacles from the Path", + "text_id": "2ed99168-9887-4306-9320-1a2d9a725894", + "image_url": null, + "first_segment": { + "id": "ae6f0e9f-5fd0-4a2f-bc08-a8fee6dbc1dc", + "content": "The Prayer Which Removes All Obstacles from the Path\noṃ āh hūṃ vajra guru padma siddhi hūṃ To the dharmakāya Amitābha we pray! To the saṃbhogakāya—the Great Compassionate One—we pray! To the nirmāṇakāya Padmākara we pray! Wondrous emanation, master of mine, In India, you were born, you studied and you contemplated; To the heart of Tibet you came, to subjugate its arrogant demons, In Orgyen you dwell, accomplishing the benefit of beings: With your compassion, inspire us with your blessing! With your love, guide us and others along the path! With your realization, grant us attainments! With your power, dispel the obstacles facing us all! Outer obstacles—dispel them externally, Inner obstacles—dispel them internally, Secret obstacles—dispel them into space! In devotion, I pay homage and take refuge in you! When we gaze on the wonder of your perfect form, Your right hand forms the mudrā of the sword, Your left in the mudrā of summoning. Your mouth held open, with teeth bared, you gaze up into the sky. O Gyalwé Dungdzin, Protector of Beings: With your compassion, inspire us with your blessing! With your love, guide us and others along the path! With your realization, grant us attainments! With your power, dispel the obstacles facing us all! Outer obstacles—dispel them externally, Inner obstacles—dispel them internally, Secret obstacles—dispel them into space! In devotion, I pay homage and take refuge in you!\noṃ āh hūṃ vajra guru padma siddhi hūṃ When hearing the priceless teachings of Dharma, Your body shines with a dazzling radiance of light, In your right hand, volumes of the tripiṭaka, In your left, the texts of Kīlaya. All these profound teachings have infused your mind, O Paṇḍita of Yangleshö: With your compassion, inspire us with your blessing! With your love, guide us and others along the path! With your realization, grant us attainments! With your power, dispel the obstacles facing us all! Outer obstacles—dispel them externally, Inner obstacles—dispel them internally, Secret obstacles—dispel them into space! In devotion, I pay homage and take refuge in you!" + } + }, + { + "title": "Spontaneously Fulfils All Wishes", + "text_id": "116c6b05-9621-435a-8c78-2d588050f006", + "image_url": null, + "first_segment": { + "id": "84b894e1-0a6d-4034-897f-f08015290440", + "content": "Spontaneously Fulfils All Wishes\nEmaho!\nIn Dewachen, ‘Blissful’ pure realm of the west, Amitābha’s compassionate blessing was aroused, And he blessed his emanation, Padmasambhava, To come into this world to bring benefit to all beings. Compassionate one, you never cease to bring us help and well-being: To the Lotus-born Guru of Orgyen, we pray! Grant your blessing, so all our wishes be spontaneously fulfilled!" + } + }, + { + "title": "Three Principal Aspects of the Path", + "text_id": "941bb6db-865d-4a05-9f1a-bc1aa2260769", + "image_url": null, + "first_segment": { + "id": "7132b964-8090-4d4d-924c-ab86d41fde30", + "content": "Three Principal Aspects of the Path\nHomage to the precious noble masters!\nThe very essence of all the buddhas’ teachings,་The path that is praised by the noble bodhisattvas, And the entrance for all fortunate ones desiring liberation— To the best of my ability, I shall now set forth." + } + }, + { + "title": "Long Life Prayer for Lhamo Dhondup", + "text_id": "c41e2387-d0d8-4bf7-8404-0393c6ea34f0", + "image_url": null, + "first_segment": { + "id": "2bc7d04a-5d8d-4f67-b596-f2c19eaea1fe", + "content": "Long Life Prayer for His Holiness the 14th Dalai Lama\nOM SVASTI! O our gurus, and your line of lamas, for whom we have the deepest gratitude, You who are the repository of the three: secret powers of body, speech, and mind of innumerable buddhas, Who manifest in a miraculous way to each devotee according to his capacity, To you, who are the wish-fulfilling gems, the source of all virtues and good qualities, We offer our prayers with intense devotion, That our protector of the great Land of Snows, Tenzin Gyatso, upholder of the Dharma, the great ocean, May live for a hundred eons. Pour on him your blessings, That his aspirations may be fulfilled.\nThe dharmadhatu, the inexpressible reality that pervades all things like the heavens, Immaculate, full of great bliss and transcendental wisdom, Manifests like a cloud the numberless abodes of the higher divinities, The mandalas of the heavenly beings. To all the higher forms of the divine ones, the yidams, We offer our prayers with intense devotion, That Tenzin Gyatso, protector of the great Land of Snows, May live for a hundred eons. Pour on him your blessings, That his aspirations may be fulfilled." + } + }, + { + "title": "In Praise of Dependent Arising", + "text_id": "ff64b716-9ff1-4bc9-8db1-a26727545b91", + "image_url": null, + "first_segment": { + "id": "30c41669-32f4-4147-ad47-e9cad6e0b434", + "content": "In Praise of Dependent Arising\nHomage to (my) Guru, Manjughosha.\nI bow to (you) the Triumphant (Buddha), who has seen and taught dependent arising, which, to see (makes you) a knower and to speak of (makes you) an unsurpassable instructor." + } + }, + { + "title": "The Prayer that Swiftly Fulfils All Wishes", + "text_id": "07c2d6ef-eca0-4e50-b209-5e1b64a47284", + "image_url": null, + "first_segment": { + "id": "64af0a47-ac86-4b57-b492-2d52b9b251b6", + "content": "The Prayer that Swiftly Fulfils All Wishes\nEmaho! In the heart of a blossoming lotus, upon the waters of the lake, You are the deity who is the spontaneous presence of the fivekāyasand wisdoms, O great, naturally arisen Padma Yabyum, Surrounded by clouds of ḍākinīs—to you we pray: Grant your blessing so that all our wishes be quickly fulfilled!\nAs a result of our negative karma, whenever we suffer, From illness, malevolent spirits (döns) and obstacles, warfare and violence, famine and starvation, Then remember your promise that even simply to think of you will immediately dissolve all such suffering— O Lord of Orgyen, we implore you, from the depths of our hearts, Grant your blessing so that all our wishes be quickly fulfilled!" + } + }, + { + "title": "The Foundation of All Qualities", + "text_id": "dfb1afb1-7804-4e25-b114-ea993e6f7d61", + "image_url": null, + "first_segment": { + "id": "4d34bdf5-ce23-4b7e-a243-e024af7d1df7", + "content": "The Foundation of All Qualities\nThe foundation of all qualities is the gracious guru; And to rely upon him or her correctly is the root of the path. Inspire us to understand this well and rouse our energies, So that we may follow with the greatest respect!\nInspire us so that we may realize how this excellent support, With its once-found freedoms, is so rare and momentous; And let us always be determined, day and night, To make the most of this precious opportunity!" + } + }, + { + "title": "The Dhammapada: The Buddha's Path of Wisdom", + "text_id": "3e585317-b848-4624-ae72-07fa545995c2", + "image_url": null, + "first_segment": { + "id": "07af70bb-2b75-4e8f-bbb1-ce52b59197f7", + "content": "The Dhammapada:\nThe Buddha's Path of Wisdom\nTranslated by" + } + }, + { + "title": "Homage", + "text_id": "0fca45a7-71a6-4e58-a615-06af56bdc73f", + "image_url": null, + "first_segment": { + "id": "0cf21396-febb-42f2-b3eb-5680b703b9f8", + "content": "Homage to him, The Blessed One, The Perfect One,\nThe Supremely Enlightened One!\nHomage to the Buddha, homage to the Dhamma," + } + }, + { + "title": "Aspiration", + "text_id": "1c2ee00a-bcc8-4158-947f-e4e69c98b4fd", + "image_url": null, + "first_segment": { + "id": "2a634570-3d6c-4f05-adb7-7309c7002549", + "content": "By the power of this meritorious deed,\nMay I not suffer the company of unwise people.\nMay I be blessed with the company of wise people," + } + } + ], + "skip": 0, + "limit": 21, + "total": 23 +} \ No newline at end of file diff --git a/pecha_api/recitations/order/recitation_order_constants.py b/pecha_api/recitations/order/recitation_order_constants.py new file mode 100644 index 000000000..f9f364a7f --- /dev/null +++ b/pecha_api/recitations/order/recitation_order_constants.py @@ -0,0 +1,10 @@ +from pathlib import Path + +ORDER_DIRECTORY = Path(__file__).resolve().parent +DEFAULT_RECITATION_LANGUAGE = "en" +SUPPORTED_RECITATION_LANGUAGES = frozenset({"bo", "en"}) + +RECITATION_ORDER_PATHS = { + "bo": ORDER_DIRECTORY / "order_recitations_bo.json", + "en": ORDER_DIRECTORY / "order_recitations_en.json", +} diff --git a/pecha_api/recitations/order/recitation_order_loader.py b/pecha_api/recitations/order/recitation_order_loader.py new file mode 100644 index 000000000..df62af4be --- /dev/null +++ b/pecha_api/recitations/order/recitation_order_loader.py @@ -0,0 +1,98 @@ +import json +from functools import lru_cache +from typing import Any, Optional +from uuid import UUID + +from fastapi import HTTPException +from starlette import status + +from pecha_api.recitations.order.recitation_order_constants import ( + DEFAULT_RECITATION_LANGUAGE, + RECITATION_ORDER_PATHS, + SUPPORTED_RECITATION_LANGUAGES, +) +from pecha_api.recitations.recitations_response_models import ( + RecitationDTO, + RecitationsResponse, + Segment, +) + + +def resolve_recitation_language(language: str) -> str: + normalized_language = (language or DEFAULT_RECITATION_LANGUAGE).lower() + if normalized_language in SUPPORTED_RECITATION_LANGUAGES: + return normalized_language + return DEFAULT_RECITATION_LANGUAGE + + +@lru_cache(maxsize=len(RECITATION_ORDER_PATHS)) +def _load_recitation_order(language: str) -> tuple[RecitationDTO, ...]: + order_path = RECITATION_ORDER_PATHS.get(language) + if order_path is None or not order_path.exists(): + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail="Recitation order data is not available", + ) + + with order_path.open(encoding="utf-8") as order_file: + order_data: dict[str, Any] = json.load(order_file) + + return tuple(_parse_recitations(order_data.get("recitations", []))) + + +def _parse_recitations(recitations: list[dict[str, Any]]) -> list[RecitationDTO]: + parsed_recitations: list[RecitationDTO] = [] + for recitation in recitations: + first_segment = None + raw_first_segment = recitation.get("first_segment") + if isinstance(raw_first_segment, dict): + first_segment = Segment( + id=UUID(raw_first_segment["id"]), + content=raw_first_segment["content"], + ) + + parsed_recitations.append( + RecitationDTO( + title=recitation["title"], + text_id=UUID(recitation["text_id"]), + image_url=recitation.get("image_url"), + first_segment=first_segment, + ) + ) + return parsed_recitations + + +def _filter_recitations_by_search( + recitations: list[RecitationDTO], + search: Optional[str], +) -> list[RecitationDTO]: + if not search or not search.strip(): + return recitations + + search_term = search.strip().casefold() + return [ + recitation + for recitation in recitations + if search_term in recitation.title.casefold() + ] + + +def get_ordered_recitations_response( + *, + language: str, + search: Optional[str] = None, + skip: int = 0, + limit: int = 10, +) -> RecitationsResponse: + resolved_language = resolve_recitation_language(language=language) + recitations = list(_load_recitation_order(resolved_language)) + filtered_recitations = _filter_recitations_by_search(recitations=recitations, search=search) + total = len(filtered_recitations) + paginated_recitations = filtered_recitations[skip : skip + limit] + + return RecitationsResponse( + recitations=paginated_recitations, + skip=skip, + limit=limit, + total=total, + ) diff --git a/pecha_api/recitations/recitations_services.py b/pecha_api/recitations/recitations_services.py index 28553ef57..a538a3675 100644 --- a/pecha_api/recitations/recitations_services.py +++ b/pecha_api/recitations/recitations_services.py @@ -1,15 +1,13 @@ from pecha_api.recitations.recitations_enum import LanguageCode,RecitationListTextType from pecha_api.texts.texts_enums import TextType from typing import List, Dict, Union,Optional -from pecha_api.collections.collections_repository import get_all_collections_by_parent, get_collection_id_by_slug -from pecha_api.collections.collections_service import get_collection +from pecha_api.recitations.order.recitation_order_loader import get_ordered_recitations_response from pecha_api.recitations.recitations_repository import get_text_images_by_text_ids from pecha_api.recitations.recitations_response_models import RecitationDTO, RecitationsResponse, Segment from pecha_api.texts.first_segment_preview_service import ( build_first_segment_previews_for_texts, ) from pecha_api.texts.texts_repository import get_all_texts_by_group_id, get_contents_by_id -from pecha_api.texts.texts_service import get_root_text_by_collection_id from pecha_api.texts.segments.segments_service import get_segment_by_id, get_related_mapped_segments, get_segment_details_by_id, get_related_mapped_segments_batch, get_segments_details_by_ids from pecha_api.texts.segments.segments_utils import SegmentUtils from pecha_api.texts.segments.segments_response_models import SegmentTranslation, SegmentTransliteration, SegmentAdaptation, SegmentRecitation @@ -34,6 +32,11 @@ from pecha_api.error_contants import ErrorConstants from pecha_api.texts.texts_utils import TextUtils from pecha_api.texts.texts_response_models import TextDTO, TableOfContent +from pecha_api.region_restrictions.region_restriction_enums import RestrictedItemType +from pecha_api.region_restrictions.region_restriction_service import ( + assert_visible_for_timezone, + filter_items_for_timezone, +) def get_recitations_with_image_urls(recitations: List[RecitationDTO]) -> List[RecitationDTO]: text_ids = [str(recitation.text_id) for recitation in recitations] @@ -87,31 +90,49 @@ async def get_recitations_with_first_segments(recitations: List[RecitationDTO]) ) return result -async def get_list_of_recitations_service(search: Optional[str] = None, language: str = "en", skip: int = 0, limit: int = 10) -> RecitationsResponse: - collection_id = await get_collection_id_by_slug(slug="Liturgy") - if collection_id is None: - raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=ErrorConstants.COLLECTION_NOT_FOUND) - - recitation_list_text_response: RecitationsResponse = await get_root_text_by_collection_id( - collection_id=collection_id, - language=language, - search=search, - skip=skip, - limit=limit +async def get_list_of_recitations_service( + search: Optional[str] = None, + language: str = "en", + skip: int = 0, + limit: int = 10, + timezone_name: Optional[str] = None, +) -> RecitationsResponse: + recitation_list_response = get_ordered_recitations_response( + language=language, + search=search, + skip=skip, + limit=limit, + ) + + recitations_with_images = get_recitations_with_image_urls( + recitations=recitation_list_response.recitations + ) + visible_recitations = filter_items_for_timezone( + recitations_with_images, + timezone_name=timezone_name, + item_type=RestrictedItemType.RECITATION, + id_of=lambda recitation: recitation.text_id, ) - recitations_with_images = get_recitations_with_image_urls(recitations=recitation_list_text_response.recitations) - recitations_with_first_segments = await get_recitations_with_first_segments(recitations=recitations_with_images) - return RecitationsResponse( - recitations=recitations_with_first_segments, - skip=skip, - limit=limit, - total=recitation_list_text_response.total + recitations=visible_recitations, + skip=skip, + limit=limit, + total=recitation_list_response.total, ) -async def get_recitation_details_service(text_id: str, recitation_details_request: RecitationDetailsRequest) -> RecitationDetailsResponse: +async def get_recitation_details_service( + text_id: str, + recitation_details_request: RecitationDetailsRequest, + timezone_name: Optional[str] = None, +) -> RecitationDetailsResponse: + assert_visible_for_timezone( + timezone_name=timezone_name, + item_type=RestrictedItemType.RECITATION, + item_id=UUID(text_id), + not_found_detail=ErrorConstants.TEXT_NOT_FOUND_MESSAGE, + ) is_valid_text: bool = await TextUtils.validate_text_exists(text_id=text_id) if not is_valid_text: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=ErrorConstants.TEXT_NOT_FOUND_MESSAGE) diff --git a/pecha_api/recitations/recitations_view.py b/pecha_api/recitations/recitations_view.py index b60ef0be2..43ac87582 100644 --- a/pecha_api/recitations/recitations_view.py +++ b/pecha_api/recitations/recitations_view.py @@ -1,5 +1,5 @@ -from typing import Optional -from fastapi import APIRouter, Query +from typing import Annotated, Optional +from fastapi import APIRouter, Header, Query from starlette import status @@ -19,9 +19,35 @@ ) @recitation_router.get("",status_code=status.HTTP_200_OK,response_model=RecitationsResponse) -async def get_list_of_recitations(search: Optional[str] = Query(None, description="Search by Recitation title"),language: str = Query(), skip: int = Query(0, ge=0), limit: int = Query(10, ge=1)): - return await get_list_of_recitations_service(search=search, language=language, skip=skip, limit=limit) +async def get_list_of_recitations( + search: Optional[str] = Query(None, description="Search by Recitation title"), + language: str = Query(), + skip: int = Query(0, ge=0), + limit: int = Query(10, ge=1), + x_timezone: Annotated[ + Optional[str], + Header(alias="X-Timezone", description="IANA timezone (e.g. Asia/Shanghai). Restricted recitations are hidden for Chinese timezones."), + ] = None, +): + return await get_list_of_recitations_service( + search=search, + language=language, + skip=skip, + limit=limit, + timezone_name=x_timezone, + ) @recitation_router.post("/{text_id}",status_code=status.HTTP_200_OK,response_model=RecitationDetailsResponse) -async def get_recitation_details(text_id: str, recitation_details_request: RecitationDetailsRequest): - return await get_recitation_details_service(text_id=text_id, recitation_details_request=recitation_details_request) +async def get_recitation_details( + text_id: str, + recitation_details_request: RecitationDetailsRequest, + x_timezone: Annotated[ + Optional[str], + Header(alias="X-Timezone", description="IANA timezone (e.g. Asia/Shanghai). Restricted recitations are hidden for Chinese timezones."), + ] = None, +): + return await get_recitation_details_service( + text_id=text_id, + recitation_details_request=recitation_details_request, + timezone_name=x_timezone, + ) diff --git a/pecha_api/region_restrictions/__init__.py b/pecha_api/region_restrictions/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/pecha_api/region_restrictions/china_timezone.py b/pecha_api/region_restrictions/china_timezone.py new file mode 100644 index 000000000..eccaf6a4d --- /dev/null +++ b/pecha_api/region_restrictions/china_timezone.py @@ -0,0 +1,17 @@ +import json +from functools import lru_cache + +from pecha_api.region_restrictions.region_restriction_constants import CHINESE_TIMEZONE_PATH + + +@lru_cache(maxsize=1) +def get_chinese_timezone_ids() -> frozenset[str]: + with CHINESE_TIMEZONE_PATH.open(encoding="utf-8") as timezone_file: + entries = json.load(timezone_file) + return frozenset(entry["id"] for entry in entries if entry.get("id")) + + +def is_china_timezone(timezone_name: str | None) -> bool: + if not timezone_name or not timezone_name.strip(): + return False + return timezone_name.strip() in get_chinese_timezone_ids() diff --git a/pecha_api/region_restrictions/region_restriction_admin_service.py b/pecha_api/region_restrictions/region_restriction_admin_service.py new file mode 100644 index 000000000..c970ec6b9 --- /dev/null +++ b/pecha_api/region_restrictions/region_restriction_admin_service.py @@ -0,0 +1,170 @@ +from collections import defaultdict +from typing import Optional +from uuid import UUID + +from fastapi import HTTPException +from starlette import status + +from pecha_api.db.database import SessionLocal +from pecha_api.plans.authors.plan_authors_service import validate_and_extract_author_details +from pecha_api.plans.shared.permissions import require_super_admin, require_super_admin_or_reviewer +from pecha_api.region_restrictions.region_restriction_enums import RestrictedItemType +from pecha_api.region_restrictions.region_restriction_item_lookup import ( + resolve_titles_for_rows, + search_restriction_candidates, +) +from pecha_api.region_restrictions.region_restriction_models import ChinaRestrictedItem +from pecha_api.region_restrictions.region_restriction_repository import ( + create_china_restricted_item, + delete_china_restricted_item_by_id, + is_item_restricted_in_china, + list_china_restricted_items, +) +from pecha_api.region_restrictions.region_restriction_response_models import ( + ChinaRestrictedItemDTO, + ChinaRestrictedItemListResponse, + ChinaRestrictionCandidateListResponse, + CreateChinaRestrictedItemRequest, +) +from pecha_api.region_restrictions.region_restriction_service import clear_restricted_items_cache + + +def _normalize_item_type(raw_type) -> RestrictedItemType: + if isinstance(raw_type, RestrictedItemType): + return raw_type + return RestrictedItemType(raw_type) + + +def _row_to_dto( + row: ChinaRestrictedItem, + *, + title: Optional[str] = None, + subtitle: Optional[str] = None, +) -> ChinaRestrictedItemDTO: + item_type = _normalize_item_type(row.item_type) + return ChinaRestrictedItemDTO( + id=row.id, + item_type=item_type, + item_id=row.item_id, + title=title, + subtitle=subtitle, + created_at=row.created_at.isoformat() if row.created_at else "", + updated_at=row.updated_at.isoformat() if row.updated_at else None, + ) + + +def _enrich_rows(db, rows: list[ChinaRestrictedItem]) -> list[ChinaRestrictedItemDTO]: + ids_by_type: dict[RestrictedItemType, list[UUID]] = defaultdict(list) + for row in rows: + ids_by_type[_normalize_item_type(row.item_type)].append(row.item_id) + + titles_by_type: dict[RestrictedItemType, dict[UUID, str]] = {} + for item_type, item_ids in ids_by_type.items(): + titles_by_type[item_type] = resolve_titles_for_rows( + db, + item_type=item_type, + item_ids=item_ids, + ) + + return [ + _row_to_dto( + row, + title=titles_by_type.get(_normalize_item_type(row.item_type), {}).get( + row.item_id + ), + ) + for row in rows + ] + + +def list_admin_china_restricted_items( + token: str, + skip: int, + limit: int, + item_type: Optional[RestrictedItemType] = None, +) -> ChinaRestrictedItemListResponse: + author = validate_and_extract_author_details(token=token) + require_super_admin_or_reviewer(author) + with SessionLocal() as db: + rows, total = list_china_restricted_items( + db=db, + skip=skip, + limit=limit, + item_type=item_type, + ) + items = _enrich_rows(db, rows) + return ChinaRestrictedItemListResponse( + items=items, + skip=skip, + limit=limit, + total=total, + ) + + +def search_admin_china_restriction_candidates( + token: str, + item_type: RestrictedItemType, + search: Optional[str], + skip: int, + limit: int, +) -> ChinaRestrictionCandidateListResponse: + author = validate_and_extract_author_details(token=token) + require_super_admin_or_reviewer(author) + with SessionLocal() as db: + items, total = search_restriction_candidates( + db=db, + item_type=item_type, + search=search, + skip=skip, + limit=limit, + ) + return ChinaRestrictionCandidateListResponse( + items=items, + skip=skip, + limit=limit, + total=total, + ) + + +def create_admin_china_restricted_item( + token: str, + body: CreateChinaRestrictedItemRequest, +) -> ChinaRestrictedItemDTO: + author = validate_and_extract_author_details(token=token) + require_super_admin(author) + with SessionLocal() as db: + if is_item_restricted_in_china( + db=db, + item_type=body.item_type, + item_id=body.item_id, + ): + raise HTTPException( + status_code=status.HTTP_409_CONFLICT, + detail="Item is already restricted in China", + ) + row = create_china_restricted_item( + db=db, + item_type=body.item_type, + item_id=body.item_id, + ) + titles = resolve_titles_for_rows( + db, + item_type=body.item_type, + item_ids=[body.item_id], + ) + dto = _row_to_dto(row, title=titles.get(body.item_id)) + clear_restricted_items_cache() + return dto + + +def delete_admin_china_restricted_item(token: str, row_id: UUID) -> None: + author = validate_and_extract_author_details(token=token) + require_super_admin(author) + with SessionLocal() as db: + deleted = delete_china_restricted_item_by_id(db=db, row_id=row_id) + if not deleted: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail="Restricted item not found", + ) + clear_restricted_items_cache() diff --git a/pecha_api/region_restrictions/region_restriction_constants.py b/pecha_api/region_restrictions/region_restriction_constants.py new file mode 100644 index 000000000..27b51b7dc --- /dev/null +++ b/pecha_api/region_restrictions/region_restriction_constants.py @@ -0,0 +1,5 @@ +from pathlib import Path + +CHINESE_TIMEZONE_PATH = ( + Path(__file__).resolve().parent.parent / "assets" / "chinese_timezone.json" +) diff --git a/pecha_api/region_restrictions/region_restriction_enums.py b/pecha_api/region_restrictions/region_restriction_enums.py new file mode 100644 index 000000000..5ad9da170 --- /dev/null +++ b/pecha_api/region_restrictions/region_restriction_enums.py @@ -0,0 +1,19 @@ +import enum +from sqlalchemy import Enum + + +class RestrictedItemType(enum.Enum): + MANTRA = "MANTRA" + ACCUMULATOR = "ACCUMULATOR" + GROUP_ACCUMULATOR = "GROUP_ACCUMULATOR" + PLAN = "PLAN" + SERIES = "SERIES" + GROUP = "GROUP" + RECITATION = "RECITATION" + RECITATION_COLLECTION = "RECITATION_COLLECTION" + + +RestrictedItemTypeEnum = Enum( + RestrictedItemType, + name="china_restricted_item_type", +) diff --git a/pecha_api/region_restrictions/region_restriction_item_lookup.py b/pecha_api/region_restrictions/region_restriction_item_lookup.py new file mode 100644 index 000000000..88a1d4824 --- /dev/null +++ b/pecha_api/region_restrictions/region_restriction_item_lookup.py @@ -0,0 +1,481 @@ +"""Resolve display titles and search candidates for China-restricted items.""" + +from __future__ import annotations + +from typing import Dict, List, Optional, Sequence, Tuple +from uuid import UUID + +from fastapi import HTTPException +from sqlalchemy import exists, func, or_, select +from sqlalchemy.orm import Session, selectinload + +from pecha_api.accumulator.accumulator_enums import AccumulatorType +from pecha_api.accumulator.accumulator_metadata_model import AccumulatorMetadata +from pecha_api.accumulator.accumulator_models import Accumulator +from pecha_api.accumulator.group_accumulator_models import GroupAccumulator +from pecha_api.mantra.mantra_metadata_model import MantraMetadata +from pecha_api.mantra.mantra_model import Mantra +from pecha_api.mantra.mantra_repository import get_mantras_by_ids +from pecha_api.plans.groups.groups_models import AuthorGroup, AuthorGroupMetadata +from pecha_api.plans.plans_models import Plan +from pecha_api.plans.series.series_metadata_model import SeriesMetadata +from pecha_api.plans.series.series_model import Series +from pecha_api.plans.users.recitation_collection.recitation_collection_models import ( + RecitationCollection, +) +from pecha_api.region_restrictions.region_restriction_enums import RestrictedItemType +from pecha_api.region_restrictions.region_restriction_response_models import ( + ChinaRestrictionCandidateDTO, +) + + +def _lang_value(language) -> str: + if language is None: + return "" + return language.value if hasattr(language, "value") else str(language) + + +def _pick_metadata_text( + entries: Sequence, + *, + field: str = "title", +) -> Optional[str]: + if not entries: + return None + preferred = ("EN", "BO", "ZH") + by_lang = { + _lang_value(getattr(entry, "language", None)).upper(): entry + for entry in entries + } + for code in preferred: + entry = by_lang.get(code) + if entry is None: + continue + value = getattr(entry, field, None) + if value and str(value).strip(): + return str(value).strip() + for entry in entries: + value = getattr(entry, field, None) + if value and str(value).strip(): + return str(value).strip() + return None + + +def _mantra_display_title(mantra: Mantra) -> Optional[str]: + title = _pick_metadata_text(mantra.metadata_entries or [], field="title") + if title: + return title + mantra_text = _pick_metadata_text(mantra.metadata_entries or [], field="mantra") + if mantra_text: + return mantra_text if len(mantra_text) <= 80 else f"{mantra_text[:77]}…" + return None + + +def _accumulator_display_title( + accumulator: Accumulator, + mantras_by_id: Optional[Dict[UUID, Mantra]] = None, +) -> Optional[str]: + name = _pick_metadata_text(accumulator.metadata_entries or [], field="name") + if name: + return name + if mantras_by_id and accumulator.mantra_id: + mantra = mantras_by_id.get(accumulator.mantra_id) + if mantra is not None: + return _mantra_display_title(mantra) + return None + + +def resolve_titles_for_rows( + db: Session, + *, + item_type: RestrictedItemType, + item_ids: Sequence[UUID], +) -> Dict[UUID, str]: + ids = list({item_id for item_id in item_ids if item_id is not None}) + if not ids: + return {} + + if item_type == RestrictedItemType.PLAN: + rows = ( + db.query(Plan.id, Plan.title) + .filter(Plan.id.in_(ids), Plan.deleted_at.is_(None)) + .all() + ) + return {row.id: row.title for row in rows if row.title} + + if item_type == RestrictedItemType.SERIES: + series_rows = ( + db.query(Series) + .options(selectinload(Series.metadata_entries)) + .filter(Series.id.in_(ids), Series.deleted_at.is_(None)) + .all() + ) + return { + series.id: title + for series in series_rows + if (title := _pick_metadata_text(series.metadata_entries or [])) + } + + if item_type == RestrictedItemType.GROUP: + groups = ( + db.query(AuthorGroup) + .options(selectinload(AuthorGroup.metadata_entries)) + .filter(AuthorGroup.id.in_(ids), AuthorGroup.deleted_at.is_(None)) + .all() + ) + return { + group.id: title + for group in groups + if (title := _pick_metadata_text(group.metadata_entries or [])) + } + + if item_type == RestrictedItemType.MANTRA: + mantras = ( + db.query(Mantra) + .options(selectinload(Mantra.metadata_entries)) + .filter(Mantra.id.in_(ids)) + .all() + ) + return { + mantra.id: title + for mantra in mantras + if (title := _mantra_display_title(mantra)) + } + + if item_type == RestrictedItemType.ACCUMULATOR: + accumulators = ( + db.query(Accumulator) + .options(selectinload(Accumulator.metadata_entries)) + .filter(Accumulator.id.in_(ids), Accumulator.deleted_at.is_(None)) + .all() + ) + mantra_ids = [ + acc.mantra_id for acc in accumulators if acc.mantra_id is not None + ] + mantras_by_id = get_mantras_by_ids(db, mantra_ids) + return { + acc.id: title + for acc in accumulators + if (title := _accumulator_display_title(acc, mantras_by_id)) + } + + if item_type == RestrictedItemType.GROUP_ACCUMULATOR: + rows = ( + db.query(GroupAccumulator.id, GroupAccumulator.title) + .filter( + GroupAccumulator.id.in_(ids), + GroupAccumulator.deleted_at.is_(None), + ) + .all() + ) + return { + row.id: row.title.strip() + for row in rows + if row.title and row.title.strip() + } + + if item_type == RestrictedItemType.RECITATION_COLLECTION: + rows = ( + db.query(RecitationCollection.id, RecitationCollection.name) + .filter(RecitationCollection.id.in_(ids)) + .all() + ) + return {row.id: row.name for row in rows if row.name} + + if item_type == RestrictedItemType.RECITATION: + return _resolve_recitation_titles(item_ids=ids) + + return {} + + +def _get_ordered_recitations_response(**kwargs): + from pecha_api.recitations.order.recitation_order_loader import ( + get_ordered_recitations_response, + ) + + return get_ordered_recitations_response(**kwargs) + + +def _resolve_recitation_titles(*, item_ids: Sequence[UUID]) -> Dict[UUID, str]: + wanted = set(item_ids) + found: Dict[UUID, str] = {} + for language in ("en", "bo"): + try: + response = _get_ordered_recitations_response( + language=language, + search=None, + skip=0, + limit=10_000, + ) + except (OSError, ValueError, TypeError, KeyError, AttributeError, HTTPException): + continue + for recitation in response.recitations: + try: + text_id = ( + recitation.text_id + if isinstance(recitation.text_id, UUID) + else UUID(str(recitation.text_id)) + ) + except (TypeError, ValueError): + continue + if text_id in wanted and text_id not in found and recitation.title: + found[text_id] = recitation.title.strip() + if len(found) == len(wanted): + break + return found + + +def search_restriction_candidates( + db: Session, + *, + item_type: RestrictedItemType, + search: Optional[str], + skip: int, + limit: int, +) -> Tuple[List[ChinaRestrictionCandidateDTO], int]: + term = search.strip() if search and search.strip() else None + + if item_type == RestrictedItemType.PLAN: + return _search_plans(db=db, term=term, skip=skip, limit=limit) + if item_type == RestrictedItemType.SERIES: + return _search_series(db=db, term=term, skip=skip, limit=limit) + if item_type == RestrictedItemType.GROUP: + return _search_groups(db=db, term=term, skip=skip, limit=limit) + if item_type == RestrictedItemType.MANTRA: + return _search_mantras(db=db, term=term, skip=skip, limit=limit) + if item_type == RestrictedItemType.ACCUMULATOR: + return _search_accumulators(db=db, term=term, skip=skip, limit=limit) + if item_type == RestrictedItemType.GROUP_ACCUMULATOR: + return _search_group_accumulators(db=db, term=term, skip=skip, limit=limit) + if item_type == RestrictedItemType.RECITATION_COLLECTION: + return _search_recitation_collections(db=db, term=term, skip=skip, limit=limit) + if item_type == RestrictedItemType.RECITATION: + return _search_recitations(term=term, skip=skip, limit=limit) + return [], 0 + + +def _search_plans( + db: Session, *, term: Optional[str], skip: int, limit: int +) -> Tuple[List[ChinaRestrictionCandidateDTO], int]: + query = db.query(Plan).filter(Plan.deleted_at.is_(None)) + if term: + query = query.filter(Plan.title.ilike(f"%{term}%")) + total = query.count() + rows = query.order_by(Plan.created_at.desc()).offset(skip).limit(limit).all() + return [ + ChinaRestrictionCandidateDTO(id=row.id, title=row.title or "Untitled plan") + for row in rows + ], total + + +def _search_series( + db: Session, *, term: Optional[str], skip: int, limit: int +) -> Tuple[List[ChinaRestrictionCandidateDTO], int]: + filters = [Series.deleted_at.is_(None)] + if term: + filters.append( + exists( + select(1).where( + SeriesMetadata.series_id == Series.id, + or_( + SeriesMetadata.title.ilike(f"%{term}%"), + SeriesMetadata.sub_title.ilike(f"%{term}%"), + ), + ) + ) + ) + query = ( + db.query(Series) + .options(selectinload(Series.metadata_entries)) + .filter(*filters) + ) + total = query.count() + rows = query.order_by(Series.created_at.desc()).offset(skip).limit(limit).all() + return [ + ChinaRestrictionCandidateDTO( + id=row.id, + title=_pick_metadata_text(row.metadata_entries or []) or "Untitled series", + ) + for row in rows + ], total + + +def _search_groups( + db: Session, *, term: Optional[str], skip: int, limit: int +) -> Tuple[List[ChinaRestrictionCandidateDTO], int]: + filters = [AuthorGroup.deleted_at.is_(None)] + if term: + filters.append( + exists( + select(1).where( + AuthorGroupMetadata.group_id == AuthorGroup.id, + or_( + AuthorGroupMetadata.title.ilike(f"%{term}%"), + AuthorGroupMetadata.sub_title.ilike(f"%{term}%"), + ), + ) + ) + ) + query = ( + db.query(AuthorGroup) + .options(selectinload(AuthorGroup.metadata_entries)) + .filter(*filters) + ) + total = query.count() + rows = ( + query.order_by(AuthorGroup.created_at.desc()).offset(skip).limit(limit).all() + ) + return [ + ChinaRestrictionCandidateDTO( + id=row.id, + title=_pick_metadata_text(row.metadata_entries or []) or "Untitled group", + subtitle=row.slug, + ) + for row in rows + ], total + + +def _search_mantras( + db: Session, *, term: Optional[str], skip: int, limit: int +) -> Tuple[List[ChinaRestrictionCandidateDTO], int]: + query = db.query(Mantra).options(selectinload(Mantra.metadata_entries)) + if term: + like = f"%{term}%" + query = query.filter( + exists( + select(1).where( + MantraMetadata.mantra_id == Mantra.id, + or_( + func.coalesce(MantraMetadata.title, "").ilike(like), + func.coalesce(MantraMetadata.mantra, "").ilike(like), + func.coalesce(MantraMetadata.pronunciation, "").ilike(like), + ), + ) + ) + ) + total = query.count() + rows = query.order_by(Mantra.created_at.desc()).offset(skip).limit(limit).all() + return [ + ChinaRestrictionCandidateDTO( + id=row.id, + title=_mantra_display_title(row) or "Untitled mantra", + ) + for row in rows + ], total + + +def _search_accumulators( + db: Session, *, term: Optional[str], skip: int, limit: int +) -> Tuple[List[ChinaRestrictionCandidateDTO], int]: + query = ( + db.query(Accumulator) + .options(selectinload(Accumulator.metadata_entries)) + .filter( + Accumulator.type == AccumulatorType.PRESET, + Accumulator.deleted_at.is_(None), + ) + ) + if term: + like = f"%{term}%" + matching_mantra_ids = db.query(MantraMetadata.mantra_id).filter( + or_( + func.coalesce(MantraMetadata.mantra, "").ilike(like), + func.coalesce(MantraMetadata.title, "").ilike(like), + func.coalesce(MantraMetadata.pronunciation, "").ilike(like), + ) + ) + matching_acc_ids = db.query(AccumulatorMetadata.accumulator_id).filter( + AccumulatorMetadata.name.ilike(like) + ) + query = query.filter( + or_( + Accumulator.mantra_id.in_(matching_mantra_ids), + Accumulator.id.in_(matching_acc_ids), + ) + ) + total = query.count() + rows = ( + query.order_by(Accumulator.created_at.desc()).offset(skip).limit(limit).all() + ) + mantra_ids = [row.mantra_id for row in rows if row.mantra_id is not None] + mantras_by_id = get_mantras_by_ids(db, mantra_ids) + return [ + ChinaRestrictionCandidateDTO( + id=row.id, + title=_accumulator_display_title(row, mantras_by_id) or "Untitled preset", + ) + for row in rows + ], total + + +def _search_group_accumulators( + db: Session, *, term: Optional[str], skip: int, limit: int +) -> Tuple[List[ChinaRestrictionCandidateDTO], int]: + query = db.query(GroupAccumulator).filter(GroupAccumulator.deleted_at.is_(None)) + if term: + query = query.filter(GroupAccumulator.title.ilike(f"%{term}%")) + total = query.count() + rows = ( + query.order_by(GroupAccumulator.created_at.desc()) + .offset(skip) + .limit(limit) + .all() + ) + return [ + ChinaRestrictionCandidateDTO( + id=row.id, + title=(row.title.strip() if row.title and row.title.strip() else "Untitled group accumulator"), + ) + for row in rows + ], total + + +def _search_recitation_collections( + db: Session, *, term: Optional[str], skip: int, limit: int +) -> Tuple[List[ChinaRestrictionCandidateDTO], int]: + query = db.query(RecitationCollection) + if term: + query = query.filter(RecitationCollection.name.ilike(f"%{term}%")) + total = query.count() + rows = ( + query.order_by(RecitationCollection.created_at.desc()) + .offset(skip) + .limit(limit) + .all() + ) + return [ + ChinaRestrictionCandidateDTO(id=row.id, title=row.name or "Untitled collection") + for row in rows + ], total + + +def _search_recitations( + *, term: Optional[str], skip: int, limit: int +) -> Tuple[List[ChinaRestrictionCandidateDTO], int]: + try: + response = _get_ordered_recitations_response( + language="en", + search=term, + skip=skip, + limit=limit, + ) + except (OSError, ValueError, TypeError, KeyError, AttributeError, HTTPException): + return [], 0 + + items: List[ChinaRestrictionCandidateDTO] = [] + for recitation in response.recitations: + try: + text_id = ( + recitation.text_id + if isinstance(recitation.text_id, UUID) + else UUID(str(recitation.text_id)) + ) + except (TypeError, ValueError): + continue + items.append( + ChinaRestrictionCandidateDTO( + id=text_id, + title=recitation.title or "Untitled recitation", + ) + ) + return items, response.total diff --git a/pecha_api/region_restrictions/region_restriction_models.py b/pecha_api/region_restrictions/region_restriction_models.py new file mode 100644 index 000000000..2ea86c362 --- /dev/null +++ b/pecha_api/region_restrictions/region_restriction_models.py @@ -0,0 +1,33 @@ +import datetime as _datetime +from uuid import uuid4 + +from sqlalchemy import Column, DateTime, UniqueConstraint +from sqlalchemy.dialects.postgresql import UUID + +from pecha_api.db.database import Base +from pecha_api.region_restrictions.region_restriction_enums import RestrictedItemTypeEnum + + +class ChinaRestrictedItem(Base): + __tablename__ = "china_restricted_items" + __table_args__ = ( + UniqueConstraint( + "item_type", + "item_id", + name="uq_china_restricted_items_type_id", + ), + ) + + id = Column(UUID(as_uuid=True), primary_key=True, default=uuid4) + item_type = Column(RestrictedItemTypeEnum, nullable=False) + item_id = Column(UUID(as_uuid=True), nullable=False) + created_at = Column( + DateTime(timezone=True), + default=lambda: _datetime.datetime.now(_datetime.timezone.utc), + nullable=False, + ) + updated_at = Column( + DateTime(timezone=True), + default=lambda: _datetime.datetime.now(_datetime.timezone.utc), + onupdate=lambda: _datetime.datetime.now(_datetime.timezone.utc), + ) diff --git a/pecha_api/region_restrictions/region_restriction_repository.py b/pecha_api/region_restrictions/region_restriction_repository.py new file mode 100644 index 000000000..73e7d822e --- /dev/null +++ b/pecha_api/region_restrictions/region_restriction_repository.py @@ -0,0 +1,70 @@ +from typing import List, Optional, Tuple +from uuid import UUID + +from sqlalchemy.orm import Session + +from pecha_api.region_restrictions.region_restriction_enums import RestrictedItemType +from pecha_api.region_restrictions.region_restriction_models import ChinaRestrictedItem + + +def get_all_china_restricted_items(db: Session) -> List[ChinaRestrictedItem]: + return db.query(ChinaRestrictedItem).all() + + +def list_china_restricted_items( + db: Session, + *, + skip: int, + limit: int, + item_type: Optional[RestrictedItemType] = None, +) -> Tuple[List[ChinaRestrictedItem], int]: + query = db.query(ChinaRestrictedItem) + if item_type is not None: + query = query.filter(ChinaRestrictedItem.item_type == item_type) + total = query.count() + rows = ( + query.order_by(ChinaRestrictedItem.created_at.desc()) + .offset(skip) + .limit(limit) + .all() + ) + return rows, total + + +def create_china_restricted_item( + db: Session, + *, + item_type: RestrictedItemType, + item_id: UUID, +) -> ChinaRestrictedItem: + row = ChinaRestrictedItem(item_type=item_type, item_id=item_id) + db.add(row) + db.commit() + db.refresh(row) + return row + + +def delete_china_restricted_item_by_id(db: Session, *, row_id: UUID) -> bool: + row = db.query(ChinaRestrictedItem).filter(ChinaRestrictedItem.id == row_id).first() + if row is None: + return False + db.delete(row) + db.commit() + return True + + +def is_item_restricted_in_china( + db: Session, + *, + item_type: RestrictedItemType, + item_id: UUID, +) -> bool: + return ( + db.query(ChinaRestrictedItem.id) + .filter( + ChinaRestrictedItem.item_type == item_type, + ChinaRestrictedItem.item_id == item_id, + ) + .first() + is not None + ) diff --git a/pecha_api/region_restrictions/region_restriction_response_models.py b/pecha_api/region_restrictions/region_restriction_response_models.py new file mode 100644 index 000000000..cbbaded89 --- /dev/null +++ b/pecha_api/region_restrictions/region_restriction_response_models.py @@ -0,0 +1,41 @@ +from typing import Optional +from uuid import UUID + +from pydantic import BaseModel + +from pecha_api.region_restrictions.region_restriction_enums import RestrictedItemType + + +class ChinaRestrictedItemDTO(BaseModel): + id: UUID + item_type: RestrictedItemType + item_id: UUID + title: Optional[str] = None + subtitle: Optional[str] = None + created_at: str + updated_at: Optional[str] = None + + +class ChinaRestrictedItemListResponse(BaseModel): + items: list[ChinaRestrictedItemDTO] + skip: int + limit: int + total: int + + +class CreateChinaRestrictedItemRequest(BaseModel): + item_type: RestrictedItemType + item_id: UUID + + +class ChinaRestrictionCandidateDTO(BaseModel): + id: UUID + title: str + subtitle: Optional[str] = None + + +class ChinaRestrictionCandidateListResponse(BaseModel): + items: list[ChinaRestrictionCandidateDTO] + skip: int + limit: int + total: int diff --git a/pecha_api/region_restrictions/region_restriction_service.py b/pecha_api/region_restrictions/region_restriction_service.py new file mode 100644 index 000000000..f339751f0 --- /dev/null +++ b/pecha_api/region_restrictions/region_restriction_service.py @@ -0,0 +1,83 @@ +from collections import defaultdict +from functools import lru_cache +from typing import Callable, Iterable, Optional, TypeVar +from uuid import UUID + +from fastapi import HTTPException +from starlette import status + +from pecha_api.db.database import SessionLocal +from pecha_api.region_restrictions.china_timezone import is_china_timezone +from pecha_api.region_restrictions.region_restriction_enums import RestrictedItemType +from pecha_api.region_restrictions.region_restriction_repository import ( + get_all_china_restricted_items, +) + +T = TypeVar("T") + + +@lru_cache(maxsize=1) +def _load_restricted_item_ids_by_type() -> dict[str, frozenset[UUID]]: + with SessionLocal() as db: + rows = get_all_china_restricted_items(db=db) + + grouped: dict[str, set[UUID]] = defaultdict(set) + for row in rows: + item_type = ( + row.item_type.value + if hasattr(row.item_type, "value") + else str(row.item_type) + ) + grouped[item_type].add(row.item_id) + return {item_type: frozenset(item_ids) for item_type, item_ids in grouped.items()} + + +def clear_restricted_items_cache() -> None: + _load_restricted_item_ids_by_type.cache_clear() + + +def get_restricted_item_ids(item_type: RestrictedItemType) -> frozenset[UUID]: + return _load_restricted_item_ids_by_type().get(item_type.value, frozenset()) + + +def is_restricted_in_china(item_type: RestrictedItemType, item_id: UUID) -> bool: + return item_id in get_restricted_item_ids(item_type) + + +def should_hide_for_timezone( + timezone_name: Optional[str], + item_type: RestrictedItemType, + item_id: UUID, +) -> bool: + return is_china_timezone(timezone_name) and is_restricted_in_china(item_type, item_id) + + +def filter_items_for_timezone( + items: Iterable[T], + *, + timezone_name: Optional[str], + item_type: RestrictedItemType, + id_of: Callable[[T], UUID], +) -> list[T]: + if not is_china_timezone(timezone_name): + return list(items) + + restricted_ids = get_restricted_item_ids(item_type) + if not restricted_ids: + return list(items) + + return [item for item in items if id_of(item) not in restricted_ids] + + +def assert_visible_for_timezone( + *, + timezone_name: Optional[str], + item_type: RestrictedItemType, + item_id: UUID, + not_found_detail: str = "Not found", +) -> None: + if should_hide_for_timezone(timezone_name, item_type, item_id): + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail=not_found_detail, + ) diff --git a/pecha_api/region_restrictions/region_restriction_views.py b/pecha_api/region_restrictions/region_restriction_views.py new file mode 100644 index 000000000..ab2b85931 --- /dev/null +++ b/pecha_api/region_restrictions/region_restriction_views.py @@ -0,0 +1,88 @@ +from typing import Annotated, Optional +from uuid import UUID + +from fastapi import APIRouter, Depends, Query +from starlette import status + +from pecha_api.plans.auth.cms_auth_deps import get_cms_author_token +from pecha_api.region_restrictions.region_restriction_admin_service import ( + create_admin_china_restricted_item, + delete_admin_china_restricted_item, + list_admin_china_restricted_items, + search_admin_china_restriction_candidates, +) +from pecha_api.region_restrictions.region_restriction_enums import RestrictedItemType +from pecha_api.region_restrictions.region_restriction_response_models import ( + ChinaRestrictedItemDTO, + ChinaRestrictedItemListResponse, + ChinaRestrictionCandidateListResponse, + CreateChinaRestrictedItemRequest, +) + +cms_china_restrictions_router = APIRouter( + prefix="/cms/admin/china-restrictions", + tags=["CMS Admin China Restrictions"], +) + + +@cms_china_restrictions_router.get( + "", + status_code=status.HTTP_200_OK, + response_model=ChinaRestrictedItemListResponse, +) +def get_cms_china_restricted_items( + skip: Annotated[int, Query(ge=0)] = 0, + limit: Annotated[int, Query(ge=1, le=100)] = 20, + item_type: Annotated[Optional[RestrictedItemType], Query()] = None, + token: Annotated[str, Depends(get_cms_author_token)] = "", +): + return list_admin_china_restricted_items( + token=token, + skip=skip, + limit=limit, + item_type=item_type, + ) + + +@cms_china_restrictions_router.get( + "/candidates", + status_code=status.HTTP_200_OK, + response_model=ChinaRestrictionCandidateListResponse, +) +def get_cms_china_restriction_candidates( + item_type: Annotated[RestrictedItemType, Query()], + search: Annotated[Optional[str], Query()] = None, + skip: Annotated[int, Query(ge=0)] = 0, + limit: Annotated[int, Query(ge=1, le=50)] = 20, + token: Annotated[str, Depends(get_cms_author_token)] = "", +): + return search_admin_china_restriction_candidates( + token=token, + item_type=item_type, + search=search, + skip=skip, + limit=limit, + ) + + +@cms_china_restrictions_router.post( + "", + status_code=status.HTTP_201_CREATED, + response_model=ChinaRestrictedItemDTO, +) +def post_cms_china_restricted_item( + body: CreateChinaRestrictedItemRequest, + token: Annotated[str, Depends(get_cms_author_token)] = "", +): + return create_admin_china_restricted_item(token=token, body=body) + + +@cms_china_restrictions_router.delete( + "/{row_id}", + status_code=status.HTTP_204_NO_CONTENT, +) +def delete_cms_china_restricted_item( + row_id: UUID, + token: Annotated[str, Depends(get_cms_author_token)] = "", +): + delete_admin_china_restricted_item(token=token, row_id=row_id) diff --git a/pecha_api/traditions/Buddhist Traditions Taxonomy - i18n-v1.1.json b/pecha_api/traditions/Buddhist Traditions Taxonomy - i18n-v1.1.json deleted file mode 100644 index d2879955a..000000000 --- a/pecha_api/traditions/Buddhist Traditions Taxonomy - i18n-v1.1.json +++ /dev/null @@ -1,4367 +0,0 @@ -{ - "_meta": { - "version": "1.1", - "languages": [ - "en", - "zh", - "bo", - "hi", - "ne", - "mn" - ], - "schema": "traditions[]: { id, names{ lang: { name, aliases[] } }, regions[], level(1-4), parent(id|null) }", - "review_needed": { - "zh": "High confidence — Chinese Buddhist terminology is well-standardized. Spot-check sub-school names.", - "bo": "High confidence for Tibetan schools (canonical Wylie/script names). Non-Tibetan schools fall back to English — needs native review for any Tibetan-script transliterations.", - "hi": "Medium confidence — major branches and Navayana are reliable. Sub-school transliterations need native Buddhist speaker review.", - "ne": "Medium confidence — mostly mirrors Hindi (shared Devanagari). Newar Buddhism entries are locally reviewed. Sub-schools need review.", - "mn": "Medium confidence — Mongolian Tibetan-school names are standard. Non-Tibetan-tradition Cyrillic transliterations need native review." - } - }, - "traditions": [ - { - "id": "theravada", - "names": { - "en": { - "name": "Theravāda", - "aliases": [ - "Theravada", - "Southern Buddhism", - "Pali Buddhism", - "Hinayana" - ] - }, - "zh": { - "name": "上座部佛教", - "aliases": [ - "南传佛教", - "巴利语佛教", - "小乘" - ] - }, - "bo": { - "name": "ཐེར་བ་ད།", - "aliases": [ - "གནས་བརྟན་པའི་སྡེ་པ།", - "Theravada" - ] - }, - "hi": { - "name": "थेरवाद", - "aliases": [ - "दक्षिणी बौद्ध धर्म", - "पाली बौद्ध धर्म" - ] - }, - "ne": { - "name": "थेरवाद", - "aliases": [ - "दक्षिणी बौद्ध धर्म", - "पाली बौद्ध धर्म" - ] - }, - "mn": { - "name": "Тхеравад", - "aliases": [ - "Тхэравада", - "Өмнөд Буддизм" - ] - } - }, - "regions": [ - "Sri Lanka", - "Thailand", - "Myanmar", - "Cambodia", - "Laos", - "Bangladesh", - "India" - ], - "level": 1, - "parent": null - }, - { - "id": "thai-buddhism", - "names": { - "en": { - "name": "Thai Buddhism", - "aliases": [ - "Thai Theravada", - "Thai Theravāda" - ] - }, - "zh": { - "name": "泰国佛教", - "aliases": [ - "泰国上座部" - ] - }, - "bo": { - "name": "Thai Buddhism", - "aliases": [ - "ཐཱའི་ཕྱོགས་ཀྱི་ནང་ཆོས།" - ] - }, - "hi": { - "name": "थाई बौद्ध धर्म", - "aliases": [ - "थाईलैंड बौद्ध धर्म" - ] - }, - "ne": { - "name": "थाई बौद्ध धर्म", - "aliases": [ - "थाइल्याण्ड बौद्ध धर्म" - ] - }, - "mn": { - "name": "Тайландын Буддизм", - "aliases": [] - } - }, - "regions": [ - "Thailand" - ], - "level": 2, - "parent": "theravada" - }, - { - "id": "dhammayut", - "names": { - "en": { - "name": "Dhammayuttika Nikāya", - "aliases": [ - "Dhammayut", - "Dhammayuttika", - "Thammayut", - "Dhammayut Order" - ] - }, - "zh": { - "name": "法宗派", - "aliases": [ - "达摩友派", - "法宗" - ] - }, - "bo": { - "name": "Dhammayut", - "aliases": [ - "དྷམ་མ་ཡུད།" - ] - }, - "hi": { - "name": "धम्मयुत निकाय", - "aliases": [ - "धम्मयुत्त" - ] - }, - "ne": { - "name": "धम्मयुत निकाय", - "aliases": [ - "धम्मयुत्त" - ] - }, - "mn": { - "name": "Дхаммаютт", - "aliases": [] - } - }, - "regions": [ - "Thailand", - "Cambodia", - "Laos" - ], - "level": 3, - "parent": "thai-buddhism" - }, - { - "id": "maha-nikaya-th", - "names": { - "en": { - "name": "Mahā Nikāya", - "aliases": [ - "Maha Nikaya", - "Mahanikaya" - ] - }, - "zh": { - "name": "大宗派", - "aliases": [ - "摩诃尼迦耶" - ] - }, - "bo": { - "name": "Maha Nikaya", - "aliases": [] - }, - "hi": { - "name": "महानिकाय", - "aliases": [] - }, - "ne": { - "name": "महानिकाय", - "aliases": [] - }, - "mn": { - "name": "Маха Никаяа", - "aliases": [] - } - }, - "regions": [ - "Thailand" - ], - "level": 3, - "parent": "thai-buddhism" - }, - { - "id": "thai-forest", - "names": { - "en": { - "name": "Thai Forest Tradition", - "aliases": [ - "Forest Tradition", - "Forest Sangha", - "Kammatthana", - "Ajahn Chah lineage", - "Ajahn Mun lineage" - ] - }, - "zh": { - "name": "泰国森林传统", - "aliases": [ - "林居传统", - "阿姜查传承", - "阿姜曼传承" - ] - }, - "bo": { - "name": "Thai Forest Tradition", - "aliases": [] - }, - "hi": { - "name": "थाई वन परंपरा", - "aliases": [ - "वन संघ", - "अज्ञान चाह परंपरा" - ] - }, - "ne": { - "name": "थाई वन परम्परा", - "aliases": [ - "वन संघ" - ] - }, - "mn": { - "name": "Тайландын Ойн Уламжлал", - "aliases": [] - } - }, - "regions": [ - "Thailand", - "International" - ], - "level": 3, - "parent": "thai-buddhism" - }, - { - "id": "burmese-buddhism", - "names": { - "en": { - "name": "Burmese Buddhism", - "aliases": [ - "Myanmar Buddhism", - "Burmese Theravada" - ] - }, - "zh": { - "name": "缅甸佛教", - "aliases": [ - "缅甸上座部" - ] - }, - "bo": { - "name": "Burmese Buddhism", - "aliases": [] - }, - "hi": { - "name": "बर्मी बौद्ध धर्म", - "aliases": [ - "म्यांमार बौद्ध धर्म" - ] - }, - "ne": { - "name": "बर्मी बौद्ध धर्म", - "aliases": [ - "म्यानमार बौद्ध धर्म" - ] - }, - "mn": { - "name": "Мьянмарын Буддизм", - "aliases": [ - "Бирмийн Буддизм" - ] - } - }, - "regions": [ - "Myanmar" - ], - "level": 2, - "parent": "theravada" - }, - { - "id": "thudhamma", - "names": { - "en": { - "name": "Thudhamma Nikāya", - "aliases": [ - "Thudhamma", - "Sudhamma" - ] - }, - "zh": { - "name": "杜达玛派", - "aliases": [] - }, - "bo": { - "name": "Thudhamma", - "aliases": [] - }, - "hi": { - "name": "तुधम्म निकाय", - "aliases": [] - }, - "ne": { - "name": "तुधम्म निकाय", - "aliases": [] - }, - "mn": { - "name": "Тудхамма", - "aliases": [] - } - }, - "regions": [ - "Myanmar" - ], - "level": 3, - "parent": "burmese-buddhism" - }, - { - "id": "shwekyin", - "names": { - "en": { - "name": "Shwekyin Nikāya", - "aliases": [ - "Shwekyin" - ] - }, - "zh": { - "name": "瑞基因派", - "aliases": [] - }, - "bo": { - "name": "Shwekyin", - "aliases": [] - }, - "hi": { - "name": "श्वेकिन निकाय", - "aliases": [] - }, - "ne": { - "name": "श्वेकिन निकाय", - "aliases": [] - }, - "mn": { - "name": "Швекин", - "aliases": [] - } - }, - "regions": [ - "Myanmar" - ], - "level": 3, - "parent": "burmese-buddhism" - }, - { - "id": "mahasi", - "names": { - "en": { - "name": "Mahasi Tradition", - "aliases": [ - "Mahasi Sayadaw", - "Mahasi Vipassana", - "Satipatthana Vipassana" - ] - }, - "zh": { - "name": "马哈希传统", - "aliases": [ - "马哈希内观", - "马哈希长老" - ] - }, - "bo": { - "name": "Mahasi Tradition", - "aliases": [] - }, - "hi": { - "name": "महासी परंपरा", - "aliases": [ - "महासी सयाडो", - "महासी विपश्यना" - ] - }, - "ne": { - "name": "महासी परम्परा", - "aliases": [ - "महासी सयाडो" - ] - }, - "mn": { - "name": "Махасийн Уламжлал", - "aliases": [] - } - }, - "regions": [ - "Myanmar", - "International" - ], - "level": 3, - "parent": "burmese-buddhism" - }, - { - "id": "pa-auk", - "names": { - "en": { - "name": "Pa-Auk Tradition", - "aliases": [ - "Pa Auk", - "Pa-Auk Sayadaw", - "Pa-Auk Tawya" - ] - }, - "zh": { - "name": "帕奥传统", - "aliases": [ - "帕奥禅师" - ] - }, - "bo": { - "name": "Pa-Auk Tradition", - "aliases": [] - }, - "hi": { - "name": "पा-ओक परंपरा", - "aliases": [ - "पा-ओक सयाडो" - ] - }, - "ne": { - "name": "पा-आउक परम्परा", - "aliases": [] - }, - "mn": { - "name": "Па-Аукийн Уламжлал", - "aliases": [] - } - }, - "regions": [ - "Myanmar", - "International" - ], - "level": 3, - "parent": "burmese-buddhism" - }, - { - "id": "sri-lankan-buddhism", - "names": { - "en": { - "name": "Sri Lankan Buddhism", - "aliases": [ - "Sinhalese Buddhism", - "Ceylon Buddhism" - ] - }, - "zh": { - "name": "斯里兰卡佛教", - "aliases": [ - "僧伽罗佛教" - ] - }, - "bo": { - "name": "Sri Lankan Buddhism", - "aliases": [] - }, - "hi": { - "name": "श्रीलंकाई बौद्ध धर्म", - "aliases": [ - "सिंहल बौद्ध धर्म", - "सीलोन बौद्ध धर्म" - ] - }, - "ne": { - "name": "श्रीलंकाली बौद्ध धर्म", - "aliases": [ - "सिंहाली बौद्ध धर्म" - ] - }, - "mn": { - "name": "Шри Ланкийн Буддизм", - "aliases": [] - } - }, - "regions": [ - "Sri Lanka" - ], - "level": 2, - "parent": "theravada" - }, - { - "id": "siam-nikaya", - "names": { - "en": { - "name": "Siam Nikāya", - "aliases": [ - "Siam Nikaya", - "Siamese Nikaya" - ] - }, - "zh": { - "name": "暹罗派", - "aliases": [] - }, - "bo": { - "name": "Siam Nikaya", - "aliases": [] - }, - "hi": { - "name": "स्याम निकाय", - "aliases": [ - "सियाम निकाय" - ] - }, - "ne": { - "name": "स्याम निकाय", - "aliases": [] - }, - "mn": { - "name": "Сиамын Никаяа", - "aliases": [] - } - }, - "regions": [ - "Sri Lanka" - ], - "level": 3, - "parent": "sri-lankan-buddhism" - }, - { - "id": "amarapura-nikaya", - "names": { - "en": { - "name": "Amarapura Nikāya", - "aliases": [ - "Amarapura Nikaya" - ] - }, - "zh": { - "name": "阿摩罗补罗派", - "aliases": [] - }, - "bo": { - "name": "Amarapura Nikaya", - "aliases": [] - }, - "hi": { - "name": "अमरपुर निकाय", - "aliases": [] - }, - "ne": { - "name": "अमरपुर निकाय", - "aliases": [] - }, - "mn": { - "name": "Амарапура Никаяа", - "aliases": [] - } - }, - "regions": [ - "Sri Lanka" - ], - "level": 3, - "parent": "sri-lankan-buddhism" - }, - { - "id": "ramanna-nikaya", - "names": { - "en": { - "name": "Rāmañña Nikāya", - "aliases": [ - "Ramanna Nikaya", - "Ramanna" - ] - }, - "zh": { - "name": "罗曼纽派", - "aliases": [] - }, - "bo": { - "name": "Ramanna Nikaya", - "aliases": [] - }, - "hi": { - "name": "रमञ्ञ निकाय", - "aliases": [] - }, - "ne": { - "name": "रमञ्ञ निकाय", - "aliases": [] - }, - "mn": { - "name": "Рамання Никаяа", - "aliases": [] - } - }, - "regions": [ - "Sri Lanka" - ], - "level": 3, - "parent": "sri-lankan-buddhism" - }, - { - "id": "cambodian-buddhism", - "names": { - "en": { - "name": "Cambodian Buddhism", - "aliases": [ - "Khmer Buddhism" - ] - }, - "zh": { - "name": "柬埔寨佛教", - "aliases": [ - "高棉佛教" - ] - }, - "bo": { - "name": "Cambodian Buddhism", - "aliases": [] - }, - "hi": { - "name": "कंबोडियाई बौद्ध धर्म", - "aliases": [ - "खमेर बौद्ध धर्म" - ] - }, - "ne": { - "name": "कम्बोडियाली बौद्ध धर्म", - "aliases": [ - "खमेर बौद्ध धर्म" - ] - }, - "mn": { - "name": "Камбожийн Буддизм", - "aliases": [ - "Кхмерийн Буддизм" - ] - } - }, - "regions": [ - "Cambodia" - ], - "level": 2, - "parent": "theravada" - }, - { - "id": "mohanikay", - "names": { - "en": { - "name": "Mohanikay", - "aliases": [ - "Mahanikay", - "Maha Nikaya Cambodia" - ] - }, - "zh": { - "name": "摩诃尼迦耶(柬)", - "aliases": [] - }, - "bo": { - "name": "Mohanikay", - "aliases": [] - }, - "hi": { - "name": "मोहनिकाय", - "aliases": [] - }, - "ne": { - "name": "मोहनिकाय", - "aliases": [] - }, - "mn": { - "name": "Мохaникaяa", - "aliases": [] - } - }, - "regions": [ - "Cambodia" - ], - "level": 3, - "parent": "cambodian-buddhism" - }, - { - "id": "lao-buddhism", - "names": { - "en": { - "name": "Lao Buddhism", - "aliases": [ - "Laotian Buddhism", - "Laos Buddhism" - ] - }, - "zh": { - "name": "老挝佛教", - "aliases": [ - "寮国佛教" - ] - }, - "bo": { - "name": "Lao Buddhism", - "aliases": [] - }, - "hi": { - "name": "लाओ बौद्ध धर्म", - "aliases": [ - "लाओस बौद्ध धर्म" - ] - }, - "ne": { - "name": "लाओ बौद्ध धर्म", - "aliases": [] - }, - "mn": { - "name": "Лаосын Буддизм", - "aliases": [] - } - }, - "regions": [ - "Laos" - ], - "level": 2, - "parent": "theravada" - }, - { - "id": "vipassana-goenka", - "names": { - "en": { - "name": "Vipassana (Goenka Tradition)", - "aliases": [ - "Vipassana", - "S.N. Goenka", - "Goenka", - "Dhamma.org" - ] - }, - "zh": { - "name": "内观禅修(葛印卡传统)", - "aliases": [ - "葛印卡内观", - "葛印卡", - "内观" - ] - }, - "bo": { - "name": "Vipassana (Goenka)", - "aliases": [ - "བི་པཤྱ་ན།" - ] - }, - "hi": { - "name": "विपश्यना (गोयनका परंपरा)", - "aliases": [ - "गोयनका", - "एस.एन. गोयनका", - "विपश्यना" - ] - }, - "ne": { - "name": "विपश्यना (गोयंका परम्परा)", - "aliases": [ - "गोयंका", - "विपश्यना" - ] - }, - "mn": { - "name": "Гоенкийн Випассана", - "aliases": [ - "Випасана" - ] - } - }, - "regions": [ - "International" - ], - "level": 2, - "parent": "theravada" - }, - { - "id": "insight-meditation", - "names": { - "en": { - "name": "Insight Meditation", - "aliases": [ - "IMS", - "Spirit Rock", - "Insight Meditation Society" - ] - }, - "zh": { - "name": "内观禅修", - "aliases": [ - "正念禅修", - "IMS" - ] - }, - "bo": { - "name": "Insight Meditation", - "aliases": [ - "ལྷག་མཐོང་བསམ་གཏན།" - ] - }, - "hi": { - "name": "इनसाइट मेडिटेशन", - "aliases": [ - "आईएमएस", - "स्पिरिट रॉक", - "विपश्यना" - ] - }, - "ne": { - "name": "इनसाइट मेडिटेशन", - "aliases": [ - "आईएमएस" - ] - }, - "mn": { - "name": "Ухаарлын Дялан", - "aliases": [ - "IMS" - ] - } - }, - "regions": [ - "International" - ], - "level": 2, - "parent": "theravada" - }, - { - "id": "navayana", - "names": { - "en": { - "name": "Navayana / Ambedkarite Buddhism", - "aliases": [ - "Navayana", - "Ambedkarite Buddhism", - "Neo-Buddhism", - "Dalit Buddhism", - "Jai Bhim", - "Bhimayana" - ] - }, - "zh": { - "name": "新乘佛教", - "aliases": [ - "安贝德卡佛教", - "达利特佛教" - ] - }, - "bo": { - "name": "Navayana", - "aliases": [] - }, - "hi": { - "name": "नवयान", - "aliases": [ - "आंबेडकर बौद्ध धर्म", - "दलित बौद्ध धर्म", - "नव-बौद्ध धर्म", - "बाबासाहेब बौद्ध धर्म", - "जय भीम" - ] - }, - "ne": { - "name": "नवयान", - "aliases": [ - "आंबेडकर बौद्ध धर्म", - "नव-बौद्ध धर्म" - ] - }, - "mn": { - "name": "Навааян", - "aliases": [] - } - }, - "regions": [ - "India" - ], - "level": 2, - "parent": "theravada" - }, - { - "id": "mahayana", - "names": { - "en": { - "name": "Mahāyāna", - "aliases": [ - "Mahayana", - "Northern Buddhism", - "Great Vehicle" - ] - }, - "zh": { - "name": "大乘佛教", - "aliases": [ - "大乘", - "北传佛教" - ] - }, - "bo": { - "name": "ཐེག་པ་ཆེན་པོ།", - "aliases": [ - "ཐེག་ཆེན།", - "Mahayana" - ] - }, - "hi": { - "name": "महायान", - "aliases": [ - "महायान बौद्ध धर्म", - "उत्तरी बौद्ध धर्म" - ] - }, - "ne": { - "name": "महायान", - "aliases": [ - "महायान बौद्ध धर्म" - ] - }, - "mn": { - "name": "Махаяан", - "aliases": [ - "Махаяна", - "Их хөлгөний шашин" - ] - } - }, - "regions": [ - "China", - "Japan", - "Korea", - "Vietnam", - "Taiwan" - ], - "level": 1, - "parent": null - }, - { - "id": "chinese-buddhism", - "names": { - "en": { - "name": "Chinese Buddhism", - "aliases": [ - "Han Buddhism", - "Chinese Mahayana" - ] - }, - "zh": { - "name": "汉传佛教", - "aliases": [ - "中国佛教", - "汉佛教" - ] - }, - "bo": { - "name": "རྒྱ་ནག་གི་ནང་ཆོས།", - "aliases": [ - "Chinese Buddhism" - ] - }, - "hi": { - "name": "चीनी बौद्ध धर्म", - "aliases": [ - "हान बौद्ध धर्म" - ] - }, - "ne": { - "name": "चिनियाँ बौद्ध धर्म", - "aliases": [ - "हान बौद्ध धर्म" - ] - }, - "mn": { - "name": "Хятадын Буддизм", - "aliases": [ - "Ханы Буддизм" - ] - } - }, - "regions": [ - "China", - "Taiwan", - "Singapore", - "Malaysia" - ], - "level": 2, - "parent": "mahayana" - }, - { - "id": "pure-land-chinese", - "names": { - "en": { - "name": "Pure Land (Chinese)", - "aliases": [ - "Jingtu", - "Jìngtǔ Zōng", - "Amitabha Buddhism", - "Chinese Pure Land" - ] - }, - "zh": { - "name": "净土宗", - "aliases": [ - "净土", - "念佛", - "阿弥陀佛信仰" - ] - }, - "bo": { - "name": "གཙང་ཞིང་སྡེ་པ།", - "aliases": [ - "Pure Land", - "Jingtu" - ] - }, - "hi": { - "name": "शुद्ध भूमि (चीनी)", - "aliases": [ - "जिंगतु", - "अमिताभ बौद्ध धर्म" - ] - }, - "ne": { - "name": "शुद्ध भूमि (चिनियाँ)", - "aliases": [ - "जिन्गतु" - ] - }, - "mn": { - "name": "Цэвэр Газрын Сургаал (Хятад)", - "aliases": [ - "Жинту" - ] - } - }, - "regions": [ - "China", - "Taiwan" - ], - "level": 3, - "parent": "chinese-buddhism" - }, - { - "id": "chan", - "names": { - "en": { - "name": "Chan", - "aliases": [ - "Chán", - "Chinese Zen", - "Chan Buddhism" - ] - }, - "zh": { - "name": "禅宗", - "aliases": [ - "禅", - "中国禅" - ] - }, - "bo": { - "name": "Chan", - "aliases": [ - "禅宗" - ] - }, - "hi": { - "name": "चान", - "aliases": [ - "चीनी ज़ेन", - "चान बौद्ध धर्म" - ] - }, - "ne": { - "name": "चान", - "aliases": [ - "चिनियाँ जेन" - ] - }, - "mn": { - "name": "Чань", - "aliases": [ - "Хятадын Зен" - ] - } - }, - "regions": [ - "China", - "Taiwan" - ], - "level": 3, - "parent": "chinese-buddhism" - }, - { - "id": "tiantai", - "names": { - "en": { - "name": "Tiantai", - "aliases": [ - "T'ien-t'ai", - "Tien-tai" - ] - }, - "zh": { - "name": "天台宗", - "aliases": [ - "天台" - ] - }, - "bo": { - "name": "Tiantai", - "aliases": [] - }, - "hi": { - "name": "तियानताई", - "aliases": [] - }, - "ne": { - "name": "तियानताई", - "aliases": [] - }, - "mn": { - "name": "Тяньтай", - "aliases": [] - } - }, - "regions": [ - "China" - ], - "level": 3, - "parent": "chinese-buddhism" - }, - { - "id": "huayan", - "names": { - "en": { - "name": "Huayan", - "aliases": [ - "Hua-yen", - "Flower Garland School", - "Avatamsaka" - ] - }, - "zh": { - "name": "华严宗", - "aliases": [ - "华严", - "花严宗" - ] - }, - "bo": { - "name": "Huayan", - "aliases": [] - }, - "hi": { - "name": "हुआयान", - "aliases": [ - "अवतंसक" - ] - }, - "ne": { - "name": "हुआयान", - "aliases": [] - }, - "mn": { - "name": "Хуаян", - "aliases": [] - } - }, - "regions": [ - "China" - ], - "level": 3, - "parent": "chinese-buddhism" - }, - { - "id": "chinese-esoteric", - "names": { - "en": { - "name": "Chinese Esoteric Buddhism", - "aliases": [ - "Mizong", - "Mìzōng", - "Zhenyan", - "Chinese Vajrayana" - ] - }, - "zh": { - "name": "密宗", - "aliases": [ - "真言宗", - "汉传密教", - "东密" - ] - }, - "bo": { - "name": "Chinese Esoteric Buddhism", - "aliases": [] - }, - "hi": { - "name": "चीनी तांत्रिक बौद्ध धर्म", - "aliases": [ - "मिज़ोंग", - "चीनी वज्रयान" - ] - }, - "ne": { - "name": "चिनियाँ तान्त्रिक बौद्ध धर्म", - "aliases": [] - }, - "mn": { - "name": "Хятадын Нууц Уламжлал", - "aliases": [ - "Мицзун" - ] - } - }, - "regions": [ - "China", - "Taiwan" - ], - "level": 3, - "parent": "chinese-buddhism" - }, - { - "id": "dharma-drum", - "names": { - "en": { - "name": "Dharma Drum Mountain", - "aliases": [ - "DDM", - "Master Shengyen" - ] - }, - "zh": { - "name": "法鼓山", - "aliases": [ - "法鼓", - "圣严法师" - ] - }, - "bo": { - "name": "Dharma Drum Mountain", - "aliases": [] - }, - "hi": { - "name": "धर्म ड्रम माउंटेन", - "aliases": [ - "DDM" - ] - }, - "ne": { - "name": "धर्म ड्रम माउन्टेन", - "aliases": [] - }, - "mn": { - "name": "Дхарма Драм Маунтейн", - "aliases": [ - "法鼓山" - ] - } - }, - "regions": [ - "Taiwan", - "International" - ], - "level": 3, - "parent": "chinese-buddhism" - }, - { - "id": "fo-guang-shan", - "names": { - "en": { - "name": "Fo Guang Shan", - "aliases": [ - "Buddha's Light Mountain", - "Master Hsing Yun", - "BLIA" - ] - }, - "zh": { - "name": "佛光山", - "aliases": [ - "佛光", - "星云法师", - "国际佛光会" - ] - }, - "bo": { - "name": "Fo Guang Shan", - "aliases": [] - }, - "hi": { - "name": "फो गुआंग शान", - "aliases": [ - "BLIA" - ] - }, - "ne": { - "name": "फो गुआंग शान", - "aliases": [] - }, - "mn": { - "name": "Фо Гуан Шань", - "aliases": [ - "佛光山" - ] - } - }, - "regions": [ - "Taiwan", - "International" - ], - "level": 3, - "parent": "chinese-buddhism" - }, - { - "id": "chung-tai-shan", - "names": { - "en": { - "name": "Chung Tai Shan", - "aliases": [ - "Zhongtaishan" - ] - }, - "zh": { - "name": "中台山", - "aliases": [ - "中台禅寺" - ] - }, - "bo": { - "name": "Chung Tai Shan", - "aliases": [] - }, - "hi": { - "name": "चुंग ताई शान", - "aliases": [] - }, - "ne": { - "name": "चुंग ताई शान", - "aliases": [] - }, - "mn": { - "name": "Чун Тай Шань", - "aliases": [] - } - }, - "regions": [ - "Taiwan" - ], - "level": 3, - "parent": "chinese-buddhism" - }, - { - "id": "japanese-buddhism", - "names": { - "en": { - "name": "Japanese Buddhism", - "aliases": [ - "Japanese Mahayana" - ] - }, - "zh": { - "name": "日本佛教", - "aliases": [] - }, - "bo": { - "name": "ཇ་པན་གྱི་ནང་ཆོས།", - "aliases": [ - "Japanese Buddhism" - ] - }, - "hi": { - "name": "जापानी बौद्ध धर्म", - "aliases": [] - }, - "ne": { - "name": "जापानी बौद्ध धर्म", - "aliases": [] - }, - "mn": { - "name": "Японы Буддизм", - "aliases": [] - } - }, - "regions": [ - "Japan" - ], - "level": 2, - "parent": "mahayana" - }, - { - "id": "zen", - "names": { - "en": { - "name": "Zen", - "aliases": [ - "Zen Buddhism", - "Japanese Zen", - "Zazen" - ] - }, - "zh": { - "name": "禅(日本)", - "aliases": [ - "日本禅", - "禅宗" - ] - }, - "bo": { - "name": "Zen", - "aliases": [ - "གཟན།" - ] - }, - "hi": { - "name": "ज़ेन", - "aliases": [ - "जापानी ज़ेन", - "ज़ेन बौद्ध धर्म" - ] - }, - "ne": { - "name": "जेन", - "aliases": [ - "जापानी जेन" - ] - }, - "mn": { - "name": "Зен", - "aliases": [ - "Японы Зен" - ] - } - }, - "regions": [ - "Japan", - "International" - ], - "level": 3, - "parent": "japanese-buddhism" - }, - { - "id": "rinzai", - "names": { - "en": { - "name": "Rinzai", - "aliases": [ - "Rinzai Zen", - "Rinzaishū" - ] - }, - "zh": { - "name": "临济宗", - "aliases": [ - "临济" - ] - }, - "bo": { - "name": "Rinzai", - "aliases": [] - }, - "hi": { - "name": "रिनज़ाई", - "aliases": [ - "रिंज़ाई ज़ेन" - ] - }, - "ne": { - "name": "रिन्जाई", - "aliases": [] - }, - "mn": { - "name": "Ринзай", - "aliases": [] - } - }, - "regions": [ - "Japan", - "International" - ], - "level": 4, - "parent": "zen" - }, - { - "id": "soto", - "names": { - "en": { - "name": "Sōtō", - "aliases": [ - "Soto", - "Soto Zen", - "Sōtō Zen", - "Sōtō-shū" - ] - }, - "zh": { - "name": "曹洞宗", - "aliases": [ - "曹洞" - ] - }, - "bo": { - "name": "Soto", - "aliases": [] - }, - "hi": { - "name": "सोटो", - "aliases": [ - "सोटो ज़ेन" - ] - }, - "ne": { - "name": "सोतो", - "aliases": [ - "सोतो जेन" - ] - }, - "mn": { - "name": "Сото", - "aliases": [] - } - }, - "regions": [ - "Japan", - "International" - ], - "level": 4, - "parent": "zen" - }, - { - "id": "obaku", - "names": { - "en": { - "name": "Ōbaku", - "aliases": [ - "Obaku", - "Ōbaku Zen" - ] - }, - "zh": { - "name": "黄檗宗", - "aliases": [ - "黄檗" - ] - }, - "bo": { - "name": "Obaku", - "aliases": [] - }, - "hi": { - "name": "ओबाकु", - "aliases": [] - }, - "ne": { - "name": "ओबाकु", - "aliases": [] - }, - "mn": { - "name": "Обаку", - "aliases": [] - } - }, - "regions": [ - "Japan" - ], - "level": 4, - "parent": "zen" - }, - { - "id": "sanbo-zen", - "names": { - "en": { - "name": "Sanbo Zen", - "aliases": [ - "Sanbo Kyodan", - "Three Treasures Zen" - ] - }, - "zh": { - "name": "三宝禅", - "aliases": [ - "三宝教团" - ] - }, - "bo": { - "name": "Sanbo Zen", - "aliases": [] - }, - "hi": { - "name": "सानबो ज़ेन", - "aliases": [ - "सानबो क्योदान" - ] - }, - "ne": { - "name": "सानबो जेन", - "aliases": [] - }, - "mn": { - "name": "Санбо Зен", - "aliases": [] - } - }, - "regions": [ - "Japan", - "International" - ], - "level": 4, - "parent": "zen" - }, - { - "id": "jodo-shu", - "names": { - "en": { - "name": "Jōdo-shū", - "aliases": [ - "Jodo-shu", - "Jodo Shu", - "Pure Land Japan", - "Honen lineage" - ] - }, - "zh": { - "name": "净土宗(日本)", - "aliases": [ - "法然净土宗" - ] - }, - "bo": { - "name": "Jodo-shu", - "aliases": [] - }, - "hi": { - "name": "जोडो-शू", - "aliases": [ - "जापानी शुद्ध भूमि" - ] - }, - "ne": { - "name": "जोडो-शू", - "aliases": [] - }, - "mn": { - "name": "Жодо Шю", - "aliases": [] - } - }, - "regions": [ - "Japan" - ], - "level": 3, - "parent": "japanese-buddhism" - }, - { - "id": "jodo-shinshu", - "names": { - "en": { - "name": "Jōdo Shinshū", - "aliases": [ - "Jodo Shinshu", - "Shin Buddhism", - "True Pure Land", - "Shin", - "Shinshu" - ] - }, - "zh": { - "name": "净土真宗", - "aliases": [ - "真宗", - "本愿寺派", - "亲鸾净土" - ] - }, - "bo": { - "name": "Jodo Shinshu", - "aliases": [ - "Shin Buddhism" - ] - }, - "hi": { - "name": "जोडो शिनशू", - "aliases": [ - "शिन बौद्ध धर्म", - "सच्ची शुद्ध भूमि" - ] - }, - "ne": { - "name": "जोडो शिन्शू", - "aliases": [ - "शिन बौद्ध धर्म" - ] - }, - "mn": { - "name": "Жодо Шиншү", - "aliases": [ - "Шин Буддизм" - ] - } - }, - "regions": [ - "Japan", - "International" - ], - "level": 3, - "parent": "japanese-buddhism" - }, - { - "id": "nishi-honganji", - "names": { - "en": { - "name": "Nishi Honganji", - "aliases": [ - "West Honganji", - "Hongwanji", - "Honpa Hongwanji" - ] - }, - "zh": { - "name": "西本愿寺", - "aliases": [ - "本愿寺派" - ] - }, - "bo": { - "name": "Nishi Honganji", - "aliases": [] - }, - "hi": { - "name": "निशी होनगांजी", - "aliases": [] - }, - "ne": { - "name": "निशी होन्गान्जी", - "aliases": [] - }, - "mn": { - "name": "Ниши Хонганжи", - "aliases": [] - } - }, - "regions": [ - "Japan", - "International" - ], - "level": 4, - "parent": "jodo-shinshu" - }, - { - "id": "higashi-honganji", - "names": { - "en": { - "name": "Higashi Honganji", - "aliases": [ - "East Honganji", - "Otani" - ] - }, - "zh": { - "name": "东本愿寺", - "aliases": [ - "大谷派" - ] - }, - "bo": { - "name": "Higashi Honganji", - "aliases": [] - }, - "hi": { - "name": "हिगाशी होनगांजी", - "aliases": [] - }, - "ne": { - "name": "हिगाशी होन्गान्जी", - "aliases": [] - }, - "mn": { - "name": "Хигаши Хонганжи", - "aliases": [] - } - }, - "regions": [ - "Japan" - ], - "level": 4, - "parent": "jodo-shinshu" - }, - { - "id": "bca", - "names": { - "en": { - "name": "Buddhist Churches of America", - "aliases": [ - "BCA", - "American Jodo Shinshu" - ] - }, - "zh": { - "name": "美国佛教会", - "aliases": [ - "美国净土真宗" - ] - }, - "bo": { - "name": "Buddhist Churches of America", - "aliases": [ - "BCA" - ] - }, - "hi": { - "name": "बौद्ध चर्च ऑफ अमेरिका", - "aliases": [ - "BCA" - ] - }, - "ne": { - "name": "बौद्ध चर्च अफ अमेरिका", - "aliases": [ - "BCA" - ] - }, - "mn": { - "name": "Америкийн Буддын Сүмүүд", - "aliases": [ - "BCA" - ] - } - }, - "regions": [ - "United States" - ], - "level": 4, - "parent": "jodo-shinshu" - }, - { - "id": "nichiren", - "names": { - "en": { - "name": "Nichiren Buddhism", - "aliases": [ - "Nichiren", - "Lotus Sutra Buddhism" - ] - }, - "zh": { - "name": "日莲佛法", - "aliases": [ - "日莲宗", - "法华宗" - ] - }, - "bo": { - "name": "Nichiren", - "aliases": [] - }, - "hi": { - "name": "निचिरेन बौद्ध धर्म", - "aliases": [ - "लोटस सूत्र बौद्ध धर्म", - "निचिरेन" - ] - }, - "ne": { - "name": "निचिरेन बौद्ध धर्म", - "aliases": [ - "निचिरेन" - ] - }, - "mn": { - "name": "Ничирен Буддизм", - "aliases": [] - } - }, - "regions": [ - "Japan", - "International" - ], - "level": 3, - "parent": "japanese-buddhism" - }, - { - "id": "nichiren-shu", - "names": { - "en": { - "name": "Nichiren Shū", - "aliases": [ - "Nichiren Shu", - "Nichirenshu" - ] - }, - "zh": { - "name": "日莲宗", - "aliases": [] - }, - "bo": { - "name": "Nichiren Shu", - "aliases": [] - }, - "hi": { - "name": "निचिरेन शू", - "aliases": [] - }, - "ne": { - "name": "निचिरेन शू", - "aliases": [] - }, - "mn": { - "name": "Ничирен Шю", - "aliases": [] - } - }, - "regions": [ - "Japan" - ], - "level": 4, - "parent": "nichiren" - }, - { - "id": "nichiren-shoshu", - "names": { - "en": { - "name": "Nichiren Shōshū", - "aliases": [ - "Nichiren Shoshu" - ] - }, - "zh": { - "name": "日莲正宗", - "aliases": [] - }, - "bo": { - "name": "Nichiren Shoshu", - "aliases": [] - }, - "hi": { - "name": "निचिरेन शोशू", - "aliases": [] - }, - "ne": { - "name": "निचिरेन शोशू", - "aliases": [] - }, - "mn": { - "name": "Ничирен Шошү", - "aliases": [] - } - }, - "regions": [ - "Japan", - "International" - ], - "level": 4, - "parent": "nichiren" - }, - { - "id": "sgi", - "names": { - "en": { - "name": "Sōka Gakkai International", - "aliases": [ - "SGI", - "Soka Gakkai", - "Sōka Gakkai", - "Value Creation Society" - ] - }, - "zh": { - "name": "创价学会", - "aliases": [ - "国际创价学会", - "SGI" - ] - }, - "bo": { - "name": "SGI", - "aliases": [ - "Soka Gakkai" - ] - }, - "hi": { - "name": "सोका गक्कई इंटरनेशनल", - "aliases": [ - "एसजीआई", - "सोका गक्कई" - ] - }, - "ne": { - "name": "सोका गक्काई इन्टरनेशनल", - "aliases": [ - "एसजीआई", - "सोका गक्काई" - ] - }, - "mn": { - "name": "Сока Гаккай Интернэшнл", - "aliases": [ - "СГИ" - ] - } - }, - "regions": [ - "Japan", - "International" - ], - "level": 4, - "parent": "nichiren" - }, - { - "id": "kempon-hokke", - "names": { - "en": { - "name": "Kempon Hokke", - "aliases": [] - }, - "zh": { - "name": "顕本法华宗", - "aliases": [] - }, - "bo": { - "name": "Kempon Hokke", - "aliases": [] - }, - "hi": { - "name": "केम्पोन होक्के", - "aliases": [] - }, - "ne": { - "name": "केम्पोन होक्के", - "aliases": [] - }, - "mn": { - "name": "Кемпон Хокке", - "aliases": [] - } - }, - "regions": [ - "Japan" - ], - "level": 4, - "parent": "nichiren" - }, - { - "id": "tendai", - "names": { - "en": { - "name": "Tendai", - "aliases": [ - "Tendaishū", - "Japanese Tiantai" - ] - }, - "zh": { - "name": "天台宗(日本)", - "aliases": [ - "延历寺", - "日本天台" - ] - }, - "bo": { - "name": "Tendai", - "aliases": [] - }, - "hi": { - "name": "तेंदाई", - "aliases": [ - "जापानी तियानताई" - ] - }, - "ne": { - "name": "तेन्दाई", - "aliases": [] - }, - "mn": { - "name": "Тэндай", - "aliases": [] - } - }, - "regions": [ - "Japan" - ], - "level": 3, - "parent": "japanese-buddhism" - }, - { - "id": "shingon", - "names": { - "en": { - "name": "Shingon", - "aliases": [ - "Shingon Buddhism", - "Japanese Vajrayana", - "Japanese Esoteric Buddhism", - "Mikkyō" - ] - }, - "zh": { - "name": "真言宗", - "aliases": [ - "日本密宗", - "东密" - ] - }, - "bo": { - "name": "Shingon", - "aliases": [ - "གཤིང་གོན།" - ] - }, - "hi": { - "name": "शिंगोन", - "aliases": [ - "जापानी वज्रयान", - "जापानी तांत्रिक बौद्ध धर्म" - ] - }, - "ne": { - "name": "शिन्गोन", - "aliases": [ - "जापानी वज्रयान" - ] - }, - "mn": { - "name": "Шингон", - "aliases": [ - "Японы Тантрын Буддизм" - ] - } - }, - "regions": [ - "Japan" - ], - "level": 3, - "parent": "japanese-buddhism" - }, - { - "id": "koyasan-shingon", - "names": { - "en": { - "name": "Koyasan Shingon", - "aliases": [ - "Koyasan", - "Kōyasan", - "Mt. Koya" - ] - }, - "zh": { - "name": "高野山真言宗", - "aliases": [ - "高野山" - ] - }, - "bo": { - "name": "Koyasan Shingon", - "aliases": [] - }, - "hi": { - "name": "कोयासान शिंगोन", - "aliases": [ - "कोया पर्वत" - ] - }, - "ne": { - "name": "कोयासान शिन्गोन", - "aliases": [] - }, - "mn": { - "name": "Коясан Шингон", - "aliases": [] - } - }, - "regions": [ - "Japan" - ], - "level": 4, - "parent": "shingon" - }, - { - "id": "rissho-koseikai", - "names": { - "en": { - "name": "Risshō Kōsei-kai", - "aliases": [ - "Rissho Koseikai", - "RKK" - ] - }, - "zh": { - "name": "立正佼成会", - "aliases": [] - }, - "bo": { - "name": "Rissho Koseikai", - "aliases": [] - }, - "hi": { - "name": "रिश्शो कोसेई-काई", - "aliases": [ - "RKK" - ] - }, - "ne": { - "name": "रिश्शो कोसेई-काई", - "aliases": [] - }, - "mn": { - "name": "Риссхо Косеикай", - "aliases": [] - } - }, - "regions": [ - "Japan", - "International" - ], - "level": 3, - "parent": "japanese-buddhism" - }, - { - "id": "korean-buddhism", - "names": { - "en": { - "name": "Korean Buddhism", - "aliases": [ - "Korean Mahayana" - ] - }, - "zh": { - "name": "韩国佛教", - "aliases": [ - "朝鲜佛教", - "韩国大乘" - ] - }, - "bo": { - "name": "ཀོ་རི་ཡའི་ནང་ཆོས།", - "aliases": [ - "Korean Buddhism" - ] - }, - "hi": { - "name": "कोरियाई बौद्ध धर्म", - "aliases": [] - }, - "ne": { - "name": "कोरियाली बौद्ध धर्म", - "aliases": [] - }, - "mn": { - "name": "Солонгосын Буддизм", - "aliases": [] - } - }, - "regions": [ - "South Korea" - ], - "level": 2, - "parent": "mahayana" - }, - { - "id": "jogye", - "names": { - "en": { - "name": "Jogye Order", - "aliases": [ - "Chogye", - "Jogye-jong", - "Korean Seon", - "Korean Zen" - ] - }, - "zh": { - "name": "曹溪宗", - "aliases": [ - "韩国禅宗", - "朝鲜禅宗" - ] - }, - "bo": { - "name": "Jogye", - "aliases": [] - }, - "hi": { - "name": "जोग्ये ऑर्डर", - "aliases": [ - "कोरियाई सेओन", - "कोरियाई ज़ेन" - ] - }, - "ne": { - "name": "जोग्ये आर्डर", - "aliases": [] - }, - "mn": { - "name": "Жогьё Орден", - "aliases": [ - "Солонгосын Зен" - ] - } - }, - "regions": [ - "South Korea" - ], - "level": 3, - "parent": "korean-buddhism" - }, - { - "id": "taego", - "names": { - "en": { - "name": "Taego Order", - "aliases": [ - "Taego-jong" - ] - }, - "zh": { - "name": "太古宗", - "aliases": [] - }, - "bo": { - "name": "Taego", - "aliases": [] - }, - "hi": { - "name": "तेगो ऑर्डर", - "aliases": [] - }, - "ne": { - "name": "तेगो आर्डर", - "aliases": [] - }, - "mn": { - "name": "Тэго Орден", - "aliases": [] - } - }, - "regions": [ - "South Korea" - ], - "level": 3, - "parent": "korean-buddhism" - }, - { - "id": "cheontae", - "names": { - "en": { - "name": "Cheontae Order", - "aliases": [ - "Chontae", - "Korean Tendai", - "Cheontae-jong" - ] - }, - "zh": { - "name": "天台宗(韩国)", - "aliases": [ - "韩国天台" - ] - }, - "bo": { - "name": "Cheontae", - "aliases": [] - }, - "hi": { - "name": "चेओंताए ऑर्डर", - "aliases": [ - "कोरियाई तेंदाई" - ] - }, - "ne": { - "name": "चेओन्ताए आर्डर", - "aliases": [] - }, - "mn": { - "name": "Чёнтэ Орден", - "aliases": [] - } - }, - "regions": [ - "South Korea" - ], - "level": 3, - "parent": "korean-buddhism" - }, - { - "id": "won-buddhism", - "names": { - "en": { - "name": "Won Buddhism", - "aliases": [ - "Wonbulgyo", - "Circle Buddhism" - ] - }, - "zh": { - "name": "圆佛教", - "aliases": [ - "圆佛", - "园佛教" - ] - }, - "bo": { - "name": "Won Buddhism", - "aliases": [] - }, - "hi": { - "name": "वोन बौद्ध धर्म", - "aliases": [ - "वृत्त बौद्ध धर्म" - ] - }, - "ne": { - "name": "वोन बौद्ध धर्म", - "aliases": [] - }, - "mn": { - "name": "Вон Буддизм", - "aliases": [] - } - }, - "regions": [ - "South Korea", - "International" - ], - "level": 3, - "parent": "korean-buddhism" - }, - { - "id": "jingak", - "names": { - "en": { - "name": "Jingak Order", - "aliases": [ - "Jingak-jong", - "Korean Vajrayana" - ] - }, - "zh": { - "name": "真觉宗", - "aliases": [ - "韩国密宗" - ] - }, - "bo": { - "name": "Jingak", - "aliases": [] - }, - "hi": { - "name": "जिंगाक ऑर्डर", - "aliases": [ - "कोरियाई वज्रयान" - ] - }, - "ne": { - "name": "जिन्गाक आर्डर", - "aliases": [] - }, - "mn": { - "name": "Жингак Орден", - "aliases": [] - } - }, - "regions": [ - "South Korea" - ], - "level": 3, - "parent": "korean-buddhism" - }, - { - "id": "vietnamese-buddhism", - "names": { - "en": { - "name": "Vietnamese Buddhism", - "aliases": [ - "Vietnamese Mahayana" - ] - }, - "zh": { - "name": "越南佛教", - "aliases": [] - }, - "bo": { - "name": "Vietnamese Buddhism", - "aliases": [] - }, - "hi": { - "name": "वियतनामी बौद्ध धर्म", - "aliases": [] - }, - "ne": { - "name": "भियतनामी बौद्ध धर्म", - "aliases": [] - }, - "mn": { - "name": "Вьетнамын Буддизм", - "aliases": [] - } - }, - "regions": [ - "Vietnam" - ], - "level": 2, - "parent": "mahayana" - }, - { - "id": "thien", - "names": { - "en": { - "name": "Thiền", - "aliases": [ - "Thien", - "Vietnamese Zen" - ] - }, - "zh": { - "name": "禅宗(越南)", - "aliases": [ - "越南禅" - ] - }, - "bo": { - "name": "Thien", - "aliases": [] - }, - "hi": { - "name": "थिएन", - "aliases": [ - "वियतनामी ज़ेन" - ] - }, - "ne": { - "name": "थिएन", - "aliases": [] - }, - "mn": { - "name": "Тхиен", - "aliases": [ - "Вьетнамын Зен" - ] - } - }, - "regions": [ - "Vietnam", - "International" - ], - "level": 3, - "parent": "vietnamese-buddhism" - }, - { - "id": "truc-lam", - "names": { - "en": { - "name": "Trúc Lâm", - "aliases": [ - "Truc Lam", - "Bamboo Forest School" - ] - }, - "zh": { - "name": "竹林禅派", - "aliases": [ - "竹林派" - ] - }, - "bo": { - "name": "Truc Lam", - "aliases": [] - }, - "hi": { - "name": "त्रुक लाम", - "aliases": [ - "बांस वन विद्यालय" - ] - }, - "ne": { - "name": "त्रुक लाम", - "aliases": [] - }, - "mn": { - "name": "Трук Лам", - "aliases": [] - } - }, - "regions": [ - "Vietnam" - ], - "level": 4, - "parent": "thien" - }, - { - "id": "lam-te", - "names": { - "en": { - "name": "Lâm Tế", - "aliases": [ - "Lam Te", - "Vietnamese Linji" - ] - }, - "zh": { - "name": "临济宗(越南)", - "aliases": [ - "越南临济" - ] - }, - "bo": { - "name": "Lam Te", - "aliases": [] - }, - "hi": { - "name": "लाम ते", - "aliases": [ - "वियतनामी रिनज़ाई" - ] - }, - "ne": { - "name": "लाम ते", - "aliases": [] - }, - "mn": { - "name": "Лам Тэ", - "aliases": [] - } - }, - "regions": [ - "Vietnam" - ], - "level": 4, - "parent": "thien" - }, - { - "id": "tinh-do", - "names": { - "en": { - "name": "Tịnh Độ", - "aliases": [ - "Tinh Do", - "Vietnamese Pure Land" - ] - }, - "zh": { - "name": "净土宗(越南)", - "aliases": [ - "越南净土" - ] - }, - "bo": { - "name": "Tinh Do", - "aliases": [] - }, - "hi": { - "name": "तिन्ह दो", - "aliases": [ - "वियतनामी शुद्ध भूमि" - ] - }, - "ne": { - "name": "तिन्ह दो", - "aliases": [] - }, - "mn": { - "name": "Тинь До", - "aliases": [] - } - }, - "regions": [ - "Vietnam" - ], - "level": 3, - "parent": "vietnamese-buddhism" - }, - { - "id": "khat-si", - "names": { - "en": { - "name": "Khất Sĩ Order", - "aliases": [ - "Khat Si", - "Mendicant Order" - ] - }, - "zh": { - "name": "越南比丘团", - "aliases": [] - }, - "bo": { - "name": "Khat Si", - "aliases": [] - }, - "hi": { - "name": "खात सी ऑर्डर", - "aliases": [] - }, - "ne": { - "name": "खात सी आर्डर", - "aliases": [] - }, - "mn": { - "name": "Кхат Си Орден", - "aliases": [] - } - }, - "regions": [ - "Vietnam" - ], - "level": 3, - "parent": "vietnamese-buddhism" - }, - { - "id": "plum-village", - "names": { - "en": { - "name": "Plum Village Community", - "aliases": [ - "Plum Village", - "Thich Nhat Hanh lineage", - "Order of Interbeing", - "Engaged Buddhism" - ] - }, - "zh": { - "name": "梅村", - "aliases": [ - "一行禅师传统", - "相即共同体", - "入世佛教" - ] - }, - "bo": { - "name": "Plum Village", - "aliases": [ - "ཤིང་ཏོག་གྲོང་འདུས།" - ] - }, - "hi": { - "name": "प्लम विलेज", - "aliases": [ - "थिच नात हान परंपरा", - "इंटरबीइंग ऑर्डर", - "एंगेज्ड बौद्ध धर्म" - ] - }, - "ne": { - "name": "प्लम भिलेज", - "aliases": [ - "थिच न्हात हान परम्परा" - ] - }, - "mn": { - "name": "Плам Виллаж", - "aliases": [ - "Тик Нат Ханы Уламжлал" - ] - } - }, - "regions": [ - "Vietnam", - "International" - ], - "level": 3, - "parent": "vietnamese-buddhism" - }, - { - "id": "vajrayana", - "names": { - "en": { - "name": "Vajrayāna", - "aliases": [ - "Vajrayana", - "Tibetan Buddhism", - "Tantric Buddhism", - "Diamond Vehicle" - ] - }, - "zh": { - "name": "金刚乘", - "aliases": [ - "密乘", - "密教", - "藏密" - ] - }, - "bo": { - "name": "རྡོ་རྗེའི་ཐེག་པ།", - "aliases": [ - "གསང་སྔགས་ཐེག་པ།", - "Vajrayana" - ] - }, - "hi": { - "name": "वज्रयान", - "aliases": [ - "तांत्रिक बौद्ध धर्म", - "वज्र मार्ग", - "हीरा मार्ग" - ] - }, - "ne": { - "name": "वज्रयान", - "aliases": [ - "तान्त्रिक बौद्ध धर्म" - ] - }, - "mn": { - "name": "Ваажраяан", - "aliases": [ - "Важраяна", - "Тантрын Буддизм", - "Алмазан Тэрэг" - ] - } - }, - "regions": [ - "Tibet", - "Bhutan", - "Mongolia", - "Nepal", - "India", - "International" - ], - "level": 1, - "parent": null - }, - { - "id": "tibetan-buddhism", - "names": { - "en": { - "name": "Tibetan Buddhism", - "aliases": [ - "Tibetan", - "Himalayan Buddhism", - "Lamaism" - ] - }, - "zh": { - "name": "藏传佛教", - "aliases": [ - "西藏佛教", - "喇嘛教" - ] - }, - "bo": { - "name": "བོད་ཀྱི་ནང་ཆོས།", - "aliases": [ - "བོད་ཆོས།", - "Tibetan Buddhism" - ] - }, - "hi": { - "name": "तिब्बती बौद्ध धर्म", - "aliases": [ - "हिमालयी बौद्ध धर्म", - "लामावाद" - ] - }, - "ne": { - "name": "तिब्बती बौद्ध धर्म", - "aliases": [ - "हिमालयी बौद्ध धर्म" - ] - }, - "mn": { - "name": "Төвдийн Буддизм", - "aliases": [ - "Ламаизм", - "Ламын шашин" - ] - } - }, - "regions": [ - "Tibet", - "Bhutan", - "Nepal", - "India" - ], - "level": 2, - "parent": "vajrayana" - }, - { - "id": "nyingma", - "names": { - "en": { - "name": "Nyingma", - "aliases": [ - "Nyingmapa", - "Ancient School", - "Old School", - "Red Hat (Nyingma)", - "Dzogchen", - "Great Perfection", - "Dzogpa Chenpo" - ] - }, - "zh": { - "name": "宁玛派", - "aliases": [ - "宁玛", - "古派", - "红教" - ] - }, - "bo": { - "name": "རྙིང་མ་པ།", - "aliases": [ - "རྙིང་མ།", - "སྔ་འགྱུར།" - ] - }, - "hi": { - "name": "न्यिंगमा", - "aliases": [ - "न्यिंगमापा", - "पुरानी परंपरा", - "लाल टोपी" - ] - }, - "ne": { - "name": "न्यिङ्गमा", - "aliases": [ - "न्यिङ्गमापा" - ] - }, - "mn": { - "name": "Нингма", - "aliases": [ - "Нингмапа", - "Хуучин Сургуул" - ] - } - }, - "regions": [ - "Tibet", - "Bhutan", - "Nepal", - "India", - "International" - ], - "level": 3, - "parent": "tibetan-buddhism" - }, - { - "id": "katok", - "names": { - "en": { - "name": "Katok", - "aliases": [ - "Kathok", - "Katok Monastery" - ] - }, - "zh": { - "name": "噶陀寺", - "aliases": [ - "噶陀" - ] - }, - "bo": { - "name": "ཀ་ཐོག", - "aliases": [ - "ཀ་ཐོག་དགོན་པ།" - ] - }, - "hi": { - "name": "कातोक", - "aliases": [] - }, - "ne": { - "name": "कातोक", - "aliases": [] - }, - "mn": { - "name": "Катог", - "aliases": [] - } - }, - "regions": [ - "Tibet" - ], - "level": 4, - "parent": "nyingma" - }, - { - "id": "palyul", - "names": { - "en": { - "name": "Palyul", - "aliases": [ - "Palyul Monastery" - ] - }, - "zh": { - "name": "白玉寺", - "aliases": [ - "白玉" - ] - }, - "bo": { - "name": "དཔལ་ཡུལ།", - "aliases": [ - "དཔལ་ཡུལ་དགོན་པ།" - ] - }, - "hi": { - "name": "पाल्युल", - "aliases": [] - }, - "ne": { - "name": "पाल्युल", - "aliases": [] - }, - "mn": { - "name": "Палюл", - "aliases": [] - } - }, - "regions": [ - "Tibet", - "International" - ], - "level": 4, - "parent": "nyingma" - }, - { - "id": "mindrolling", - "names": { - "en": { - "name": "Mindrolling", - "aliases": [ - "Mindroling", - "Mindrolling Monastery" - ] - }, - "zh": { - "name": "敏珠林寺", - "aliases": [ - "敏珠林" - ] - }, - "bo": { - "name": "སྨིན་གྲོལ་གླིང་།", - "aliases": [ - "སྨིན་གྲོལ།" - ] - }, - "hi": { - "name": "मिनद्रोलिंग", - "aliases": [] - }, - "ne": { - "name": "मिनद्रोलिङ", - "aliases": [] - }, - "mn": { - "name": "Миндролинг", - "aliases": [] - } - }, - "regions": [ - "Tibet", - "India", - "International" - ], - "level": 4, - "parent": "nyingma" - }, - { - "id": "shechen", - "names": { - "en": { - "name": "Shechen", - "aliases": [ - "Shechen Monastery" - ] - }, - "zh": { - "name": "雪谦寺", - "aliases": [ - "雪谦" - ] - }, - "bo": { - "name": "ཞེ་ཆེན།", - "aliases": [ - "ཞེ་ཆེན་དགོན་པ།" - ] - }, - "hi": { - "name": "शेचेन", - "aliases": [] - }, - "ne": { - "name": "शेचेन", - "aliases": [] - }, - "mn": { - "name": "Шечен", - "aliases": [] - } - }, - "regions": [ - "Tibet", - "Nepal", - "International" - ], - "level": 4, - "parent": "nyingma" - }, - { - "id": "dorje-drag", - "names": { - "en": { - "name": "Dorje Drak", - "aliases": [ - "Dorje Drag", - "Dorjé Drak" - ] - }, - "zh": { - "name": "多吉扎寺", - "aliases": [ - "多吉扎" - ] - }, - "bo": { - "name": "རྡོ་རྗེ་བྲག", - "aliases": [ - "རྡོ་རྗེ་བྲག་དགོན།" - ] - }, - "hi": { - "name": "दोर्जे ड्राग", - "aliases": [] - }, - "ne": { - "name": "दोर्जे ड्राग", - "aliases": [] - }, - "mn": { - "name": "Дорже Драг", - "aliases": [] - } - }, - "regions": [ - "Tibet" - ], - "level": 4, - "parent": "nyingma" - }, - { - "id": "dzogchen-monastery", - "names": { - "en": { - "name": "Dzogchen Monastery", - "aliases": [ - "Dzogchen Gompa", - "Rudam", - "Ru-Dam Orgyen Samten Choling" - ] - }, - "zh": { - "name": "佐钦寺", - "aliases": [ - "佐钦" - ] - }, - "bo": { - "name": "རྫོགས་ཆེན་དགོན་པ།", - "aliases": [ - "རྫོགས་ཆེན།" - ] - }, - "hi": { - "name": "ज़ोगचेन", - "aliases": [] - }, - "ne": { - "name": "ज़ोगचेन", - "aliases": [] - }, - "mn": { - "name": "Дзогчен", - "aliases": [] - } - }, - "regions": [ - "Tibet" - ], - "level": 4, - "parent": "nyingma" - }, - { - "id": "kagyu", - "names": { - "en": { - "name": "Kagyu", - "aliases": [ - "Kagyupa", - "Kagyu School", - "Oral Transmission School" - ] - }, - "zh": { - "name": "噶举派", - "aliases": [ - "噶举", - "白教" - ] - }, - "bo": { - "name": "བཀའ་བརྒྱུད།", - "aliases": [ - "བཀའ་བརྒྱུད་པ།" - ] - }, - "hi": { - "name": "काग्यू", - "aliases": [ - "काग्यूपा" - ] - }, - "ne": { - "name": "काग्यु", - "aliases": [ - "काग्युपा" - ] - }, - "mn": { - "name": "Кагью", - "aliases": [ - "Кагьюпа" - ] - } - }, - "regions": [ - "Tibet", - "Bhutan", - "Nepal", - "India", - "International" - ], - "level": 3, - "parent": "tibetan-buddhism" - }, - { - "id": "karma-kagyu", - "names": { - "en": { - "name": "Karma Kagyu", - "aliases": [ - "Karma Kagyü", - "Black Hat Kagyu", - "Karmapa lineage", - "Kamtsang Kagyu" - ] - }, - "zh": { - "name": "噶玛噶举", - "aliases": [ - "噶玛巴传承", - "黑帽噶举" - ] - }, - "bo": { - "name": "ཀར་མ་བཀའ་བརྒྱུད།", - "aliases": [ - "ཀར་མ་པའི་བརྒྱུད་འཛིན།" - ] - }, - "hi": { - "name": "कर्मा काग्यू", - "aliases": [ - "कर्मापा परंपरा", - "काले टोपी काग्यू" - ] - }, - "ne": { - "name": "कर्म काग्यु", - "aliases": [ - "कर्मापा परम्परा" - ] - }, - "mn": { - "name": "Карма Кагью", - "aliases": [ - "Кармапа", - "Хар малгайтан" - ] - } - }, - "regions": [ - "Tibet", - "International" - ], - "level": 4, - "parent": "kagyu" - }, - { - "id": "drikung-kagyu", - "names": { - "en": { - "name": "Drikung Kagyu", - "aliases": [ - "Drigung Kagyu", - "Drikung Kagyü" - ] - }, - "zh": { - "name": "直贡噶举", - "aliases": [ - "直贡" - ] - }, - "bo": { - "name": "འབྲི་གུང་བཀའ་བརྒྱུད།", - "aliases": [ - "འབྲི་གུང་།" - ] - }, - "hi": { - "name": "द्रिकुंग काग्यू", - "aliases": [ - "दृग्गुंग काग्यू" - ] - }, - "ne": { - "name": "द्रिकुङ काग्यु", - "aliases": [] - }, - "mn": { - "name": "Дрикунг Кагью", - "aliases": [] - } - }, - "regions": [ - "Tibet", - "International" - ], - "level": 4, - "parent": "kagyu" - }, - { - "id": "drukpa-kagyu", - "names": { - "en": { - "name": "Drukpa Kagyu", - "aliases": [ - "Drukpa Kagyü", - "Dragon School", - "Bhutan Buddhism" - ] - }, - "zh": { - "name": "竹巴噶举", - "aliases": [ - "竹巴", - "不丹佛教" - ] - }, - "bo": { - "name": "འབྲུག་པ་བཀའ་བརྒྱུད།", - "aliases": [ - "འབྲུག་པ།" - ] - }, - "hi": { - "name": "द्रुकपा काग्यू", - "aliases": [ - "भूटान बौद्ध धर्म", - "ड्रैगन विद्यालय" - ] - }, - "ne": { - "name": "द्रुक्पा काग्यु", - "aliases": [ - "भुटानी बौद्ध धर्म" - ] - }, - "mn": { - "name": "Друкпа Кагью", - "aliases": [ - "Бутаны Буддизм" - ] - } - }, - "regions": [ - "Bhutan", - "Tibet", - "Nepal", - "International" - ], - "level": 4, - "parent": "kagyu" - }, - { - "id": "shangpa-kagyu", - "names": { - "en": { - "name": "Shangpa Kagyu", - "aliases": [ - "Shangpa Kagyü" - ] - }, - "zh": { - "name": "香巴噶举", - "aliases": [ - "香巴" - ] - }, - "bo": { - "name": "ཤངས་པ་བཀའ་བརྒྱུད།", - "aliases": [ - "ཤངས་པ།" - ] - }, - "hi": { - "name": "शांगपा काग्यू", - "aliases": [] - }, - "ne": { - "name": "शांग्पा काग्यु", - "aliases": [] - }, - "mn": { - "name": "Шангпа Кагью", - "aliases": [] - } - }, - "regions": [ - "Tibet", - "International" - ], - "level": 4, - "parent": "kagyu" - }, - { - "id": "taklung-kagyu", - "names": { - "en": { - "name": "Taklung Kagyu", - "aliases": [ - "Taklungpa" - ] - }, - "zh": { - "name": "达隆噶举", - "aliases": [ - "达隆" - ] - }, - "bo": { - "name": "སྟག་ལུང་བཀའ་བརྒྱུད།", - "aliases": [ - "སྟག་ལུང་།" - ] - }, - "hi": { - "name": "तकलुंग काग्यू", - "aliases": [] - }, - "ne": { - "name": "ताकलुङ काग्यु", - "aliases": [] - }, - "mn": { - "name": "Таклунг Кагью", - "aliases": [] - } - }, - "regions": [ - "Tibet" - ], - "level": 4, - "parent": "kagyu" - }, - { - "id": "sakya", - "names": { - "en": { - "name": "Sakya", - "aliases": [ - "Sakyapa", - "Sakya School", - "Grey Earth School" - ] - }, - "zh": { - "name": "萨迦派", - "aliases": [ - "萨迦", - "花教" - ] - }, - "bo": { - "name": "ས་སྐྱ་པ།", - "aliases": [ - "ས་སྐྱ།" - ] - }, - "hi": { - "name": "साक्य", - "aliases": [ - "साक्यपा" - ] - }, - "ne": { - "name": "साक्य", - "aliases": [ - "साक्यपा" - ] - }, - "mn": { - "name": "Сакья", - "aliases": [ - "Сакьяпа" - ] - } - }, - "regions": [ - "Tibet", - "Nepal", - "International" - ], - "level": 3, - "parent": "tibetan-buddhism" - }, - { - "id": "ngorpa", - "names": { - "en": { - "name": "Ngorpa", - "aliases": [ - "Ngor", - "Ngor Monastery" - ] - }, - "zh": { - "name": "俄尔派", - "aliases": [ - "俄尔巴" - ] - }, - "bo": { - "name": "ངོར་པ།", - "aliases": [ - "ངོར་ཆོས་སྡེ།" - ] - }, - "hi": { - "name": "न्गोरपा", - "aliases": [ - "न्गोर" - ] - }, - "ne": { - "name": "न्गोरपा", - "aliases": [] - }, - "mn": { - "name": "Нгорпа", - "aliases": [] - } - }, - "regions": [ - "Tibet" - ], - "level": 4, - "parent": "sakya" - }, - { - "id": "tsarpa", - "names": { - "en": { - "name": "Tsarpa", - "aliases": [ - "Tsar", - "Tsarchen lineage" - ] - }, - "zh": { - "name": "察尔派", - "aliases": [] - }, - "bo": { - "name": "ཚར་པ།", - "aliases": [ - "ཚར་ཆེན་བརྒྱུད་འཛིན།" - ] - }, - "hi": { - "name": "त्सारपा", - "aliases": [] - }, - "ne": { - "name": "त्सारपा", - "aliases": [] - }, - "mn": { - "name": "Царпа", - "aliases": [] - } - }, - "regions": [ - "Tibet" - ], - "level": 4, - "parent": "sakya" - }, - { - "id": "dzongsar", - "names": { - "en": { - "name": "Dzongsar", - "aliases": [ - "Khyentse lineage", - "Dzongsar Khyentse" - ] - }, - "zh": { - "name": "宗萨", - "aliases": [ - "宗萨钦哲传承" - ] - }, - "bo": { - "name": "རྫོང་གསར།", - "aliases": [ - "མཁྱེན་བརྩེའི་བརྒྱུད་འཛིན།" - ] - }, - "hi": { - "name": "दोंगसार", - "aliases": [ - "ख्येंत्से परंपरा" - ] - }, - "ne": { - "name": "दोन्गसार", - "aliases": [ - "ख्येन्त्से परम्परा" - ] - }, - "mn": { - "name": "Дзонгсар", - "aliases": [ - "Кхьенце" - ] - } - }, - "regions": [ - "Tibet", - "International" - ], - "level": 4, - "parent": "sakya" - }, - { - "id": "gelug", - "names": { - "en": { - "name": "Gelug", - "aliases": [ - "Gelugpa", - "Yellow Hat", - "Ganden Tradition", - "Dalai Lama lineage" - ] - }, - "zh": { - "name": "格鲁派", - "aliases": [ - "格鲁", - "黄教", - "达赖喇嘛传承" - ] - }, - "bo": { - "name": "དགེ་ལུགས་པ།", - "aliases": [ - "དགེ་ལུགས།", - "དགའ་ལྡན་ལུགས།" - ] - }, - "hi": { - "name": "गेलुग", - "aliases": [ - "गेलुगपा", - "पीली टोपी", - "दलाई लामा परंपरा" - ] - }, - "ne": { - "name": "गेलुग", - "aliases": [ - "गेलुगपा", - "दलाई लामा परम्परा" - ] - }, - "mn": { - "name": "Гэлүг", - "aliases": [ - "Гэлугпа", - "Шар малгайтан", - "Далай Ламын уламжлал" - ] - } - }, - "regions": [ - "Tibet", - "Mongolia", - "India", - "International" - ], - "level": 3, - "parent": "tibetan-buddhism" - }, - { - "id": "jonang", - "names": { - "en": { - "name": "Jonang", - "aliases": [ - "Jonangpa", - "Jonang School", - "Kalachakra lineage" - ] - }, - "zh": { - "name": "觉囊派", - "aliases": [ - "觉囊", - "时轮金刚传承" - ] - }, - "bo": { - "name": "ཇོ་ནང་པ།", - "aliases": [ - "ཇོ་ནང།", - "དུས་འཁོར་བརྒྱུད་འཛིན།" - ] - }, - "hi": { - "name": "जोनांग", - "aliases": [ - "जोनांगपा", - "कालचक्र परंपरा" - ] - }, - "ne": { - "name": "जोनाङ", - "aliases": [ - "जोनाङपा" - ] - }, - "mn": { - "name": "Жонанг", - "aliases": [ - "Жонангпа", - "Калачакра" - ] - } - }, - "regions": [ - "Tibet", - "International" - ], - "level": 3, - "parent": "tibetan-buddhism" - }, - { - "id": "rime", - "names": { - "en": { - "name": "Rimé", - "aliases": [ - "Rime", - "Non-sectarian", - "Eclectic movement" - ] - }, - "zh": { - "name": "利美运动", - "aliases": [ - "不分派", - "无宗派主义", - "利美" - ] - }, - "bo": { - "name": "རིས་མེད།", - "aliases": [ - "རིས་མེད་ཆོས་སྡེ།" - ] - }, - "hi": { - "name": "रिमे", - "aliases": [ - "गैर-सांप्रदायिक", - "रिमे आंदोलन" - ] - }, - "ne": { - "name": "रिमे", - "aliases": [ - "गैर-सम्प्रदायवादी" - ] - }, - "mn": { - "name": "Риме", - "aliases": [ - "Тэнцвэрт Буддизм", - "Бүлгийн бус" - ] - } - }, - "regions": [ - "Tibet", - "International" - ], - "level": 3, - "parent": "tibetan-buddhism" - }, - { - "id": "mongolian-buddhism", - "names": { - "en": { - "name": "Mongolian Buddhism", - "aliases": [ - "Mongolian Vajrayana", - "Mongolian Lamaism" - ] - }, - "zh": { - "name": "蒙古佛教", - "aliases": [ - "蒙古喇嘛教" - ] - }, - "bo": { - "name": "སོག་ཀྱི་ནང་ཆོས།", - "aliases": [ - "Mongolian Buddhism" - ] - }, - "hi": { - "name": "मंगोलियाई बौद्ध धर्म", - "aliases": [] - }, - "ne": { - "name": "मंगोलियाली बौद्ध धर्म", - "aliases": [] - }, - "mn": { - "name": "Монгол Буддизм", - "aliases": [ - "Монголын Буддын шашин", - "Монгол лам ын шашин" - ] - } - }, - "regions": [ - "Mongolia" - ], - "level": 2, - "parent": "vajrayana" - }, - { - "id": "buryat-buddhism", - "names": { - "en": { - "name": "Buryat Buddhism", - "aliases": [ - "Buryatia Buddhism" - ] - }, - "zh": { - "name": "布里亚特佛教", - "aliases": [] - }, - "bo": { - "name": "Buryat Buddhism", - "aliases": [] - }, - "hi": { - "name": "बुर्यात बौद्ध धर्म", - "aliases": [] - }, - "ne": { - "name": "बुर्यात बौद्ध धर्म", - "aliases": [] - }, - "mn": { - "name": "Буриадын Буддизм", - "aliases": [ - "Буриад Буддизм" - ] - } - }, - "regions": [ - "Russia" - ], - "level": 2, - "parent": "vajrayana" - }, - { - "id": "kalmyk-buddhism", - "names": { - "en": { - "name": "Kalmyk Buddhism", - "aliases": [ - "Kalmykia Buddhism" - ] - }, - "zh": { - "name": "卡尔梅克佛教", - "aliases": [] - }, - "bo": { - "name": "Kalmyk Buddhism", - "aliases": [] - }, - "hi": { - "name": "कालमिक बौद्ध धर्म", - "aliases": [] - }, - "ne": { - "name": "कालमिक बौद्ध धर्म", - "aliases": [] - }, - "mn": { - "name": "Халимгийн Буддизм", - "aliases": [ - "Халимаг Буддизм" - ] - } - }, - "regions": [ - "Russia" - ], - "level": 2, - "parent": "vajrayana" - }, - { - "id": "tuvan-buddhism", - "names": { - "en": { - "name": "Tuvan Buddhism", - "aliases": [ - "Tuvinian Buddhism", - "Tuva Buddhism" - ] - }, - "zh": { - "name": "图瓦佛教", - "aliases": [] - }, - "bo": { - "name": "Tuvan Buddhism", - "aliases": [] - }, - "hi": { - "name": "तुवान बौद्ध धर्म", - "aliases": [] - }, - "ne": { - "name": "तुवान बौद्ध धर्म", - "aliases": [] - }, - "mn": { - "name": "Тувагийн Буддизм", - "aliases": [ - "Тувa Буддизм" - ] - } - }, - "regions": [ - "Russia" - ], - "level": 2, - "parent": "vajrayana" - }, - { - "id": "newar-buddhism", - "names": { - "en": { - "name": "Newar Buddhism", - "aliases": [ - "Newari Buddhism", - "Nepal Buddhism" - ] - }, - "zh": { - "name": "尼瓦尔佛教", - "aliases": [ - "尼泊尔佛教" - ] - }, - "bo": { - "name": "Newar Buddhism", - "aliases": [] - }, - "hi": { - "name": "नेवार बौद्ध धर्म", - "aliases": [ - "नेवारी बौद्ध धर्म", - "नेपाल बौद्ध धर्म" - ] - }, - "ne": { - "name": "नेवार बौद्ध धर्म", - "aliases": [ - "नेवारी बौद्ध धर्म", - "नेपाली बौद्ध धर्म" - ] - }, - "mn": { - "name": "Неварын Буддизм", - "aliases": [] - } - }, - "regions": [ - "Nepal" - ], - "level": 2, - "parent": "vajrayana" - }, - { - "id": "bon", - "names": { - "en": { - "name": "Bön", - "aliases": [ - "Bon", - "Bonpo", - "Tibetan Bon", - "Yungdrung Bon" - ] - }, - "zh": { - "name": "苯教", - "aliases": [ - "苯", - "本教", - "西藏苯教", - "雍仲苯教" - ] - }, - "bo": { - "name": "བོན།", - "aliases": [ - "གཡུང་དྲུང་བོན།", - "བོན་པོ།" - ] - }, - "hi": { - "name": "बोन", - "aliases": [ - "बोनपो", - "तिब्बती बोन", - "युंगद्रुंग बोन" - ] - }, - "ne": { - "name": "बोन", - "aliases": [ - "बोनपो", - "तिब्बती बोन" - ] - }, - "mn": { - "name": "Бөн", - "aliases": [ - "Бөнпо", - "Төвдийн Бөн" - ] - } - }, - "regions": [ - "Tibet", - "Nepal", - "International" - ], - "level": 1, - "parent": null - }, - { - "id": "western-contemporary", - "names": { - "en": { - "name": "Western / Contemporary Buddhism", - "aliases": [ - "Western Buddhism", - "Modern Buddhism", - "Convert Buddhism" - ] - }, - "zh": { - "name": "西方佛教", - "aliases": [ - "现代佛教", - "当代佛教" - ] - }, - "bo": { - "name": "ནུབ་ཕྱོགས་ཀྱི་ནང་ཆོས།", - "aliases": [ - "Western Buddhism" - ] - }, - "hi": { - "name": "पश्चिमी बौद्ध धर्म", - "aliases": [ - "आधुनिक बौद्ध धर्म", - "समकालीन बौद्ध धर्म" - ] - }, - "ne": { - "name": "पश्चिमी बौद्ध धर्म", - "aliases": [ - "आधुनिक बौद्ध धर्म" - ] - }, - "mn": { - "name": "Баруун Буддизм", - "aliases": [ - "Орчин үеийн Буддизм" - ] - } - }, - "regions": [ - "International" - ], - "level": 1, - "parent": null - }, - { - "id": "secular-buddhism", - "names": { - "en": { - "name": "Secular Buddhism", - "aliases": [ - "Secular", - "Pragmatic Buddhism", - "Naturalistic Buddhism" - ] - }, - "zh": { - "name": "世俗佛教", - "aliases": [ - "非宗教性佛教", - "自然主义佛教" - ] - }, - "bo": { - "name": "Secular Buddhism", - "aliases": [] - }, - "hi": { - "name": "धर्मनिरपेक्ष बौद्ध धर्म", - "aliases": [ - "व्यावहारिक बौद्ध धर्म", - "नैसर्गिक बौद्ध धर्म" - ] - }, - "ne": { - "name": "धर्मनिरपेक्ष बौद्ध धर्म", - "aliases": [ - "व्यावहारिक बौद्ध धर्म" - ] - }, - "mn": { - "name": "Шашингүй Буддизм", - "aliases": [ - "Практик Буддизм" - ] - } - }, - "regions": [ - "International" - ], - "level": 2, - "parent": "western-contemporary" - }, - { - "id": "triratna", - "names": { - "en": { - "name": "Triratna Buddhist Community", - "aliases": [ - "Triratna", - "FWBO", - "Friends of the Western Buddhist Order", - "Western Buddhist Order" - ] - }, - "zh": { - "name": "三宝佛教社区", - "aliases": [ - "西方佛教友团", - "FWBO" - ] - }, - "bo": { - "name": "Triratna", - "aliases": [ - "FWBO" - ] - }, - "hi": { - "name": "त्रिरत्न बौद्ध समुदाय", - "aliases": [ - "FWBO", - "पश्चिमी बौद्ध आदेश के मित्र", - "त्रिरत्न" - ] - }, - "ne": { - "name": "त्रिरत्न बौद्ध समुदाय", - "aliases": [ - "FWBO", - "त्रिरत्न" - ] - }, - "mn": { - "name": "Тригратна", - "aliases": [ - "FWBO", - "Гурван Эрдэнийн Буддын Нийгэмлэг" - ] - } - }, - "regions": [ - "International" - ], - "level": 2, - "parent": "western-contemporary" - }, - { - "id": "nkt", - "names": { - "en": { - "name": "New Kadampa Tradition", - "aliases": [ - "NKT", - "Kadampa Buddhism", - "Modern Kadampa" - ] - }, - "zh": { - "name": "新噶当派", - "aliases": [ - "NKT", - "噶当派" - ] - }, - "bo": { - "name": "གདམས་ངག་གསར་པ།", - "aliases": [ - "NKT" - ] - }, - "hi": { - "name": "न्यू कादम्पा ट्रेडिशन", - "aliases": [ - "एनकेटी", - "कदम्पा बौद्ध धर्म" - ] - }, - "ne": { - "name": "न्यू काडम्पा ट्रेडिसन", - "aliases": [ - "एनकेटी" - ] - }, - "mn": { - "name": "Шинэ Кадампа Уламжлал", - "aliases": [ - "НКТ" - ] - } - }, - "regions": [ - "International" - ], - "level": 2, - "parent": "western-contemporary" - }, - { - "id": "shambhala", - "names": { - "en": { - "name": "Shambhala Buddhism", - "aliases": [ - "Shambhala", - "Shambhala International", - "Chogyam Trungpa lineage" - ] - }, - "zh": { - "name": "香巴拉佛教", - "aliases": [ - "香巴拉国际", - "创巴仁波切传承" - ] - }, - "bo": { - "name": "ཤམ་བྷ་ལའི་ནང་ཆོས།", - "aliases": [ - "Shambhala" - ] - }, - "hi": { - "name": "शंभाला बौद्ध धर्म", - "aliases": [ - "शंभाला इंटरनेशनल", - "चोग्यम त्रुंगपा परंपरा" - ] - }, - "ne": { - "name": "शम्भाला बौद्ध धर्म", - "aliases": [ - "शम्भाला" - ] - }, - "mn": { - "name": "Шамбала Буддизм", - "aliases": [ - "Шамбала Интернэшнл" - ] - } - }, - "regions": [ - "International" - ], - "level": 2, - "parent": "western-contemporary" - } - ] -} \ No newline at end of file diff --git a/pecha_api/traditions/tradition_constants.py b/pecha_api/traditions/tradition_constants.py index 8f02f3d0c..45367a719 100644 --- a/pecha_api/traditions/tradition_constants.py +++ b/pecha_api/traditions/tradition_constants.py @@ -1,8 +1,11 @@ from pathlib import Path -from uuid import UUID +from uuid import UUID, uuid5 -TRADITION_TAXONOMY_PATH = Path(__file__).resolve().parent / "Buddhist Traditions Taxonomy - i18n-v1.1.json" TRADITION_ONBOARDING_PATH = Path(__file__).resolve().parent / "tradition.json" TRADITION_ID_NAMESPACE = UUID("f47ac10b-58cc-4372-a567-0e02b2c3d479") DEFAULT_LLM_MODEL = "gemini-2.5-flash-lite" DEFAULT_CHAT_LANGUAGE = "en" + + +def tradition_id_from_code(code: str) -> UUID: + return uuid5(TRADITION_ID_NAMESPACE, code) diff --git a/pecha_api/traditions/tradition_prompt.py b/pecha_api/traditions/tradition_prompt.py index f008ed84a..d2313359d 100644 --- a/pecha_api/traditions/tradition_prompt.py +++ b/pecha_api/traditions/tradition_prompt.py @@ -1,4 +1,14 @@ -from pecha_api.traditions.tradition_taxonomy import build_tradition_catalog +from pecha_api.traditions.tradition_onboarding import get_tradition_path_entry, list_tradition_path_codes + + +def build_tradition_catalog(language: str = "en") -> str: + lines: list[str] = [] + for code in sorted(list_tradition_path_codes()): + path_entry = get_tradition_path_entry(code, language=language) + if path_entry is None: + continue + lines.append(f'{code}|{path_entry["title"]}|0|') + return "\n".join(lines) def build_tradition_chat_system_prompt(language: str = "en") -> str: diff --git a/pecha_api/traditions/tradition_repository.py b/pecha_api/traditions/tradition_repository.py index 2a9dbad44..87c5d182c 100644 --- a/pecha_api/traditions/tradition_repository.py +++ b/pecha_api/traditions/tradition_repository.py @@ -1,4 +1,4 @@ -from typing import List, Optional +from typing import List from uuid import UUID from fastapi import HTTPException @@ -9,69 +9,12 @@ from pecha_api.plans.plans_enums import LanguageCode from pecha_api.plans.auth.plan_auth_models import ResponseError from pecha_api.plans.response_message import NOT_FOUND +from pecha_api.traditions.tradition_constants import tradition_id_from_code from pecha_api.traditions.tradition_models import Tradition, TraditionMetadata, UserTradition from pecha_api.traditions.tradition_onboarding import ( get_tradition_path_entry, list_tradition_path_codes, ) -from pecha_api.traditions.tradition_taxonomy import ( - load_tradition_taxonomy, - tradition_id_from_code, -) - - -def get_tradition_by_code(db: Session, code: str) -> Optional[Tradition]: - tradition_id = tradition_id_from_code(code) - return db.query(Tradition).filter(Tradition.id == tradition_id).first() - - -def sync_traditions_from_taxonomy(db: Session) -> None: - taxonomy = load_tradition_taxonomy() - code_to_uuid = { - entry["id"]: tradition_id_from_code(entry["id"]) - for entry in taxonomy["traditions"] - } - - for entry in taxonomy["traditions"]: - tradition_id = code_to_uuid[entry["id"]] - parent_code = entry.get("parent") - parent_id = code_to_uuid.get(parent_code) if parent_code else None - - tradition = db.query(Tradition).filter(Tradition.id == tradition_id).first() - if tradition is None: - tradition = Tradition( - id=tradition_id, - parent_id=parent_id, - regions=entry.get("regions"), - ) - db.add(tradition) - else: - tradition.parent_id = parent_id - tradition.regions = entry.get("regions") - - for language_code, localized_names in entry.get("names", {}).items(): - language = LanguageCode[language_code.upper()] - metadata = ( - db.query(TraditionMetadata) - .filter( - TraditionMetadata.tradition_id == tradition_id, - TraditionMetadata.language == language, - ) - .first() - ) - if metadata is None: - metadata = TraditionMetadata( - tradition_id=tradition_id, - language=language, - name=localized_names.get("name", entry["id"]), - other_names=localized_names.get("aliases"), - ) - db.add(metadata) - else: - metadata.name = localized_names.get("name", entry["id"]) - metadata.other_names = localized_names.get("aliases") - - db.commit() def get_user_traditions(db: Session, user_id: UUID) -> List[UserTradition]: @@ -84,14 +27,6 @@ def get_user_traditions(db: Session, user_id: UUID) -> List[UserTradition]: ) -def ensure_tradition_exists(db: Session, tradition_code: str) -> UUID: - tradition_id = tradition_id_from_code(tradition_code) - existing = db.query(Tradition).filter(Tradition.id == tradition_id).first() - if existing is None: - sync_traditions_from_taxonomy(db) - return tradition_id - - def ensure_path_tradition_exists(db: Session, tradition_code: str) -> UUID: tradition_id = tradition_id_from_code(tradition_code) existing = db.query(Tradition).filter(Tradition.id == tradition_id).first() diff --git a/pecha_api/traditions/tradition_service.py b/pecha_api/traditions/tradition_service.py index cc7387204..665ce22f0 100644 --- a/pecha_api/traditions/tradition_service.py +++ b/pecha_api/traditions/tradition_service.py @@ -9,7 +9,7 @@ from pecha_api.db.database import SessionLocal from pecha_api.plans.plans_enums import LanguageCode from pecha_api.traditions.llm_client import chat_with_worker -from pecha_api.traditions.tradition_constants import DEFAULT_CHAT_LANGUAGE +from pecha_api.traditions.tradition_constants import DEFAULT_CHAT_LANGUAGE, tradition_id_from_code from pecha_api.traditions.tradition_onboarding import ( get_tradition_onboarding_content, get_tradition_path_entry, @@ -37,13 +37,6 @@ UserTraditionDTO, UserTraditionsResponse, ) -from pecha_api.traditions.tradition_taxonomy import ( - get_tradition_display_name, - get_tradition_entry, - list_tradition_codes, - load_tradition_taxonomy, - tradition_id_from_code, -) from pecha_api.users.users_service import validate_and_extract_user_details @@ -59,7 +52,7 @@ def _normalize_suggested_traditions( suggested_traditions: list, language: str, ) -> List[SuggestedTradition]: - allowed_codes = list_tradition_codes() + allowed_codes = list_tradition_path_codes() normalized: list[SuggestedTradition] = [] seen_codes: set[str] = set() @@ -71,8 +64,8 @@ def _normalize_suggested_traditions( if not code or code not in allowed_codes or code in seen_codes: continue - entry = get_tradition_entry(code) - name = item.get("name") or (get_tradition_display_name(entry, language) if entry else code) + path_entry = get_tradition_path_entry(code, language=language) + name = item.get("name") or (path_entry["title"] if path_entry else code) normalized.append(SuggestedTradition(code=code, name=name)) seen_codes.add(code) @@ -95,7 +88,7 @@ def _normalize_selected_tradition_code(selected_code: object) -> str | None: return None normalized = str(selected_code).strip() - if normalized not in list_tradition_codes(): + if normalized not in list_tradition_path_codes(): return None return normalized @@ -231,14 +224,17 @@ async def get_user_traditions_service(token: str) -> UserTraditionsResponse: async def list_traditions_service(language: str = DEFAULT_CHAT_LANGUAGE) -> TraditionListResponse: traditions: list[TraditionListItemDTO] = [] - for entry in load_tradition_taxonomy()["traditions"]: + for code in sorted(list_tradition_path_codes()): + path_entry = get_tradition_path_entry(code, language=language) + if path_entry is None: + continue traditions.append( TraditionListItemDTO( - code=entry["id"], - name=get_tradition_display_name(entry, language), - level=entry["level"], - parent_code=entry.get("parent"), - regions=entry.get("regions") or [], + code=code, + name=path_entry["title"], + level=0, + parent_code=None, + regions=[], ) ) return TraditionListResponse(traditions=traditions) @@ -276,9 +272,6 @@ def _resolve_tradition_code(user_tradition) -> str: for code in list_tradition_path_codes(): if tradition_id_from_code(code) == tradition_id: return code - for entry in load_tradition_taxonomy()["traditions"]: - if tradition_id_from_code(entry["id"]) == tradition_id: - return entry["id"] if user_tradition.tradition and user_tradition.tradition.metadata_entries: for metadata in user_tradition.tradition.metadata_entries: @@ -291,24 +284,13 @@ def _resolve_tradition_code(user_tradition) -> str: def _build_user_tradition_dto(user_tradition, tradition_code: str, language: str) -> UserTraditionDTO: path_entry = get_tradition_path_entry(tradition_code, language=language) - if path_entry is not None: - return UserTraditionDTO( - id=user_tradition.id, - tradition_code=tradition_code, - tradition_name=path_entry["title"], - level=0, - parent_code=None, - created_at=user_tradition.created_at, - updated_at=user_tradition.updated_at, - ) - - entry = get_tradition_entry(tradition_code) + tradition_name = path_entry["title"] if path_entry else tradition_code return UserTraditionDTO( id=user_tradition.id, tradition_code=tradition_code, - tradition_name=get_tradition_display_name(entry, language) if entry else tradition_code, - level=entry["level"] if entry else 0, - parent_code=entry.get("parent") if entry else None, + tradition_name=tradition_name, + level=0, + parent_code=None, created_at=user_tradition.created_at, updated_at=user_tradition.updated_at, ) diff --git a/pecha_api/traditions/tradition_taxonomy.py b/pecha_api/traditions/tradition_taxonomy.py deleted file mode 100644 index a18ff6005..000000000 --- a/pecha_api/traditions/tradition_taxonomy.py +++ /dev/null @@ -1,46 +0,0 @@ -import json -from functools import lru_cache -from typing import Any -from uuid import UUID, uuid5 - -from pecha_api.traditions.tradition_constants import ( - DEFAULT_CHAT_LANGUAGE, - TRADITION_ID_NAMESPACE, - TRADITION_TAXONOMY_PATH, -) - - -def tradition_id_from_code(code: str) -> UUID: - return uuid5(TRADITION_ID_NAMESPACE, code) - - -@lru_cache(maxsize=1) -def load_tradition_taxonomy() -> dict[str, Any]: - with TRADITION_TAXONOMY_PATH.open(encoding="utf-8") as taxonomy_file: - return json.load(taxonomy_file) - - -def get_tradition_entry(code: str) -> dict[str, Any] | None: - for entry in load_tradition_taxonomy()["traditions"]: - if entry["id"] == code: - return entry - return None - - -def get_tradition_display_name(entry: dict[str, Any], language: str = DEFAULT_CHAT_LANGUAGE) -> str: - names = entry.get("names", {}) - language_entry = names.get(language) or names.get(DEFAULT_CHAT_LANGUAGE) or next(iter(names.values()), {}) - return language_entry.get("name", entry["id"]) - - -def build_tradition_catalog(language: str = DEFAULT_CHAT_LANGUAGE) -> str: - lines: list[str] = [] - for entry in load_tradition_taxonomy()["traditions"]: - parent = entry.get("parent") or "" - name = get_tradition_display_name(entry, language) - lines.append(f'{entry["id"]}|{name}|{entry["level"]}|{parent}') - return "\n".join(lines) - - -def list_tradition_codes() -> set[str]: - return {entry["id"] for entry in load_tradition_taxonomy()["traditions"]} diff --git a/tests/accumulator/test_accumulator_repository.py b/tests/accumulator/test_accumulator_repository.py new file mode 100644 index 000000000..d4e037822 --- /dev/null +++ b/tests/accumulator/test_accumulator_repository.py @@ -0,0 +1,72 @@ +from unittest.mock import MagicMock, patch +from uuid import uuid4 + +from pecha_api.accumulator.accumulator_repository import ( + get_accumulator_with_history, + get_user_total_counted_by_parent, +) + + +def test_get_user_total_counted_by_parent_sums_history_across_preset_instances(): + db = MagicMock() + user_id = uuid4() + parent_id = uuid4() + + query = MagicMock() + db.query.return_value = query + query.join.return_value = query + query.filter.return_value = query + query.scalar.return_value = 150 + + result = get_user_total_counted_by_parent(db, user_id, parent_id) + + assert result == 150 + query.join.assert_called_once() + query.filter.assert_called_once() + query.scalar.assert_called_once() + + +def test_get_user_total_counted_by_parent_returns_zero_when_no_history(): + db = MagicMock() + query = MagicMock() + db.query.return_value = query + query.join.return_value = query + query.filter.return_value = query + query.scalar.return_value = None + + result = get_user_total_counted_by_parent(db, uuid4(), uuid4()) + + assert result == 0 + + +@patch("pecha_api.accumulator.accumulator_repository.get_user_total_counted_by_parent") +@patch("pecha_api.accumulator.accumulator_repository.get_user_accumulator_by_parent") +def test_get_accumulator_with_history_uses_parent_lifetime_total_not_current_only( + mock_get_by_parent, + mock_total_by_parent, +): + db = MagicMock() + user_id = uuid4() + parent_id = uuid4() + accumulator_id = uuid4() + + accumulator = MagicMock() + accumulator.id = accumulator_id + mock_get_by_parent.return_value = accumulator + mock_total_by_parent.return_value = 300 + + history_query = MagicMock() + db.query.return_value = history_query + history_query.filter.return_value = history_query + history_query.order_by.return_value = history_query + history_query.all.return_value = [] + + result = get_accumulator_with_history(db, user_id, parent_id) + + assert result is not None + returned_accumulator, total_counted, sessions = result + assert returned_accumulator is accumulator + assert total_counted == 300 + assert sessions == [] + mock_get_by_parent.assert_called_once_with(db, user_id, parent_id) + mock_total_by_parent.assert_called_once_with(db, user_id, parent_id) diff --git a/tests/accumulator/test_accumulator_views.py b/tests/accumulator/test_accumulator_views.py index 678885ad3..daca40d49 100644 --- a/tests/accumulator/test_accumulator_views.py +++ b/tests/accumulator/test_accumulator_views.py @@ -109,7 +109,7 @@ async def test_get_all_accumulators_success(self, mock_service): assert isinstance(result, PublicAccumulatorsResponse) assert len(result.accumulators) == 2 assert result.total == 2 - mock_service.assert_called_once_with(skip=0, limit=20, language=None, search=None) + mock_service.assert_called_once_with(skip=0, limit=20, language=None, search=None, timezone_name=None) @patch('pecha_api.accumulator.accumulator_views.get_all_accumulators_service') @pytest.mark.asyncio @@ -140,7 +140,7 @@ async def test_get_all_accumulators_pagination(self, mock_service): assert result.skip == 5 assert result.limit == 1 assert result.total == 10 - mock_service.assert_called_once_with(skip=5, limit=1, language=None, search=None) + mock_service.assert_called_once_with(skip=5, limit=1, language=None, search=None, timezone_name=None) @patch('pecha_api.accumulator.accumulator_views.get_all_accumulators_service') @pytest.mark.asyncio @@ -152,7 +152,7 @@ async def test_get_all_accumulators_with_language(self, mock_service): await get_all_preset_accumulators(skip=0, limit=20, language="bo") - mock_service.assert_called_once_with(skip=0, limit=20, language="bo", search=None) + mock_service.assert_called_once_with(skip=0, limit=20, language="bo", search=None, timezone_name=None) class TestGetUserAccumulators: diff --git a/tests/bookmarks/test_bookmark_utils.py b/tests/bookmarks/test_bookmark_utils.py index a384b7808..a254c41c9 100644 --- a/tests/bookmarks/test_bookmark_utils.py +++ b/tests/bookmarks/test_bookmark_utils.py @@ -239,9 +239,13 @@ async def test_enrich_text_bookmark_returns_empty_when_segment_missing(): bookmark.name = None with patch( - "pecha_api.bookmarks.bookmark_utils._resolve_text_segment", + "pecha_api.bookmarks.bookmark_utils.get_texts_by_id", new_callable=AsyncMock, - return_value=(None, None), + return_value=MagicMock(title="Unused"), + ), patch( + "pecha_api.bookmarks.bookmark_utils.build_first_segment_preview_for_text", + new_callable=AsyncMock, + return_value=None, ): result = await enrich_text_bookmark(bookmark) @@ -322,18 +326,14 @@ async def test_enrich_text_bookmark_handles_missing_text_details(): bookmark.source_id = text_id bookmark.name = None - mock_segment = MagicMock() - mock_segment.id = segment_id - mock_segment.content = "Segment content" - with patch( - "pecha_api.bookmarks.bookmark_utils._resolve_text_segment", - new_callable=AsyncMock, - return_value=(segment_id, mock_segment), - ), patch( "pecha_api.bookmarks.bookmark_utils.get_texts_by_id", new_callable=AsyncMock, return_value=None, + ), patch( + "pecha_api.bookmarks.bookmark_utils.build_first_segment_preview_for_text", + new_callable=AsyncMock, + return_value=(segment_id, "Segment content"), ): result = await enrich_text_bookmark(bookmark) @@ -353,27 +353,18 @@ async def test_enrich_text_bookmark_with_language_uses_localized_text(): bookmark.source_id = text_id bookmark.name = None - mock_segment = MagicMock() - mock_segment.id = segment_id - mock_segment.text_id = text_id - mock_segment.content = "English content" - localized_text = MagicMock() localized_text.id = localized_text_id localized_text.title = "བོད་ཡིག་ཁ་བྱང་" with patch( - "pecha_api.bookmarks.bookmark_utils._resolve_text_segment", - new_callable=AsyncMock, - return_value=(segment_id, mock_segment), - ), patch( "pecha_api.bookmarks.bookmark_utils._resolve_localized_text", new_callable=AsyncMock, return_value=localized_text, ), patch( - "pecha_api.bookmarks.bookmark_utils._resolve_localized_segment", + "pecha_api.bookmarks.bookmark_utils.build_first_segment_preview_for_text", new_callable=AsyncMock, - return_value=mock_segment, + return_value=(segment_id, "English content"), ): result = await enrich_text_bookmark(bookmark, language="BO") @@ -808,19 +799,10 @@ async def test_enrich_text_bookmark_falls_back_when_localized_text_missing(): bookmark.source_id = text_id bookmark.name = None - mock_segment = MagicMock() - mock_segment.id = segment_id - mock_segment.text_id = text_id - mock_segment.content = "Fallback content" - mock_text = MagicMock() mock_text.title = "Original title" with patch( - "pecha_api.bookmarks.bookmark_utils._resolve_text_segment", - new_callable=AsyncMock, - return_value=(segment_id, mock_segment), - ), patch( "pecha_api.bookmarks.bookmark_utils._resolve_localized_text", new_callable=AsyncMock, return_value=None, @@ -828,6 +810,10 @@ async def test_enrich_text_bookmark_falls_back_when_localized_text_missing(): "pecha_api.bookmarks.bookmark_utils.get_texts_by_id", new_callable=AsyncMock, return_value=mock_text, + ), patch( + "pecha_api.bookmarks.bookmark_utils.build_first_segment_preview_for_text", + new_callable=AsyncMock, + return_value=(segment_id, "Fallback content"), ): result = await enrich_text_bookmark(bookmark, language="BO") diff --git a/tests/daily_log/test_daily_log_cache_service.py b/tests/daily_log/test_daily_log_cache_service.py index 8cbd8816f..d66b73049 100644 --- a/tests/daily_log/test_daily_log_cache_service.py +++ b/tests/daily_log/test_daily_log_cache_service.py @@ -5,7 +5,7 @@ import pytest from pecha_api.daily_log.daily_log_cache_service import ( - _seconds_until_end_of_utc_day, + _seconds_until_end_of_day_in_timezone, get_user_stats_cache, invalidate_user_stats_cache, is_user_logged_today_in_cache, @@ -15,16 +15,16 @@ from pecha_api.daily_log.daily_log_response_models import StreakStats, UserStatsResponse -def test_seconds_until_end_of_utc_day_returns_positive_value(): +def test_seconds_until_end_of_day_in_timezone_returns_positive_value(): fixed_now = datetime(2026, 6, 11, 12, 0, 0, tzinfo=timezone.utc) with patch("pecha_api.daily_log.daily_log_cache_service.datetime") as mock_datetime: mock_datetime.now.return_value = fixed_now mock_datetime.side_effect = lambda *args, **kwargs: datetime(*args, **kwargs) - seconds = _seconds_until_end_of_utc_day() + seconds = _seconds_until_end_of_day_in_timezone("UTC") - assert seconds == 12 * 60 * 60 + assert seconds == (12 * 60 * 60) - 1 @pytest.mark.asyncio @@ -64,7 +64,7 @@ async def test_set_user_daily_log_cache_stores_until_end_of_day(): log_date = date(2026, 6, 11) with patch( - "pecha_api.daily_log.daily_log_cache_service._seconds_until_end_of_utc_day", + "pecha_api.daily_log.daily_log_cache_service._seconds_until_end_of_day_in_timezone", return_value=3600, ), patch( "pecha_api.daily_log.daily_log_cache_service.set_cache", @@ -93,7 +93,10 @@ async def test_get_user_stats_cache_returns_parsed_response(): new_callable=AsyncMock, return_value=stats_payload, ): - result = await get_user_stats_cache(user_id=user_id) + result = await get_user_stats_cache( + user_id=user_id, + timezone_name="Asia/Kathmandu", + ) assert isinstance(result, UserStatsResponse) assert result.streak.current == 2 @@ -117,7 +120,11 @@ async def test_set_user_stats_cache_uses_five_minute_timeout(): "pecha_api.daily_log.daily_log_cache_service.set_cache", new_callable=AsyncMock, ) as mock_set_cache: - await set_user_stats_cache(user_id=user_id, data=stats) + await set_user_stats_cache( + user_id=user_id, + data=stats, + timezone_name="Asia/Kathmandu", + ) mock_set_cache.assert_awaited_once() _, kwargs = mock_set_cache.await_args @@ -133,6 +140,9 @@ async def test_invalidate_user_stats_cache_deletes_key(): "pecha_api.daily_log.daily_log_cache_service.delete_cache", new_callable=AsyncMock, ) as mock_delete_cache: - await invalidate_user_stats_cache(user_id=user_id) + await invalidate_user_stats_cache( + user_id=user_id, + timezone_name="Asia/Kathmandu", + ) mock_delete_cache.assert_awaited_once() diff --git a/tests/daily_log/test_daily_log_service.py b/tests/daily_log/test_daily_log_service.py index dd40b663e..431e7803b 100644 --- a/tests/daily_log/test_daily_log_service.py +++ b/tests/daily_log/test_daily_log_service.py @@ -7,30 +7,28 @@ from fastapi import HTTPException from pecha_api.daily_log.daily_log_service import ( - _utc_today, + _today_for_timezone, calculate_streak, get_user_streak_service, record_daily_log_if_needed, ) -def test_utc_today_returns_current_utc_date(): - assert isinstance(_utc_today(), date) +def test_today_for_timezone_defaults_to_utc_date(): + assert isinstance(_today_for_timezone(), date) def test_calculate_streak_returns_zero_when_no_logs_exist(): today = date(2026, 6, 11) - with patch("pecha_api.daily_log.daily_log_service._utc_today", return_value=today): - assert calculate_streak(log_dates=set()) == 0 + assert calculate_streak(log_dates=set(), today=today) == 0 def test_calculate_streak_returns_one_when_only_today_logged(): today = date(2026, 6, 11) log_dates = {today} - with patch("pecha_api.daily_log.daily_log_service._utc_today", return_value=today): - assert calculate_streak(log_dates=log_dates) == 1 + assert calculate_streak(log_dates=log_dates, today=today) == 1 def test_calculate_streak_counts_consecutive_days_from_today(): @@ -42,8 +40,7 @@ def test_calculate_streak_counts_consecutive_days_from_today(): today - timedelta(days=4), } - with patch("pecha_api.daily_log.daily_log_service._utc_today", return_value=today): - assert calculate_streak(log_dates=log_dates) == 3 + assert calculate_streak(log_dates=log_dates, today=today) == 3 def test_calculate_streak_uses_yesterday_when_today_missing(): @@ -51,8 +48,7 @@ def test_calculate_streak_uses_yesterday_when_today_missing(): yesterday = today - timedelta(days=1) log_dates = {yesterday, yesterday - timedelta(days=1)} - with patch("pecha_api.daily_log.daily_log_service._utc_today", return_value=today): - assert calculate_streak(log_dates=log_dates) == 2 + assert calculate_streak(log_dates=log_dates, today=today) == 2 @pytest.mark.asyncio @@ -60,10 +56,10 @@ async def test_record_daily_log_if_needed_skips_when_cache_hit(): user_id = uuid4() today = date(2026, 6, 11) - with patch("pecha_api.daily_log.daily_log_service._utc_today", return_value=today), \ + with patch("pecha_api.daily_log.daily_log_service._today_for_timezone", return_value=today), \ patch("pecha_api.daily_log.daily_log_service.is_user_logged_today_in_cache", return_value=True) as mock_cache, \ patch("pecha_api.daily_log.daily_log_service.SessionLocal") as mock_session: - await record_daily_log_if_needed(user_id=user_id) + await record_daily_log_if_needed(user_id=user_id, timezone_name="Asia/Kathmandu") mock_cache.assert_awaited_once_with(user_id=user_id, log_date=today) mock_session.assert_not_called() @@ -77,19 +73,26 @@ async def test_record_daily_log_if_needed_saves_when_not_cached_or_logged(): mock_db.__enter__ = MagicMock(return_value=mock_db) mock_db.__exit__ = MagicMock(return_value=False) - with patch("pecha_api.daily_log.daily_log_service._utc_today", return_value=today), \ + with patch("pecha_api.daily_log.daily_log_service._today_for_timezone", return_value=today), \ patch("pecha_api.daily_log.daily_log_service.is_user_logged_today_in_cache", return_value=False), \ patch("pecha_api.daily_log.daily_log_service.SessionLocal", return_value=mock_db), \ patch("pecha_api.daily_log.daily_log_service.has_log_for_date", return_value=False) as mock_has_log, \ patch("pecha_api.daily_log.daily_log_service.save_daily_log") as mock_save, \ patch("pecha_api.daily_log.daily_log_service.set_user_daily_log_cache") as mock_set_cache, \ patch("pecha_api.daily_log.daily_log_service.invalidate_user_stats_cache", new_callable=AsyncMock) as mock_invalidate: - await record_daily_log_if_needed(user_id=user_id) + await record_daily_log_if_needed(user_id=user_id, timezone_name="Asia/Kathmandu") mock_has_log.assert_called_once_with(db=mock_db, user_id=user_id, log_date=today) mock_save.assert_called_once_with(db=mock_db, user_id=user_id, log_date=today) - mock_set_cache.assert_awaited_once_with(user_id=user_id, log_date=today) - mock_invalidate.assert_awaited_once_with(user_id=user_id) + mock_set_cache.assert_awaited_once_with( + user_id=user_id, + log_date=today, + timezone_name="Asia/Kathmandu", + ) + mock_invalidate.assert_awaited_once_with( + user_id=user_id, + timezone_name="Asia/Kathmandu", + ) @pytest.mark.asyncio @@ -100,16 +103,20 @@ async def test_record_daily_log_if_needed_sets_cache_when_db_already_has_log(): mock_db.__enter__ = MagicMock(return_value=mock_db) mock_db.__exit__ = MagicMock(return_value=False) - with patch("pecha_api.daily_log.daily_log_service._utc_today", return_value=today), \ + with patch("pecha_api.daily_log.daily_log_service._today_for_timezone", return_value=today), \ patch("pecha_api.daily_log.daily_log_service.is_user_logged_today_in_cache", return_value=False), \ patch("pecha_api.daily_log.daily_log_service.SessionLocal", return_value=mock_db), \ patch("pecha_api.daily_log.daily_log_service.has_log_for_date", return_value=True), \ patch("pecha_api.daily_log.daily_log_service.save_daily_log") as mock_save, \ patch("pecha_api.daily_log.daily_log_service.set_user_daily_log_cache") as mock_set_cache: - await record_daily_log_if_needed(user_id=user_id) + await record_daily_log_if_needed(user_id=user_id, timezone_name="Asia/Kathmandu") mock_save.assert_not_called() - mock_set_cache.assert_awaited_once_with(user_id=user_id, log_date=today) + mock_set_cache.assert_awaited_once_with( + user_id=user_id, + log_date=today, + timezone_name="Asia/Kathmandu", + ) @pytest.mark.asyncio @@ -123,13 +130,25 @@ async def test_get_user_streak_service_returns_streak(): mock_db.__exit__ = MagicMock(return_value=False) with patch("pecha_api.daily_log.daily_log_service.validate_and_extract_user_details", return_value=mock_user), \ + patch("pecha_api.daily_log.daily_log_service._today_for_timezone", return_value=today), \ patch("pecha_api.daily_log.daily_log_service.record_daily_log_if_needed", new_callable=AsyncMock) as mock_record, \ patch("pecha_api.daily_log.daily_log_service.SessionLocal", return_value=mock_db), \ - patch("pecha_api.daily_log.daily_log_service.get_user_streak", return_value=2): - result = await get_user_streak_service(token="test_token") + patch("pecha_api.daily_log.daily_log_service.get_user_streak", return_value=2) as mock_get_streak: + result = await get_user_streak_service( + token="test_token", + timezone_name="Asia/Kathmandu", + ) assert result.streak == 2 - mock_record.assert_awaited_once_with(user_id=user_id) + mock_record.assert_awaited_once_with( + user_id=user_id, + timezone_name="Asia/Kathmandu", + ) + mock_get_streak.assert_called_once_with( + db=mock_db, + user_id=user_id, + today=today, + ) @pytest.mark.asyncio @@ -146,7 +165,7 @@ async def test_get_user_stats_service_aggregates_all_sources(): with patch("pecha_api.daily_log.daily_log_service.validate_and_extract_user_details", return_value=mock_user), \ patch("pecha_api.daily_log.daily_log_service.record_daily_log_if_needed", new_callable=AsyncMock) as mock_record, \ - patch("pecha_api.daily_log.daily_log_service._utc_today", return_value=today), \ + patch("pecha_api.daily_log.daily_log_service._today_for_timezone", return_value=today), \ patch("pecha_api.daily_log.daily_log_service.get_user_stats_cache", new_callable=AsyncMock, return_value=None), \ patch("pecha_api.daily_log.daily_log_service.set_user_stats_cache", new_callable=AsyncMock) as mock_set_stats_cache, \ patch("pecha_api.daily_log.daily_log_service.SessionLocal", return_value=mock_db), \ @@ -154,7 +173,10 @@ async def test_get_user_stats_service_aggregates_all_sources(): patch("pecha_api.daily_log.daily_log_service.get_highest_streak", return_value=7), \ patch("pecha_api.daily_log.daily_log_service.get_week_active_days", return_value=[2, 3, 6]), \ patch("pecha_api.daily_log.daily_log_service.get_user_activity_totals", return_value=(1200, 10800, 42)): - result = await get_user_stats_service(token="test_token") + result = await get_user_stats_service( + token="test_token", + timezone_name="Asia/Kathmandu", + ) assert result.streak.current == 3 assert result.streak.highest == 7 @@ -162,8 +184,16 @@ async def test_get_user_stats_service_aggregates_all_sources(): assert result.total_timer == 1200 assert result.total_accumulated == 10800 assert result.total_practice_days == 42 - mock_record.assert_awaited_once_with(user_id=user_id, db=mock_db) - mock_set_stats_cache.assert_awaited_once_with(user_id=user_id, data=result) + mock_record.assert_awaited_once_with( + user_id=user_id, + db=mock_db, + timezone_name="Asia/Kathmandu", + ) + mock_set_stats_cache.assert_awaited_once_with( + user_id=user_id, + data=result, + timezone_name="Asia/Kathmandu", + ) @pytest.mark.asyncio @@ -177,10 +207,14 @@ async def test_get_user_stats_service_returns_cached_stats_without_db_queries(): with patch("pecha_api.daily_log.daily_log_service.validate_and_extract_user_details", return_value=mock_user), \ patch("pecha_api.daily_log.daily_log_service.record_daily_log_if_needed", new_callable=AsyncMock), \ + patch("pecha_api.daily_log.daily_log_service._today_for_timezone", return_value=date(2026, 6, 11)), \ patch("pecha_api.daily_log.daily_log_service.get_user_stats_cache", new_callable=AsyncMock, return_value=cached_stats), \ patch("pecha_api.daily_log.daily_log_service.get_user_streak") as mock_streak, \ patch("pecha_api.daily_log.daily_log_service.set_user_stats_cache", new_callable=AsyncMock) as mock_set_stats_cache: - result = await get_user_stats_service(token="test_token") + result = await get_user_stats_service( + token="test_token", + timezone_name="Asia/Kathmandu", + ) assert result is cached_stats mock_streak.assert_not_called() diff --git a/tests/daily_log/test_daily_log_views.py b/tests/daily_log/test_daily_log_views.py index cb87c554c..f05839cea 100644 --- a/tests/daily_log/test_daily_log_views.py +++ b/tests/daily_log/test_daily_log_views.py @@ -15,10 +15,13 @@ async def test_get_user_streak_endpoint_success(): "pecha_api.daily_log.daily_log_views.get_user_streak_service", return_value=UserStreakResponse(streak=5), ) as mock_service: - result = await get_user_streak(authentication_credential=mock_credentials) + result = await get_user_streak(authentication_credential=mock_credentials, x_timezone="Asia/Kathmandu") assert result.streak == 5 - mock_service.assert_awaited_once_with(token="test_token") + mock_service.assert_awaited_once_with( + token="test_token", + timezone_name="Asia/Kathmandu", + ) @pytest.mark.asyncio @@ -52,8 +55,11 @@ async def test_get_user_stats_endpoint_success(): "pecha_api.daily_log.daily_log_views.get_user_stats_service", return_value=response, ) as mock_service: - result = await get_user_stats(authentication_credential=mock_credentials) + result = await get_user_stats(authentication_credential=mock_credentials, x_timezone="Asia/Kathmandu") assert result.streak.highest == 7 assert result.total_practice_days == 42 - mock_service.assert_awaited_once_with(token="test_token") + mock_service.assert_awaited_once_with( + token="test_token", + timezone_name="Asia/Kathmandu", + ) diff --git a/tests/group_accumulator/test_group_accumulator_views.py b/tests/group_accumulator/test_group_accumulator_views.py index d82e79bd7..84ec80ed9 100644 --- a/tests/group_accumulator/test_group_accumulator_views.py +++ b/tests/group_accumulator/test_group_accumulator_views.py @@ -123,7 +123,7 @@ def test_get_group_accumulators_success(self, mock_service): assert data["total"] == 2 assert data["skip"] == 0 assert data["limit"] == 20 - mock_service.assert_called_once_with(group_id=group_id, skip=0, limit=20, token=None) + mock_service.assert_called_once_with(group_id=group_id, skip=0, limit=20, token=None, timezone_name=None) @patch('pecha_api.group_accumulator.group_accumulator_views.get_group_accumulators_service') def test_get_group_accumulators_with_pagination(self, mock_service): @@ -140,7 +140,7 @@ def test_get_group_accumulators_with_pagination(self, mock_service): ) assert response.status_code == status.HTTP_200_OK - mock_service.assert_called_once_with(group_id=group_id, skip=5, limit=5, token=None) + mock_service.assert_called_once_with(group_id=group_id, skip=5, limit=5, token=None, timezone_name=None) @patch('pecha_api.group_accumulator.group_accumulator_views.get_group_accumulators_service') def test_get_group_accumulators_empty(self, mock_service): diff --git a/tests/mantra/test_mantra_views.py b/tests/mantra/test_mantra_views.py index 6cc778574..9c869d0d2 100644 --- a/tests/mantra/test_mantra_views.py +++ b/tests/mantra/test_mantra_views.py @@ -50,7 +50,7 @@ def test_get_mantras_endpoint_success(self, mock_service): assert isinstance(result, MantraResponse) assert len(result.mantras) == 2 - mock_service.assert_called_once_with(language=None) + mock_service.assert_called_once_with(language=None, timezone_name=None) @patch('pecha_api.mantra.mantra_views.get_mantras_service') def test_get_mantras_endpoint_empty(self, mock_service): @@ -76,4 +76,4 @@ def test_get_mantras_endpoint_with_language(self, mock_service): assert len(result.mantras) == 1 assert result.mantras[0].metadata[0].language == LanguageCode.BO - mock_service.assert_called_once_with(language="bo") + mock_service.assert_called_once_with(language="bo", timezone_name=None) diff --git a/tests/plans/groups/test_groups_service.py b/tests/plans/groups/test_groups_service.py index 3d1447a34..cdd5489c4 100644 --- a/tests/plans/groups/test_groups_service.py +++ b/tests/plans/groups/test_groups_service.py @@ -54,6 +54,7 @@ leave_group, unfollow_group, update_author_group, + delete_author_group, transfer_group_ownership, update_group_member_role, OWNER_ROLE_NOT_ASSIGNABLE, @@ -1924,6 +1925,71 @@ def test_update_author_group_forbidden_for_viewer(): assert exc.value.status_code == status.HTTP_403_FORBIDDEN +def test_delete_author_group_success_as_owner(): + author = _make_author() + group = _make_group() + owner = MagicMock() + owner.role = AuthorGroupMemberRole.OWNER + + with patch("pecha_api.plans.groups.groups_service.SessionLocal") as mock_session, patch( + "pecha_api.plans.groups.groups_service.validate_and_extract_author_details", + return_value=author, + ), patch( + "pecha_api.plans.groups.groups_service.get_group_by_id", + return_value=group, + ), patch( + "pecha_api.plans.groups.groups_service.get_group_member", + return_value=owner, + ), patch( + "pecha_api.plans.groups.groups_service.update_group", + ) as mock_update: + _session_local_context(mock_session) + delete_author_group(token="t", group_id=group.id) + + assert group.deleted_at is not None + assert group.deleted_by == author.email + mock_update.assert_called_once() + + +def test_delete_author_group_forbidden_for_admin_member(): + author = _make_author() + group = _make_group() + admin = MagicMock() + admin.role = AuthorGroupMemberRole.ADMIN + + with patch("pecha_api.plans.groups.groups_service.SessionLocal") as mock_session, patch( + "pecha_api.plans.groups.groups_service.validate_and_extract_author_details", + return_value=author, + ), patch( + "pecha_api.plans.groups.groups_service.get_group_by_id", + return_value=group, + ), patch( + "pecha_api.plans.groups.groups_service.get_group_member", + return_value=admin, + ): + _session_local_context(mock_session) + with pytest.raises(HTTPException) as exc: + delete_author_group(token="t", group_id=group.id) + assert exc.value.status_code == status.HTTP_403_FORBIDDEN + + +def test_delete_author_group_not_found(): + author = _make_author() + + with patch("pecha_api.plans.groups.groups_service.SessionLocal") as mock_session, patch( + "pecha_api.plans.groups.groups_service.validate_and_extract_author_details", + return_value=author, + ), patch( + "pecha_api.plans.groups.groups_service.get_group_by_id", + return_value=None, + ): + _session_local_context(mock_session) + with pytest.raises(HTTPException) as exc: + delete_author_group(token="t", group_id=uuid4()) + assert exc.value.status_code == status.HTTP_404_NOT_FOUND + assert exc.value.detail == GROUP_NOT_FOUND + + def test_revoke_group_invite_not_found(): author = _make_author() group = _make_group() diff --git a/tests/plans/groups/test_groups_views.py b/tests/plans/groups/test_groups_views.py index 45ad4cc7e..ac0ecb483 100644 --- a/tests/plans/groups/test_groups_views.py +++ b/tests/plans/groups/test_groups_views.py @@ -103,6 +103,7 @@ def test_get_public_groups_success(): skip=0, limit=20, token=None, + timezone_name=None, ) assert response.json()["total"] == 1 @@ -134,6 +135,7 @@ def test_get_public_groups_with_auth_passes_token(): skip=0, limit=20, token="dummy", + timezone_name=None, ) @@ -298,6 +300,19 @@ def test_patch_cms_group_delegates_to_service(): mock_service.assert_called_once() +def test_delete_cms_group_delegates_to_service(): + group_id = uuid4() + with patch( + "pecha_api.plans.groups.groups_views.delete_author_group", + ) as mock_service: + response = client.delete( + f"/cms/author/groups/{group_id}", + headers={"Authorization": "Bearer dummy"}, + ) + assert response.status_code == status.HTTP_204_NO_CONTENT + mock_service.assert_called_once_with(token="dummy", group_id=group_id) + + def test_get_cms_group_by_id(): group_id = uuid4() with patch( diff --git a/tests/plans/public/test_plan_public_service.py b/tests/plans/public/test_plan_public_service.py index 62346abd4..10b8e73fa 100644 --- a/tests/plans/public/test_plan_public_service.py +++ b/tests/plans/public/test_plan_public_service.py @@ -1002,9 +1002,10 @@ async def test_get_plan_daily_content_resolves_plan_by_date_in_series(): ) assert result.plan_id == plan_one_id - mock_series_plans.assert_called_once_with( + mock_series_plans.assert_called_with( db=mock_db, series_id=series_id, language="en" ) + assert mock_series_plans.call_count == 2 mock_day_fn.assert_called_once_with(db=mock_db, plan_id=plan_one_id, day_number=3) diff --git a/tests/plans/public/test_plan_public_views.py b/tests/plans/public/test_plan_public_views.py index bea32649f..8abd3c589 100644 --- a/tests/plans/public/test_plan_public_views.py +++ b/tests/plans/public/test_plan_public_views.py @@ -110,7 +110,8 @@ async def test_get_plans_success(sample_plans_response): sort_by="title", sort_order="asc", skip=0, - limit=20 + limit=20, + timezone_name=None, ) @@ -132,7 +133,8 @@ async def test_get_plans_with_search_filter(sample_plans_response): sort_by="title", sort_order="asc", skip=0, - limit=20 + limit=20, + timezone_name=None, ) @@ -154,7 +156,8 @@ async def test_get_plans_with_language_filter(sample_plans_response): sort_by="title", sort_order="asc", skip=0, - limit=20 + limit=20, + timezone_name=None, ) @@ -176,7 +179,8 @@ async def test_get_plans_with_sorting(sample_plans_response): sort_by="subscription_count", sort_order="desc", skip=0, - limit=20 + limit=20, + timezone_name=None, ) @@ -196,7 +200,8 @@ async def test_get_plans_with_pagination(sample_plans_response): sort_by="title", sort_order="asc", skip=10, - limit=5 + limit=5, + timezone_name=None, ) @@ -218,7 +223,8 @@ async def test_get_plans_with_all_filters(sample_plans_response): sort_by="total_days", sort_order="desc", skip=5, - limit=10 + limit=10, + timezone_name=None, ) @@ -310,7 +316,7 @@ async def test_get_plan_details_success(sample_plan_dto): assert "image" in data assert "image" in data["author"] - mock_service.assert_called_once_with(plan_id=plan_id) + mock_service.assert_called_once_with(plan_id=plan_id, timezone_name=None) @pytest.mark.asyncio @@ -769,6 +775,7 @@ async def test_get_plans_with_tag_filter(sample_plans_response): sort_order="asc", skip=0, limit=20, + timezone_name=None, ) @@ -792,6 +799,7 @@ async def test_get_plans_with_group_filter(sample_plans_response): sort_order="asc", skip=0, limit=20, + timezone_name=None, ) diff --git a/tests/plans/series/test_series_views.py b/tests/plans/series/test_series_views.py index 47986028b..245e74990 100644 --- a/tests/plans/series/test_series_views.py +++ b/tests/plans/series/test_series_views.py @@ -97,7 +97,7 @@ def test_get_series_list_success(sample_series_list_response): assert response.status_code == status.HTTP_200_OK data = response.json() - mock_service.assert_called_once_with(search=None, skip=0, limit=10, language=None, group_id=None, token=None) + mock_service.assert_called_once_with(search=None, skip=0, limit=10, language=None, group_id=None, token=None, timezone_name=None) assert "series" in data assert data["skip"] == 0 @@ -212,7 +212,7 @@ def test_get_series_list_with_search_pagination(sample_series_dto): assert response.status_code == status.HTTP_200_OK data = response.json() - mock_service.assert_called_once_with(search="meditation", skip=2, limit=5, language=None, group_id=None, token=None) + mock_service.assert_called_once_with(search="meditation", skip=2, limit=5, language=None, group_id=None, token=None, timezone_name=None) assert data["series"] == [] assert data["skip"] == 2 @@ -307,7 +307,7 @@ def test_get_series_by_id_success(sample_series_dto): response = client.get(f"/series/{series_id}") assert response.status_code == status.HTTP_200_OK - mock_detail.assert_called_once_with(series_id=series_id, language=None, token=None) + mock_detail.assert_called_once_with(series_id=series_id, language=None, token=None, timezone_name=None) data = response.json() assert data["id"] == str(sample_series_dto.id) @@ -387,7 +387,7 @@ def test_get_series_by_id_includes_total_days_in_response(): assert response.status_code == status.HTTP_200_OK data = response.json() - mock_detail.assert_called_once_with(series_id=series_id, language=None, token=None) + mock_detail.assert_called_once_with(series_id=series_id, language=None, token=None, timezone_name=None) assert data["total_days"] == 8 assert len(data["plans"]) == 2 @@ -426,7 +426,7 @@ def test_get_series_list_returns_plan_count_not_plans(): assert response.status_code == status.HTTP_200_OK data = response.json() - mock_service.assert_called_once_with(search=None, skip=0, limit=10, language=None, group_id=None, token=None) + mock_service.assert_called_once_with(search=None, skip=0, limit=10, language=None, group_id=None, token=None, timezone_name=None) assert len(data["series"]) == 1 assert data["series"][0]["plan_count"] == 0 @@ -444,7 +444,7 @@ def test_get_series_list_with_group_filter(): response = client.get("/series", params={"group_id": str(group_id)}) assert response.status_code == status.HTTP_200_OK - mock_service.assert_called_once_with(search=None, skip=0, limit=10, language=None, group_id=group_id, token=None) + mock_service.assert_called_once_with(search=None, skip=0, limit=10, language=None, group_id=group_id, token=None, timezone_name=None) def test_update_series_accepts_empty_body(): @@ -631,7 +631,7 @@ def test_get_series_by_id_passes_language_param(sample_series_dto): response = client.get(f"/series/{series_id}", params={"language": "bo"}) assert response.status_code == status.HTTP_200_OK - mock_detail.assert_called_once_with(series_id=series_id, language="bo", token=None) + mock_detail.assert_called_once_with(series_id=series_id, language="bo", token=None, timezone_name=None) def test_get_cms_series_by_id_passes_language_param(sample_series_dto): diff --git a/tests/recitations/test_recitations_services.py b/tests/recitations/test_recitations_services.py index 581cbcd56..fa827e531 100644 --- a/tests/recitations/test_recitations_services.py +++ b/tests/recitations/test_recitations_services.py @@ -38,187 +38,72 @@ class TestGetListOfRecitationsService: """Test cases for get_list_of_recitations_service function.""" - @patch('pecha_api.recitations.recitations_services.get_collection_id_by_slug') - @patch('pecha_api.recitations.recitations_services.get_root_text_by_collection_id') @patch('pecha_api.recitations.recitations_services.get_recitations_with_image_urls') - @patch('pecha_api.recitations.recitations_services.get_recitations_with_first_segments') @pytest.mark.asyncio async def test_get_list_of_recitations_service_success( self, - mock_get_recitations_with_first_segments, mock_get_recitations_with_image_urls, - mock_get_root_text, - mock_get_collection_id ): - """Test successful retrieval of recitations list.""" - # Setup mocks - liturgy_collection_id = str(uuid4()) - mock_get_collection_id.return_value = liturgy_collection_id - - text_id = str(uuid4()) - text_title = "Test Recitation" - recitation_dto = RecitationDTO(text_id=UUID(text_id), title=text_title) - mock_recitations_response = RecitationsResponse(recitations=[recitation_dto], skip=0, limit=10, total=1) - mock_get_root_text.return_value = mock_recitations_response - mock_get_recitations_with_image_urls.return_value = [recitation_dto] - mock_get_recitations_with_first_segments.return_value = [recitation_dto] - - # Execute + """Test successful retrieval of recitations list from JSON order data.""" + mock_get_recitations_with_image_urls.side_effect = lambda recitations: recitations + result = await get_list_of_recitations_service(language="en") - - # Verify + assert isinstance(result, RecitationsResponse) - assert len(result.recitations) == 1 + assert len(result.recitations) == 10 assert result.skip == 0 assert result.limit == 10 - assert result.total == 1 - - recitation = result.recitations[0] - assert isinstance(recitation, RecitationDTO) - assert recitation.title == text_title - assert str(recitation.text_id) == text_id - - # Verify mock calls - mock_get_collection_id.assert_called_once_with(slug="Liturgy") - mock_get_root_text.assert_called_once_with( - collection_id=liturgy_collection_id, - language="en", - search=None, - skip=0, - limit=10 - ) - mock_get_recitations_with_first_segments.assert_awaited_once_with(recitations=[recitation_dto]) + assert result.total == 21 + assert result.recitations[0].title == "Refuge and Bodhichitta" + assert result.recitations[0].first_segment is not None + mock_get_recitations_with_image_urls.assert_called_once() - @patch('pecha_api.recitations.recitations_services.get_collection_id_by_slug') - @pytest.mark.asyncio - async def test_get_list_of_recitations_service_collection_not_found( - self, - mock_get_collection_id - ): - """Test get_list_of_recitations_service when Liturgy collection is not found.""" - mock_get_collection_id.return_value = None - - with pytest.raises(HTTPException) as exc_info: - await get_list_of_recitations_service(language="en") - - assert exc_info.value.status_code == status.HTTP_404_NOT_FOUND - assert exc_info.value.detail == ErrorConstants.COLLECTION_NOT_FOUND - mock_get_collection_id.assert_called_once_with(slug="Liturgy") - - @patch('pecha_api.recitations.recitations_services.get_collection_id_by_slug') - @patch('pecha_api.recitations.recitations_services.get_root_text_by_collection_id') @patch('pecha_api.recitations.recitations_services.get_recitations_with_image_urls') - @patch('pecha_api.recitations.recitations_services.get_recitations_with_first_segments') @pytest.mark.asyncio async def test_get_list_of_recitations_service_search_filter_no_match( self, - mock_get_recitations_with_first_segments, mock_get_recitations_with_image_urls, - mock_get_root_text, - mock_get_collection_id ): """Test get_list_of_recitations_service when search filter returns no match.""" - liturgy_collection_id = str(uuid4()) - mock_get_collection_id.return_value = liturgy_collection_id - - # Search returns empty results from DB - mock_recitations_response = RecitationsResponse(recitations=[], skip=0, limit=10, total=0) - mock_get_root_text.return_value = mock_recitations_response - mock_get_recitations_with_image_urls.return_value = [] - mock_get_recitations_with_first_segments.return_value = [] - + mock_get_recitations_with_image_urls.side_effect = lambda recitations: recitations + result = await get_list_of_recitations_service(search="nonexistent", language="en") - + assert isinstance(result, RecitationsResponse) assert len(result.recitations) == 0 assert result.recitations == [] assert result.total == 0 - - mock_get_collection_id.assert_called_once_with(slug="Liturgy") - mock_get_root_text.assert_called_once_with( - collection_id=liturgy_collection_id, - language="en", - search="nonexistent", - skip=0, - limit=10 - ) - @patch('pecha_api.recitations.recitations_services.get_collection_id_by_slug') - @patch('pecha_api.recitations.recitations_services.get_root_text_by_collection_id') @patch('pecha_api.recitations.recitations_services.get_recitations_with_image_urls') - @patch('pecha_api.recitations.recitations_services.get_recitations_with_first_segments') @pytest.mark.asyncio async def test_get_list_of_recitations_service_with_search_match( self, - mock_get_recitations_with_first_segments, mock_get_recitations_with_image_urls, - mock_get_root_text, - mock_get_collection_id ): """Test get_list_of_recitations_service when search filter matches.""" - liturgy_collection_id = str(uuid4()) - mock_get_collection_id.return_value = liturgy_collection_id - - text_id = str(uuid4()) - text_title = "Morning Prayer Recitation" - recitation_dto = RecitationDTO(text_id=UUID(text_id), title=text_title) - mock_recitations_response = RecitationsResponse(recitations=[recitation_dto], skip=0, limit=10, total=1) - mock_get_root_text.return_value = mock_recitations_response - mock_get_recitations_with_image_urls.return_value = [recitation_dto] - mock_get_recitations_with_first_segments.return_value = [recitation_dto] - result = await get_list_of_recitations_service(search="morning", language="en") - + mock_get_recitations_with_image_urls.side_effect = lambda recitations: recitations + + result = await get_list_of_recitations_service(search="refuge", language="en") + assert isinstance(result, RecitationsResponse) assert len(result.recitations) == 1 - assert result.recitations[0].title == text_title - assert str(result.recitations[0].text_id) == text_id - - mock_get_collection_id.assert_called_once_with(slug="Liturgy") - mock_get_root_text.assert_called_once_with( - collection_id=liturgy_collection_id, - language="en", - search="morning", - skip=0, - limit=10 - ) + assert result.recitations[0].title == "Refuge and Bodhichitta" + assert result.total == 1 - @patch('pecha_api.recitations.recitations_services.get_collection_id_by_slug') - @patch('pecha_api.recitations.recitations_services.get_root_text_by_collection_id') @patch('pecha_api.recitations.recitations_services.get_recitations_with_image_urls') - @patch('pecha_api.recitations.recitations_services.get_recitations_with_first_segments') @pytest.mark.asyncio async def test_get_list_of_recitations_service_different_languages( - self, - mock_get_recitations_with_first_segments, + self, mock_get_recitations_with_image_urls, - mock_get_root_text, - mock_get_collection_id ): """Test get_list_of_recitations_service with different language parameters.""" - liturgy_collection_id = str(uuid4()) - mock_get_collection_id.return_value = liturgy_collection_id - - text_id = str(uuid4()) - text_title = "Tibetan Recitation" - recitation_dto = RecitationDTO(text_id=UUID(text_id), title=text_title) - mock_recitations_response = RecitationsResponse(recitations=[recitation_dto], skip=0, limit=10, total=1) - mock_get_root_text.return_value = mock_recitations_response - mock_get_recitations_with_image_urls.return_value = [recitation_dto] - mock_get_recitations_with_first_segments.return_value = [recitation_dto] - result = await get_list_of_recitations_service(language="bo") - - assert isinstance(result, RecitationsResponse) - assert len(result.recitations) == 1 - assert result.recitations[0].title == text_title - - # Verify language is passed correctly - mock_get_root_text.assert_called_once_with( - collection_id=liturgy_collection_id, - language="bo", - search=None, - skip=0, - limit=10 - ) + mock_get_recitations_with_image_urls.side_effect = lambda recitations: recitations + + bo_result = await get_list_of_recitations_service(language="bo") + en_result = await get_list_of_recitations_service(language="fr") + + assert bo_result.recitations[0].title == "སྐྱབས་འགྲོ་སེམས་བསྐྱེད།" + assert en_result.recitations[0].title == "Refuge and Bodhichitta" class TestGetRecitationsWithFirstSegments: diff --git a/tests/recitations/test_recitations_views.py b/tests/recitations/test_recitations_views.py index 5da268569..cc71ffb7b 100644 --- a/tests/recitations/test_recitations_views.py +++ b/tests/recitations/test_recitations_views.py @@ -34,7 +34,9 @@ async def test_get_list_of_recitations_success(): ) as mock_service: resp = await get_list_of_recitations(search=None, language="en", skip=0, limit=10) - mock_service.assert_awaited_once_with(search=None, language="en", skip=0, limit=10) + mock_service.assert_awaited_once_with( + search=None, language="en", skip=0, limit=10, timezone_name=None + ) assert resp == expected assert len(resp.recitations) == 2 assert resp.skip == 0 @@ -64,7 +66,9 @@ async def test_get_list_of_recitations_single_recitation(): ) as mock_service: resp = await get_list_of_recitations(search=None, language="bo", skip=0, limit=10) - mock_service.assert_awaited_once_with(search=None, language="bo", skip=0, limit=10) + mock_service.assert_awaited_once_with( + search=None, language="bo", skip=0, limit=10, timezone_name=None + ) assert resp == expected assert len(resp.recitations) == 1 assert resp.total == 1 @@ -92,7 +96,9 @@ async def test_get_list_of_recitations_with_search(): ) as mock_service: resp = await get_list_of_recitations(search="prayer", language="en", skip=0, limit=10) - mock_service.assert_awaited_once_with(search="prayer", language="en", skip=0, limit=10) + mock_service.assert_awaited_once_with( + search="prayer", language="en", skip=0, limit=10, timezone_name=None + ) assert resp == expected assert len(resp.recitations) == 1 assert resp.total == 1 @@ -131,7 +137,9 @@ async def test_get_recitation_details_success(): ) as mock_service: resp = await get_recitation_details(text_id=text_id, recitation_details_request=request) - mock_service.assert_awaited_once_with(text_id=text_id, recitation_details_request=request) + mock_service.assert_awaited_once_with( + text_id=text_id, recitation_details_request=request, timezone_name=None + ) assert resp == expected_response assert resp.text_id == UUID(text_id) assert resp.title == "Test Recitation" @@ -176,7 +184,9 @@ async def test_get_recitation_details_with_multiple_segments(): ) as mock_service: resp = await get_recitation_details(text_id=text_id, recitation_details_request=request) - mock_service.assert_awaited_once_with(text_id=text_id, recitation_details_request=request) + mock_service.assert_awaited_once_with( + text_id=text_id, recitation_details_request=request, timezone_name=None + ) assert resp == expected_response assert len(resp.segments) == 2 @@ -216,7 +226,9 @@ async def test_get_recitation_details_with_all_types(): ) as mock_service: resp = await get_recitation_details(text_id=text_id, recitation_details_request=request) - mock_service.assert_awaited_once_with(text_id=text_id, recitation_details_request=request) + mock_service.assert_awaited_once_with( + text_id=text_id, recitation_details_request=request, timezone_name=None + ) assert resp == expected_response assert len(resp.segments[0].recitation) == 1 assert len(resp.segments[0].translations) == 2 diff --git a/tests/region_restrictions/test_region_restriction_admin_service.py b/tests/region_restrictions/test_region_restriction_admin_service.py new file mode 100644 index 000000000..4df243e7a --- /dev/null +++ b/tests/region_restrictions/test_region_restriction_admin_service.py @@ -0,0 +1,224 @@ +from datetime import datetime, timezone +from types import SimpleNamespace +from unittest.mock import MagicMock, patch +from uuid import uuid4 + +import pytest +from fastapi import HTTPException + +from pecha_api.region_restrictions.region_restriction_admin_service import ( + create_admin_china_restricted_item, + delete_admin_china_restricted_item, + list_admin_china_restricted_items, + search_admin_china_restriction_candidates, +) +from pecha_api.region_restrictions.region_restriction_enums import RestrictedItemType +from pecha_api.region_restrictions.region_restriction_response_models import ( + ChinaRestrictionCandidateDTO, + CreateChinaRestrictedItemRequest, +) + + +def _author(): + return SimpleNamespace(id=uuid4(), email="admin@example.com") + + +def _row(*, item_type=RestrictedItemType.PLAN, item_id=None, title_time=None): + now = title_time or datetime(2026, 7, 1, tzinfo=timezone.utc) + return SimpleNamespace( + id=uuid4(), + item_type=item_type, + item_id=item_id or uuid4(), + created_at=now, + updated_at=now, + ) + + +@patch("pecha_api.region_restrictions.region_restriction_admin_service.resolve_titles_for_rows") +@patch("pecha_api.region_restrictions.region_restriction_admin_service.list_china_restricted_items") +@patch("pecha_api.region_restrictions.region_restriction_admin_service.SessionLocal") +@patch("pecha_api.region_restrictions.region_restriction_admin_service.require_super_admin_or_reviewer") +@patch("pecha_api.region_restrictions.region_restriction_admin_service.validate_and_extract_author_details") +def test_list_admin_china_restricted_items_enriches_titles( + mock_validate, + mock_require, + mock_session_local, + mock_list, + mock_titles, +): + author = _author() + mock_validate.return_value = author + row = _row(item_id=uuid4()) + mock_list.return_value = ([row], 1) + mock_titles.return_value = {row.item_id: "Morning Practice"} + db = MagicMock() + mock_session_local.return_value.__enter__.return_value = db + + result = list_admin_china_restricted_items( + token="token", + skip=0, + limit=20, + item_type=RestrictedItemType.PLAN, + ) + + mock_require.assert_called_once_with(author) + mock_list.assert_called_once_with( + db=db, skip=0, limit=20, item_type=RestrictedItemType.PLAN + ) + assert result.total == 1 + assert result.items[0].title == "Morning Practice" + assert result.items[0].item_id == row.item_id + + +@patch("pecha_api.region_restrictions.region_restriction_admin_service.search_restriction_candidates") +@patch("pecha_api.region_restrictions.region_restriction_admin_service.SessionLocal") +@patch("pecha_api.region_restrictions.region_restriction_admin_service.require_super_admin_or_reviewer") +@patch("pecha_api.region_restrictions.region_restriction_admin_service.validate_and_extract_author_details") +def test_search_admin_china_restriction_candidates( + mock_validate, + mock_require, + mock_session_local, + mock_search, +): + mock_validate.return_value = _author() + candidate = ChinaRestrictionCandidateDTO(id=uuid4(), title="Heart Sutra") + mock_search.return_value = ([candidate], 1) + db = MagicMock() + mock_session_local.return_value.__enter__.return_value = db + + result = search_admin_china_restriction_candidates( + token="token", + item_type=RestrictedItemType.RECITATION, + search="heart", + skip=0, + limit=10, + ) + + mock_search.assert_called_once_with( + db=db, + item_type=RestrictedItemType.RECITATION, + search="heart", + skip=0, + limit=10, + ) + assert result.total == 1 + assert result.items[0].title == "Heart Sutra" + + +@patch("pecha_api.region_restrictions.region_restriction_admin_service.clear_restricted_items_cache") +@patch("pecha_api.region_restrictions.region_restriction_admin_service.resolve_titles_for_rows") +@patch("pecha_api.region_restrictions.region_restriction_admin_service.create_china_restricted_item") +@patch("pecha_api.region_restrictions.region_restriction_admin_service.is_item_restricted_in_china") +@patch("pecha_api.region_restrictions.region_restriction_admin_service.SessionLocal") +@patch("pecha_api.region_restrictions.region_restriction_admin_service.require_super_admin") +@patch("pecha_api.region_restrictions.region_restriction_admin_service.validate_and_extract_author_details") +def test_create_admin_china_restricted_item_success( + mock_validate, + mock_require, + mock_session_local, + mock_is_restricted, + mock_create, + mock_titles, + mock_clear_cache, +): + author = _author() + mock_validate.return_value = author + mock_is_restricted.return_value = False + item_id = uuid4() + row = _row(item_type=RestrictedItemType.SERIES, item_id=item_id) + mock_create.return_value = row + mock_titles.return_value = {item_id: "Series Title"} + db = MagicMock() + mock_session_local.return_value.__enter__.return_value = db + body = CreateChinaRestrictedItemRequest( + item_type=RestrictedItemType.SERIES, + item_id=item_id, + ) + + result = create_admin_china_restricted_item(token="token", body=body) + + mock_require.assert_called_once_with(author) + mock_create.assert_called_once_with( + db=db, item_type=RestrictedItemType.SERIES, item_id=item_id + ) + mock_clear_cache.assert_called_once() + assert result.title == "Series Title" + assert result.item_id == item_id + + +@patch("pecha_api.region_restrictions.region_restriction_admin_service.is_item_restricted_in_china") +@patch("pecha_api.region_restrictions.region_restriction_admin_service.SessionLocal") +@patch("pecha_api.region_restrictions.region_restriction_admin_service.require_super_admin") +@patch("pecha_api.region_restrictions.region_restriction_admin_service.validate_and_extract_author_details") +def test_create_admin_china_restricted_item_conflict( + mock_validate, + mock_require, + mock_session_local, + mock_is_restricted, +): + mock_validate.return_value = _author() + mock_is_restricted.return_value = True + mock_session_local.return_value.__enter__.return_value = MagicMock() + body = CreateChinaRestrictedItemRequest( + item_type=RestrictedItemType.PLAN, + item_id=uuid4(), + ) + + with pytest.raises(HTTPException) as exc_info: + create_admin_china_restricted_item(token="token", body=body) + + assert exc_info.value.status_code == 409 + + +@patch("pecha_api.region_restrictions.region_restriction_admin_service.clear_restricted_items_cache") +@patch("pecha_api.region_restrictions.region_restriction_admin_service.delete_china_restricted_item_by_id") +@patch("pecha_api.region_restrictions.region_restriction_admin_service.SessionLocal") +@patch("pecha_api.region_restrictions.region_restriction_admin_service.require_super_admin") +@patch("pecha_api.region_restrictions.region_restriction_admin_service.validate_and_extract_author_details") +def test_delete_admin_china_restricted_item_success( + mock_validate, + mock_require, + mock_session_local, + mock_delete, + mock_clear_cache, +): + mock_validate.return_value = _author() + mock_delete.return_value = True + mock_session_local.return_value.__enter__.return_value = MagicMock() + row_id = uuid4() + + delete_admin_china_restricted_item(token="token", row_id=row_id) + + mock_delete.assert_called_once() + mock_clear_cache.assert_called_once() + + +@patch("pecha_api.region_restrictions.region_restriction_admin_service.delete_china_restricted_item_by_id") +@patch("pecha_api.region_restrictions.region_restriction_admin_service.SessionLocal") +@patch("pecha_api.region_restrictions.region_restriction_admin_service.require_super_admin") +@patch("pecha_api.region_restrictions.region_restriction_admin_service.validate_and_extract_author_details") +def test_delete_admin_china_restricted_item_not_found( + mock_validate, + mock_require, + mock_session_local, + mock_delete, +): + mock_validate.return_value = _author() + mock_delete.return_value = False + mock_session_local.return_value.__enter__.return_value = MagicMock() + + with pytest.raises(HTTPException) as exc_info: + delete_admin_china_restricted_item(token="token", row_id=uuid4()) + + assert exc_info.value.status_code == 404 + + +def test_row_to_dto_normalizes_string_item_type(): + from pecha_api.region_restrictions.region_restriction_admin_service import _row_to_dto + + row = _row(item_type="PLAN") + row.updated_at = None + dto = _row_to_dto(row, title="From string type") + assert dto.item_type == RestrictedItemType.PLAN + assert dto.title == "From string type" + assert dto.updated_at is None diff --git a/tests/region_restrictions/test_region_restriction_item_lookup.py b/tests/region_restrictions/test_region_restriction_item_lookup.py new file mode 100644 index 000000000..25f83e719 --- /dev/null +++ b/tests/region_restrictions/test_region_restriction_item_lookup.py @@ -0,0 +1,405 @@ +import uuid +from types import SimpleNamespace +from unittest.mock import MagicMock, patch + +from pecha_api.region_restrictions.region_restriction_enums import RestrictedItemType +from pecha_api.region_restrictions.region_restriction_item_lookup import ( + _accumulator_display_title, + _mantra_display_title, + _pick_metadata_text, + resolve_titles_for_rows, + search_restriction_candidates, +) + + +def test_pick_metadata_text_prefers_english(): + entries = [ + SimpleNamespace(language="BO", title="བོད་"), + SimpleNamespace(language="EN", title="English"), + SimpleNamespace(language="ZH", title="中文"), + ] + assert _pick_metadata_text(entries) == "English" + + +def test_pick_metadata_text_falls_back_to_first_nonempty(): + entries = [ + SimpleNamespace(language="XX", title=""), + SimpleNamespace(language="YY", title="Fallback"), + ] + assert _pick_metadata_text(entries) == "Fallback" + assert _pick_metadata_text([]) is None + + +def test_mantra_display_title_uses_title_then_mantra_text(): + mantra = SimpleNamespace( + metadata_entries=[SimpleNamespace(language="EN", title="Om", mantra=None)] + ) + assert _mantra_display_title(mantra) == "Om" + + long_mantra = "x" * 100 + mantra_only = SimpleNamespace( + metadata_entries=[ + SimpleNamespace(language="EN", title=None, mantra=long_mantra) + ] + ) + title = _mantra_display_title(mantra_only) + assert title is not None + assert title.startswith("xxx") + assert title.endswith("…") + assert len(title) <= 80 + + +def test_accumulator_display_title_falls_back_to_mantra(): + mantra_id = uuid.uuid4() + acc = SimpleNamespace(metadata_entries=[], mantra_id=mantra_id) + mantra = SimpleNamespace( + metadata_entries=[SimpleNamespace(language="EN", title="Mantra Name", mantra=None)] + ) + assert _accumulator_display_title(acc, {mantra_id: mantra}) == "Mantra Name" + assert _accumulator_display_title(acc, {}) is None + + +def test_resolve_titles_for_plans(): + plan_id = uuid.uuid4() + db = MagicMock() + db.query.return_value.filter.return_value.all.return_value = [ + SimpleNamespace(id=plan_id, title="Morning Practice") + ] + + titles = resolve_titles_for_rows( + db, + item_type=RestrictedItemType.PLAN, + item_ids=[plan_id], + ) + + assert titles == {plan_id: "Morning Practice"} + + +def test_resolve_titles_empty_ids(): + assert resolve_titles_for_rows(MagicMock(), item_type=RestrictedItemType.PLAN, item_ids=[]) == {} + + +def test_resolve_titles_for_series_group_mantra_group_accumulator_collection(): + series_id = uuid.uuid4() + group_id = uuid.uuid4() + mantra_id = uuid.uuid4() + ga_id = uuid.uuid4() + collection_id = uuid.uuid4() + + series = SimpleNamespace( + id=series_id, + metadata_entries=[SimpleNamespace(language="EN", title="Series A")], + ) + group = SimpleNamespace( + id=group_id, + metadata_entries=[SimpleNamespace(language="EN", title="Group A")], + ) + mantra = SimpleNamespace( + id=mantra_id, + metadata_entries=[SimpleNamespace(language="EN", title="Mantra A", mantra=None)], + ) + ga = SimpleNamespace(id=ga_id, title=" Group Acc ") + collection = SimpleNamespace(id=collection_id, name="My Collection") + + db = MagicMock() + + with patch( + "pecha_api.region_restrictions.region_restriction_item_lookup.selectinload", + return_value=MagicMock(), + ): + # SERIES + db.query.return_value.options.return_value.filter.return_value.all.return_value = [ + series + ] + assert resolve_titles_for_rows( + db, item_type=RestrictedItemType.SERIES, item_ids=[series_id] + ) == {series_id: "Series A"} + + # GROUP + db.query.return_value.options.return_value.filter.return_value.all.return_value = [ + group + ] + assert resolve_titles_for_rows( + db, item_type=RestrictedItemType.GROUP, item_ids=[group_id] + ) == {group_id: "Group A"} + + # MANTRA + db.query.return_value.options.return_value.filter.return_value.all.return_value = [ + mantra + ] + assert resolve_titles_for_rows( + db, item_type=RestrictedItemType.MANTRA, item_ids=[mantra_id] + ) == {mantra_id: "Mantra A"} + + # GROUP_ACCUMULATOR + db.query.return_value.filter.return_value.all.return_value = [ga] + assert resolve_titles_for_rows( + db, item_type=RestrictedItemType.GROUP_ACCUMULATOR, item_ids=[ga_id] + ) == {ga_id: "Group Acc"} + + # RECITATION_COLLECTION + db.query.return_value.filter.return_value.all.return_value = [collection] + assert resolve_titles_for_rows( + db, item_type=RestrictedItemType.RECITATION_COLLECTION, item_ids=[collection_id] + ) == {collection_id: "My Collection"} + + +def test_resolve_titles_for_accumulator_uses_metadata(): + acc_id = uuid.uuid4() + acc = SimpleNamespace( + id=acc_id, + mantra_id=None, + metadata_entries=[SimpleNamespace(language="EN", name="Acc Name")], + ) + db = MagicMock() + db.query.return_value.options.return_value.filter.return_value.all.return_value = [acc] + + with patch( + "pecha_api.region_restrictions.region_restriction_item_lookup.selectinload", + return_value=MagicMock(), + ), patch( + "pecha_api.region_restrictions.region_restriction_item_lookup.get_mantras_by_ids", + return_value={}, + ): + titles = resolve_titles_for_rows( + db, item_type=RestrictedItemType.ACCUMULATOR, item_ids=[acc_id] + ) + + assert titles == {acc_id: "Acc Name"} + + +def test_resolve_titles_for_recitation(): + text_id = uuid.uuid4() + fake_response = SimpleNamespace( + recitations=[SimpleNamespace(text_id=text_id, title="Heart Sutra")], + total=1, + ) + + with patch( + "pecha_api.region_restrictions.region_restriction_item_lookup._get_ordered_recitations_response", + return_value=fake_response, + ): + titles = resolve_titles_for_rows( + MagicMock(), + item_type=RestrictedItemType.RECITATION, + item_ids=[text_id], + ) + + assert titles == {text_id: "Heart Sutra"} + + +def test_search_restriction_candidates_plans(): + plan_id = uuid.uuid4() + db = MagicMock() + query = MagicMock() + db.query.return_value = query + query.filter.return_value = query + query.count.return_value = 1 + query.order_by.return_value.offset.return_value.limit.return_value.all.return_value = [ + SimpleNamespace(id=plan_id, title="Morning Practice") + ] + + items, total = search_restriction_candidates( + db, + item_type=RestrictedItemType.PLAN, + search="morning", + skip=0, + limit=20, + ) + + assert total == 1 + assert len(items) == 1 + assert items[0].id == plan_id + assert items[0].title == "Morning Practice" + + +def test_search_recitations_uses_order_loader(): + text_id = uuid.uuid4() + fake_response = SimpleNamespace( + recitations=[SimpleNamespace(text_id=text_id, title="Heart Sutra")], + total=1, + ) + + with patch( + "pecha_api.region_restrictions.region_restriction_item_lookup._get_ordered_recitations_response", + return_value=fake_response, + ): + items, total = search_restriction_candidates( + MagicMock(), + item_type=RestrictedItemType.RECITATION, + search="heart", + skip=0, + limit=20, + ) + + assert total == 1 + assert items[0].id == text_id + assert items[0].title == "Heart Sutra" + + +def test_search_recitations_returns_empty_on_loader_failure(): + with patch( + "pecha_api.region_restrictions.region_restriction_item_lookup._get_ordered_recitations_response", + side_effect=ValueError("boom"), + ): + items, total = search_restriction_candidates( + MagicMock(), + item_type=RestrictedItemType.RECITATION, + search="heart", + skip=0, + limit=20, + ) + + assert items == [] + assert total == 0 + + +def test_search_group_accumulators(): + ga_id = uuid.uuid4() + db = MagicMock() + query = MagicMock() + db.query.return_value = query + query.filter.return_value = query + query.count.return_value = 1 + query.order_by.return_value.offset.return_value.limit.return_value.all.return_value = [ + SimpleNamespace(id=ga_id, title="Group Acc") + ] + + items, total = search_restriction_candidates( + db, + item_type=RestrictedItemType.GROUP_ACCUMULATOR, + search="group", + skip=0, + limit=20, + ) + + assert total == 1 + assert items[0].id == ga_id + assert items[0].title == "Group Acc" + + +def test_search_recitation_collections(): + collection_id = uuid.uuid4() + db = MagicMock() + query = MagicMock() + db.query.return_value = query + query.filter.return_value = query + query.count.return_value = 1 + query.order_by.return_value.offset.return_value.limit.return_value.all.return_value = [ + SimpleNamespace(id=collection_id, name="Collection") + ] + + items, total = search_restriction_candidates( + db, + item_type=RestrictedItemType.RECITATION_COLLECTION, + search=None, + skip=0, + limit=20, + ) + + assert total == 1 + assert items[0].title == "Collection" + + +def test_search_series_groups_mantras_accumulators_with_selectinload_mocked(): + series_id = uuid.uuid4() + group_id = uuid.uuid4() + mantra_id = uuid.uuid4() + acc_id = uuid.uuid4() + + series = SimpleNamespace( + id=series_id, + metadata_entries=[SimpleNamespace(language="EN", title="Series Search")], + ) + group = SimpleNamespace( + id=group_id, + slug="group-a", + metadata_entries=[SimpleNamespace(language="EN", title="Group Search")], + ) + mantra = SimpleNamespace( + id=mantra_id, + metadata_entries=[ + SimpleNamespace(language="EN", title="Mantra Search", mantra=None) + ], + ) + acc = SimpleNamespace( + id=acc_id, + mantra_id=None, + metadata_entries=[SimpleNamespace(language="EN", name="Acc Search")], + ) + + def _query_chain(rows): + query = MagicMock() + query.options.return_value = query + query.filter.return_value = query + query.count.return_value = len(rows) + query.order_by.return_value.offset.return_value.limit.return_value.all.return_value = ( + rows + ) + return query + + with patch( + "pecha_api.region_restrictions.region_restriction_item_lookup.selectinload", + return_value=MagicMock(), + ), patch( + "pecha_api.region_restrictions.region_restriction_item_lookup.get_mantras_by_ids", + return_value={}, + ), patch( + "pecha_api.region_restrictions.region_restriction_item_lookup.exists", + return_value=MagicMock(), + ): + db = MagicMock() + db.query.return_value = _query_chain([series]) + items, total = search_restriction_candidates( + db, item_type=RestrictedItemType.SERIES, search="series", skip=0, limit=10 + ) + assert total == 1 + assert items[0].title == "Series Search" + + db.query.return_value = _query_chain([group]) + items, total = search_restriction_candidates( + db, item_type=RestrictedItemType.GROUP, search="group", skip=0, limit=10 + ) + assert total == 1 + assert items[0].title == "Group Search" + + db.query.return_value = _query_chain([mantra]) + items, total = search_restriction_candidates( + db, item_type=RestrictedItemType.MANTRA, search="mantra", skip=0, limit=10 + ) + assert total == 1 + assert items[0].title == "Mantra Search" + + db.query.return_value = _query_chain([acc]) + items, total = search_restriction_candidates( + db, + item_type=RestrictedItemType.ACCUMULATOR, + search="acc", + skip=0, + limit=10, + ) + assert total == 1 + assert items[0].title == "Acc Search" + + +def test_search_whitespace_search_treated_as_none_for_plans(): + plan_id = uuid.uuid4() + db = MagicMock() + query = MagicMock() + db.query.return_value = query + query.filter.return_value = query + query.count.return_value = 1 + query.order_by.return_value.offset.return_value.limit.return_value.all.return_value = [ + SimpleNamespace(id=plan_id, title="Any") + ] + + items, total = search_restriction_candidates( + db, + item_type=RestrictedItemType.PLAN, + search=" ", + skip=0, + limit=5, + ) + + assert total == 1 + assert items[0].id == plan_id diff --git a/tests/region_restrictions/test_region_restriction_repository.py b/tests/region_restrictions/test_region_restriction_repository.py new file mode 100644 index 000000000..d7545d887 --- /dev/null +++ b/tests/region_restrictions/test_region_restriction_repository.py @@ -0,0 +1,128 @@ +from unittest.mock import MagicMock, patch +from uuid import uuid4 + +from pecha_api.region_restrictions.region_restriction_enums import RestrictedItemType +from pecha_api.region_restrictions.region_restriction_models import ChinaRestrictedItem +from pecha_api.region_restrictions.region_restriction_repository import ( + create_china_restricted_item, + delete_china_restricted_item_by_id, + get_all_china_restricted_items, + is_item_restricted_in_china, + list_china_restricted_items, +) + + +def test_get_all_china_restricted_items(): + db = MagicMock() + rows = [MagicMock(), MagicMock()] + db.query.return_value.all.return_value = rows + + assert get_all_china_restricted_items(db=db) == rows + db.query.assert_called_once_with(ChinaRestrictedItem) + + +def test_list_china_restricted_items_without_type_filter(): + db = MagicMock() + query = MagicMock() + db.query.return_value = query + query.count.return_value = 2 + rows = [MagicMock(), MagicMock()] + query.order_by.return_value.offset.return_value.limit.return_value.all.return_value = rows + + result_rows, total = list_china_restricted_items(db=db, skip=5, limit=10) + + assert total == 2 + assert result_rows == rows + query.filter.assert_not_called() + query.order_by.assert_called_once() + query.order_by.return_value.offset.assert_called_once_with(5) + query.order_by.return_value.offset.return_value.limit.assert_called_once_with(10) + + +def test_list_china_restricted_items_with_type_filter(): + db = MagicMock() + query = MagicMock() + filtered = MagicMock() + db.query.return_value = query + query.filter.return_value = filtered + filtered.count.return_value = 1 + filtered.order_by.return_value.offset.return_value.limit.return_value.all.return_value = [ + MagicMock() + ] + + rows, total = list_china_restricted_items( + db=db, skip=0, limit=20, item_type=RestrictedItemType.MANTRA + ) + + assert total == 1 + assert len(rows) == 1 + query.filter.assert_called_once() + + +def test_create_china_restricted_item(): + db = MagicMock() + item_id = uuid4() + created = MagicMock() + created.item_type = RestrictedItemType.PLAN + created.item_id = item_id + + with patch( + "pecha_api.region_restrictions.region_restriction_repository.ChinaRestrictedItem", + return_value=created, + ) as mock_model: + row = create_china_restricted_item( + db=db, + item_type=RestrictedItemType.PLAN, + item_id=item_id, + ) + + mock_model.assert_called_once_with( + item_type=RestrictedItemType.PLAN, item_id=item_id + ) + db.add.assert_called_once_with(created) + db.commit.assert_called_once() + db.refresh.assert_called_once_with(created) + assert row is created + + +def test_delete_china_restricted_item_by_id_success(): + db = MagicMock() + row = MagicMock() + db.query.return_value.filter.return_value.first.return_value = row + + assert delete_china_restricted_item_by_id(db=db, row_id=uuid4()) is True + db.delete.assert_called_once_with(row) + db.commit.assert_called_once() + + +def test_delete_china_restricted_item_by_id_missing(): + db = MagicMock() + db.query.return_value.filter.return_value.first.return_value = None + + assert delete_china_restricted_item_by_id(db=db, row_id=uuid4()) is False + db.delete.assert_not_called() + db.commit.assert_not_called() + + +def test_is_item_restricted_in_china_true(): + db = MagicMock() + db.query.return_value.filter.return_value.first.return_value = (uuid4(),) + + assert ( + is_item_restricted_in_china( + db=db, item_type=RestrictedItemType.GROUP, item_id=uuid4() + ) + is True + ) + + +def test_is_item_restricted_in_china_false(): + db = MagicMock() + db.query.return_value.filter.return_value.first.return_value = None + + assert ( + is_item_restricted_in_china( + db=db, item_type=RestrictedItemType.GROUP, item_id=uuid4() + ) + is False + ) diff --git a/tests/region_restrictions/test_region_restriction_service.py b/tests/region_restrictions/test_region_restriction_service.py new file mode 100644 index 000000000..5366135d7 --- /dev/null +++ b/tests/region_restrictions/test_region_restriction_service.py @@ -0,0 +1,169 @@ +from unittest.mock import MagicMock, patch +from uuid import uuid4 + +import pytest +from fastapi import HTTPException + +from pecha_api.region_restrictions.china_timezone import ( + get_chinese_timezone_ids, + is_china_timezone, +) +from pecha_api.region_restrictions.region_restriction_enums import RestrictedItemType +from pecha_api.region_restrictions.region_restriction_service import ( + assert_visible_for_timezone, + clear_restricted_items_cache, + filter_items_for_timezone, + get_restricted_item_ids, + is_restricted_in_china, + should_hide_for_timezone, +) + + +@pytest.fixture(autouse=True) +def clear_cache(): + get_chinese_timezone_ids.cache_clear() + clear_restricted_items_cache() + yield + get_chinese_timezone_ids.cache_clear() + clear_restricted_items_cache() + + +def test_is_china_timezone_matches_configured_ids(): + assert is_china_timezone("Asia/Shanghai") is True + assert is_china_timezone("Asia/Hong_Kong") is True + assert is_china_timezone("America/Los_Angeles") is False + assert is_china_timezone(None) is False + assert is_china_timezone("") is False + assert is_china_timezone(" ") is False + assert is_china_timezone(" Asia/Shanghai ") is True + + +def test_filter_items_for_timezone_hides_restricted_items_in_china(): + visible_id = uuid4() + hidden_id = uuid4() + items = [{"id": visible_id}, {"id": hidden_id}] + + with patch( + "pecha_api.region_restrictions.region_restriction_service.get_restricted_item_ids", + return_value=frozenset({hidden_id}), + ): + filtered = filter_items_for_timezone( + items, + timezone_name="Asia/Shanghai", + item_type=RestrictedItemType.MANTRA, + id_of=lambda item: item["id"], + ) + + assert filtered == [{"id": visible_id}] + + +def test_filter_items_for_timezone_keeps_all_items_outside_china(): + hidden_id = uuid4() + items = [{"id": hidden_id}] + + with patch( + "pecha_api.region_restrictions.region_restriction_service.get_restricted_item_ids", + return_value=frozenset({hidden_id}), + ): + filtered = filter_items_for_timezone( + items, + timezone_name="America/Los_Angeles", + item_type=RestrictedItemType.MANTRA, + id_of=lambda item: item["id"], + ) + + assert filtered == items + + +def test_filter_items_for_timezone_keeps_all_when_no_restricted_ids(): + items = [{"id": uuid4()}, {"id": uuid4()}] + + with patch( + "pecha_api.region_restrictions.region_restriction_service.get_restricted_item_ids", + return_value=frozenset(), + ): + filtered = filter_items_for_timezone( + items, + timezone_name="Asia/Shanghai", + item_type=RestrictedItemType.PLAN, + id_of=lambda item: item["id"], + ) + + assert filtered == items + + +def test_should_hide_for_timezone(): + item_id = uuid4() + + with patch( + "pecha_api.region_restrictions.region_restriction_service.is_restricted_in_china", + return_value=True, + ): + assert should_hide_for_timezone("Asia/Shanghai", RestrictedItemType.PLAN, item_id) is True + assert should_hide_for_timezone("America/New_York", RestrictedItemType.PLAN, item_id) is False + + +def test_assert_visible_for_timezone_raises_for_restricted_item(): + item_id = uuid4() + + with patch( + "pecha_api.region_restrictions.region_restriction_service.should_hide_for_timezone", + return_value=True, + ): + with pytest.raises(HTTPException) as exc_info: + assert_visible_for_timezone( + timezone_name="Asia/Shanghai", + item_type=RestrictedItemType.MANTRA, + item_id=item_id, + not_found_detail="Not found", + ) + + assert exc_info.value.status_code == 404 + + +def test_assert_visible_for_timezone_allows_visible_item(): + item_id = uuid4() + + with patch( + "pecha_api.region_restrictions.region_restriction_service.should_hide_for_timezone", + return_value=False, + ): + assert_visible_for_timezone( + timezone_name="Asia/Shanghai", + item_type=RestrictedItemType.MANTRA, + item_id=item_id, + ) + + +def test_get_restricted_item_ids_and_is_restricted_in_china(): + item_id = uuid4() + other_id = uuid4() + + with patch( + "pecha_api.region_restrictions.region_restriction_service._load_restricted_item_ids_by_type", + return_value={RestrictedItemType.PLAN.value: frozenset({item_id})}, + ): + assert get_restricted_item_ids(RestrictedItemType.PLAN) == frozenset({item_id}) + assert get_restricted_item_ids(RestrictedItemType.MANTRA) == frozenset() + assert is_restricted_in_china(RestrictedItemType.PLAN, item_id) is True + assert is_restricted_in_china(RestrictedItemType.PLAN, other_id) is False + + +def test_load_restricted_item_ids_by_type_groups_rows(): + plan_id = uuid4() + mantra_id = uuid4() + rows = [ + MagicMock(item_type=RestrictedItemType.PLAN, item_id=plan_id), + MagicMock(item_type="MANTRA", item_id=mantra_id), + ] + + with patch( + "pecha_api.region_restrictions.region_restriction_service.SessionLocal" + ) as mock_session_local, patch( + "pecha_api.region_restrictions.region_restriction_service.get_all_china_restricted_items", + return_value=rows, + ): + mock_session_local.return_value.__enter__.return_value = MagicMock() + clear_restricted_items_cache() + assert get_restricted_item_ids(RestrictedItemType.PLAN) == frozenset({plan_id}) + assert get_restricted_item_ids(RestrictedItemType.MANTRA) == frozenset({mantra_id}) diff --git a/tests/region_restrictions/test_region_restriction_views.py b/tests/region_restrictions/test_region_restriction_views.py new file mode 100644 index 000000000..7ac8ea1d5 --- /dev/null +++ b/tests/region_restrictions/test_region_restriction_views.py @@ -0,0 +1,111 @@ +import uuid +from unittest.mock import patch + +from pecha_api.region_restrictions.region_restriction_enums import RestrictedItemType +from pecha_api.region_restrictions.region_restriction_response_models import ( + ChinaRestrictedItemDTO, + ChinaRestrictedItemListResponse, + ChinaRestrictionCandidateDTO, + ChinaRestrictionCandidateListResponse, + CreateChinaRestrictedItemRequest, +) +from pecha_api.region_restrictions.region_restriction_views import ( + delete_cms_china_restricted_item, + get_cms_china_restriction_candidates, + get_cms_china_restricted_items, + post_cms_china_restricted_item, +) + + +def test_get_cms_china_restricted_items_delegates_to_service(): + expected = ChinaRestrictedItemListResponse(items=[], skip=0, limit=20, total=0) + + with patch( + "pecha_api.region_restrictions.region_restriction_views.list_admin_china_restricted_items", + return_value=expected, + ) as mock_service: + resp = get_cms_china_restricted_items( + skip=0, + limit=20, + item_type=None, + token="token123", + ) + + assert resp == expected + mock_service.assert_called_once_with( + token="token123", + skip=0, + limit=20, + item_type=None, + ) + + +def test_get_cms_china_restriction_candidates_delegates_to_service(): + expected = ChinaRestrictionCandidateListResponse( + items=[ + ChinaRestrictionCandidateDTO( + id=uuid.uuid4(), + title="Morning Practice", + ) + ], + skip=0, + limit=20, + total=1, + ) + + with patch( + "pecha_api.region_restrictions.region_restriction_views.search_admin_china_restriction_candidates", + return_value=expected, + ) as mock_service: + resp = get_cms_china_restriction_candidates( + item_type=RestrictedItemType.PLAN, + search="morning", + skip=0, + limit=20, + token="token123", + ) + + assert resp == expected + mock_service.assert_called_once_with( + token="token123", + item_type=RestrictedItemType.PLAN, + search="morning", + skip=0, + limit=20, + ) + + +def test_post_cms_china_restricted_item_delegates_to_service(): + item_id = uuid.uuid4() + row_id = uuid.uuid4() + body = CreateChinaRestrictedItemRequest( + item_type=RestrictedItemType.PLAN, + item_id=item_id, + ) + expected = ChinaRestrictedItemDTO( + id=row_id, + item_type=RestrictedItemType.PLAN, + item_id=item_id, + title="Morning Practice", + created_at="2026-07-05T00:00:00+00:00", + ) + + with patch( + "pecha_api.region_restrictions.region_restriction_views.create_admin_china_restricted_item", + return_value=expected, + ) as mock_service: + resp = post_cms_china_restricted_item(body=body, token="token123") + + assert resp == expected + mock_service.assert_called_once_with(token="token123", body=body) + + +def test_delete_cms_china_restricted_item_delegates_to_service(): + row_id = uuid.uuid4() + + with patch( + "pecha_api.region_restrictions.region_restriction_views.delete_admin_china_restricted_item", + ) as mock_service: + delete_cms_china_restricted_item(row_id=row_id, token="token123") + + mock_service.assert_called_once_with(token="token123", row_id=row_id)