Compare commits
5 Commits
crud-edits
...
0673896133
| Author | SHA1 | Date | |
|---|---|---|---|
| 0673896133 | |||
| f165ccb941 | |||
| e0fb924a1d | |||
| cf5518c7ec | |||
| 26df03cfd7 |
@@ -3,7 +3,7 @@ import uuid
|
|||||||
from fastapi import APIRouter, status
|
from fastapi import APIRouter, status
|
||||||
|
|
||||||
from app.api.deps import CurrentUser, SessionDep
|
from app.api.deps import CurrentUser, SessionDep
|
||||||
from app.schemas.source import CitationCreate, CitationRead
|
from app.schemas.source import CitationCreate, CitationRead, CitationUpdate
|
||||||
from app.services import citation_service, tree_service
|
from app.services import citation_service, tree_service
|
||||||
|
|
||||||
router = APIRouter(prefix="/trees", tags=["citations"])
|
router = APIRouter(prefix="/trees", tags=["citations"])
|
||||||
@@ -31,6 +31,25 @@ async def list_citations(
|
|||||||
return [CitationRead.model_validate(c) for c in citations]
|
return [CitationRead.model_validate(c) for c in citations]
|
||||||
|
|
||||||
|
|
||||||
|
@router.patch("/{tree_id}/citations/{citation_id}", response_model=CitationRead)
|
||||||
|
async def update_citation(
|
||||||
|
tree_id: uuid.UUID,
|
||||||
|
citation_id: uuid.UUID,
|
||||||
|
data: CitationUpdate,
|
||||||
|
session: SessionDep,
|
||||||
|
current: CurrentUser,
|
||||||
|
) -> CitationRead:
|
||||||
|
tree = await tree_service.get_tree(session, viewer_id=current.id, tree_id=tree_id)
|
||||||
|
citation = await citation_service.update_citation(
|
||||||
|
session,
|
||||||
|
actor=current,
|
||||||
|
tree=tree,
|
||||||
|
citation_id=citation_id,
|
||||||
|
changes=data.model_dump(exclude_unset=True),
|
||||||
|
)
|
||||||
|
return CitationRead.model_validate(citation)
|
||||||
|
|
||||||
|
|
||||||
@router.delete("/{tree_id}/citations/{citation_id}", status_code=status.HTTP_204_NO_CONTENT)
|
@router.delete("/{tree_id}/citations/{citation_id}", status_code=status.HTTP_204_NO_CONTENT)
|
||||||
async def delete_citation(
|
async def delete_citation(
|
||||||
tree_id: uuid.UUID, citation_id: uuid.UUID, session: SessionDep, current: CurrentUser
|
tree_id: uuid.UUID, citation_id: uuid.UUID, session: SessionDep, current: CurrentUser
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import uuid
|
|||||||
from fastapi import APIRouter, File, Form, Response, UploadFile, status
|
from fastapi import APIRouter, File, Form, Response, UploadFile, status
|
||||||
|
|
||||||
from app.api.deps import CurrentUser, ObjectStoreDep, SessionDep
|
from app.api.deps import CurrentUser, ObjectStoreDep, SessionDep
|
||||||
from app.schemas.media import MediaRead
|
from app.schemas.media import MediaRead, MediaUpdate
|
||||||
from app.services import media_service, tree_service
|
from app.services import media_service, tree_service
|
||||||
|
|
||||||
|
|
||||||
@@ -81,6 +81,26 @@ async def media_content(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@router.patch("/{tree_id}/media/{media_id}", response_model=MediaRead)
|
||||||
|
async def update_media(
|
||||||
|
tree_id: uuid.UUID,
|
||||||
|
media_id: uuid.UUID,
|
||||||
|
data: MediaUpdate,
|
||||||
|
session: SessionDep,
|
||||||
|
current: CurrentUser,
|
||||||
|
store: ObjectStoreDep,
|
||||||
|
) -> MediaRead:
|
||||||
|
tree = await tree_service.get_tree(session, viewer_id=current.id, tree_id=tree_id)
|
||||||
|
media = await media_service.update_media(
|
||||||
|
session,
|
||||||
|
actor=current,
|
||||||
|
tree=tree,
|
||||||
|
media_id=media_id,
|
||||||
|
changes=data.model_dump(exclude_unset=True),
|
||||||
|
)
|
||||||
|
return _read(media)
|
||||||
|
|
||||||
|
|
||||||
@router.delete("/{tree_id}/media/{media_id}", status_code=status.HTTP_204_NO_CONTENT)
|
@router.delete("/{tree_id}/media/{media_id}", status_code=status.HTTP_204_NO_CONTENT)
|
||||||
async def delete_media(
|
async def delete_media(
|
||||||
tree_id: uuid.UUID, media_id: uuid.UUID, session: SessionDep, current: CurrentUser
|
tree_id: uuid.UUID, media_id: uuid.UUID, session: SessionDep, current: CurrentUser
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import uuid
|
|||||||
from fastapi import APIRouter, status
|
from fastapi import APIRouter, status
|
||||||
|
|
||||||
from app.api.deps import CurrentUser, SessionDep
|
from app.api.deps import CurrentUser, SessionDep
|
||||||
from app.schemas.relationship import RelationshipCreate, RelationshipRead
|
from app.schemas.relationship import RelationshipCreate, RelationshipRead, RelationshipUpdate
|
||||||
from app.services import relationship_service, tree_service
|
from app.services import relationship_service, tree_service
|
||||||
|
|
||||||
router = APIRouter(prefix="/trees", tags=["relationships"])
|
router = APIRouter(prefix="/trees", tags=["relationships"])
|
||||||
@@ -47,6 +47,25 @@ async def list_person_relationships(
|
|||||||
return [RelationshipRead.model_validate(r) for r in rels]
|
return [RelationshipRead.model_validate(r) for r in rels]
|
||||||
|
|
||||||
|
|
||||||
|
@router.patch("/{tree_id}/relationships/{relationship_id}", response_model=RelationshipRead)
|
||||||
|
async def update_relationship(
|
||||||
|
tree_id: uuid.UUID,
|
||||||
|
relationship_id: uuid.UUID,
|
||||||
|
data: RelationshipUpdate,
|
||||||
|
session: SessionDep,
|
||||||
|
current: CurrentUser,
|
||||||
|
) -> RelationshipRead:
|
||||||
|
tree = await tree_service.get_tree(session, viewer_id=current.id, tree_id=tree_id)
|
||||||
|
rel = await relationship_service.update_relationship(
|
||||||
|
session,
|
||||||
|
actor=current,
|
||||||
|
tree=tree,
|
||||||
|
relationship_id=relationship_id,
|
||||||
|
changes=data.model_dump(exclude_unset=True),
|
||||||
|
)
|
||||||
|
return RelationshipRead.model_validate(rel)
|
||||||
|
|
||||||
|
|
||||||
@router.delete(
|
@router.delete(
|
||||||
"/{tree_id}/relationships/{relationship_id}", status_code=status.HTTP_204_NO_CONTENT
|
"/{tree_id}/relationships/{relationship_id}", status_code=status.HTTP_204_NO_CONTENT
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import uuid
|
|||||||
from fastapi import APIRouter, status
|
from fastapi import APIRouter, status
|
||||||
|
|
||||||
from app.api.deps import CurrentUser, SessionDep
|
from app.api.deps import CurrentUser, SessionDep
|
||||||
from app.schemas.source import SourceCreate, SourceRead
|
from app.schemas.source import SourceCreate, SourceRead, SourceUpdate
|
||||||
from app.services import source_service, tree_service
|
from app.services import source_service, tree_service
|
||||||
|
|
||||||
router = APIRouter(prefix="/trees", tags=["sources"])
|
router = APIRouter(prefix="/trees", tags=["sources"])
|
||||||
@@ -40,6 +40,25 @@ async def get_source(
|
|||||||
return SourceRead.model_validate(source)
|
return SourceRead.model_validate(source)
|
||||||
|
|
||||||
|
|
||||||
|
@router.patch("/{tree_id}/sources/{source_id}", response_model=SourceRead)
|
||||||
|
async def update_source(
|
||||||
|
tree_id: uuid.UUID,
|
||||||
|
source_id: uuid.UUID,
|
||||||
|
data: SourceUpdate,
|
||||||
|
session: SessionDep,
|
||||||
|
current: CurrentUser,
|
||||||
|
) -> SourceRead:
|
||||||
|
tree = await tree_service.get_tree(session, viewer_id=current.id, tree_id=tree_id)
|
||||||
|
source = await source_service.update_source(
|
||||||
|
session,
|
||||||
|
actor=current,
|
||||||
|
tree=tree,
|
||||||
|
source_id=source_id,
|
||||||
|
changes=data.model_dump(exclude_unset=True),
|
||||||
|
)
|
||||||
|
return SourceRead.model_validate(source)
|
||||||
|
|
||||||
|
|
||||||
@router.delete("/{tree_id}/sources/{source_id}", status_code=status.HTTP_204_NO_CONTENT)
|
@router.delete("/{tree_id}/sources/{source_id}", status_code=status.HTTP_204_NO_CONTENT)
|
||||||
async def delete_source(
|
async def delete_source(
|
||||||
tree_id: uuid.UUID, source_id: uuid.UUID, session: SessionDep, current: CurrentUser
|
tree_id: uuid.UUID, source_id: uuid.UUID, session: SessionDep, current: CurrentUser
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import uuid
|
|||||||
from fastapi import APIRouter, status
|
from fastapi import APIRouter, status
|
||||||
|
|
||||||
from app.api.deps import CurrentUser, SessionDep
|
from app.api.deps import CurrentUser, SessionDep
|
||||||
from app.schemas.tree import TreeCreate, TreeRead
|
from app.schemas.tree import TreeCreate, TreeRead, TreeUpdate
|
||||||
from app.services import tree_service
|
from app.services import tree_service
|
||||||
|
|
||||||
router = APIRouter(prefix="/trees", tags=["trees"])
|
router = APIRouter(prefix="/trees", tags=["trees"])
|
||||||
@@ -38,6 +38,16 @@ async def get_tree(tree_id: uuid.UUID, session: SessionDep, current: CurrentUser
|
|||||||
return TreeRead.model_validate(tree)
|
return TreeRead.model_validate(tree)
|
||||||
|
|
||||||
|
|
||||||
|
@router.patch("/{tree_id}", response_model=TreeRead)
|
||||||
|
async def update_tree(
|
||||||
|
tree_id: uuid.UUID, data: TreeUpdate, session: SessionDep, current: CurrentUser
|
||||||
|
) -> TreeRead:
|
||||||
|
tree = await tree_service.update_tree(
|
||||||
|
session, actor=current, tree_id=tree_id, changes=data.model_dump(exclude_unset=True)
|
||||||
|
)
|
||||||
|
return TreeRead.model_validate(tree)
|
||||||
|
|
||||||
|
|
||||||
@router.delete("/{tree_id}", status_code=status.HTTP_204_NO_CONTENT)
|
@router.delete("/{tree_id}", status_code=status.HTTP_204_NO_CONTENT)
|
||||||
async def delete_tree(tree_id: uuid.UUID, session: SessionDep, current: CurrentUser) -> None:
|
async def delete_tree(tree_id: uuid.UUID, session: SessionDep, current: CurrentUser) -> None:
|
||||||
await tree_service.delete_tree(session, actor=current, tree_id=tree_id)
|
await tree_service.delete_tree(session, actor=current, tree_id=tree_id)
|
||||||
|
|||||||
@@ -4,6 +4,13 @@ from datetime import datetime
|
|||||||
from pydantic import BaseModel, ConfigDict
|
from pydantic import BaseModel, ConfigDict
|
||||||
|
|
||||||
|
|
||||||
|
class MediaUpdate(BaseModel):
|
||||||
|
title: str | None = None
|
||||||
|
person_id: uuid.UUID | None = None
|
||||||
|
event_id: uuid.UUID | None = None
|
||||||
|
source_id: uuid.UUID | None = None
|
||||||
|
|
||||||
|
|
||||||
class MediaRead(BaseModel):
|
class MediaRead(BaseModel):
|
||||||
model_config = ConfigDict(from_attributes=True)
|
model_config = ConfigDict(from_attributes=True)
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,11 @@ class RelationshipCreate(BaseModel):
|
|||||||
notes: str | None = None
|
notes: str | None = None
|
||||||
|
|
||||||
|
|
||||||
|
class RelationshipUpdate(BaseModel):
|
||||||
|
qualifier: ParentChildQualifier | None = None
|
||||||
|
notes: str | None = None
|
||||||
|
|
||||||
|
|
||||||
class RelationshipRead(BaseModel):
|
class RelationshipRead(BaseModel):
|
||||||
model_config = ConfigDict(from_attributes=True)
|
model_config = ConfigDict(from_attributes=True)
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,23 @@ class SourceRead(BaseModel):
|
|||||||
created_at: datetime
|
created_at: datetime
|
||||||
|
|
||||||
|
|
||||||
|
class SourceUpdate(BaseModel):
|
||||||
|
title: str | None = None
|
||||||
|
author: str | None = None
|
||||||
|
source_type: str | None = None
|
||||||
|
repository: str | None = None
|
||||||
|
url: str | None = None
|
||||||
|
citation_text: str | None = None
|
||||||
|
publication_info: str | None = None
|
||||||
|
quality_note: str | None = None
|
||||||
|
|
||||||
|
|
||||||
|
class CitationUpdate(BaseModel):
|
||||||
|
page: str | None = None
|
||||||
|
detail: str | None = None
|
||||||
|
confidence: CitationConfidence | None = None
|
||||||
|
|
||||||
|
|
||||||
class CitationCreate(BaseModel):
|
class CitationCreate(BaseModel):
|
||||||
source_id: uuid.UUID
|
source_id: uuid.UUID
|
||||||
# Exactly one target fact.
|
# Exactly one target fact.
|
||||||
|
|||||||
@@ -12,6 +12,12 @@ class TreeCreate(BaseModel):
|
|||||||
visibility: TreeVisibility = TreeVisibility.private
|
visibility: TreeVisibility = TreeVisibility.private
|
||||||
|
|
||||||
|
|
||||||
|
class TreeUpdate(BaseModel):
|
||||||
|
name: str | None = None
|
||||||
|
description: str | None = None
|
||||||
|
visibility: TreeVisibility | None = None
|
||||||
|
|
||||||
|
|
||||||
class TreeRead(BaseModel):
|
class TreeRead(BaseModel):
|
||||||
model_config = ConfigDict(from_attributes=True)
|
model_config = ConfigDict(from_attributes=True)
|
||||||
|
|
||||||
|
|||||||
@@ -113,6 +113,38 @@ async def list_citations(
|
|||||||
return list((await session.execute(stmt)).scalars().all())
|
return list((await session.execute(stmt)).scalars().all())
|
||||||
|
|
||||||
|
|
||||||
|
async def update_citation(
|
||||||
|
session: AsyncSession, *, actor: User, tree: Tree, citation_id: uuid.UUID, changes: dict
|
||||||
|
) -> Citation:
|
||||||
|
if not await privacy.can_edit_tree(session, user_id=actor.id, tree=tree):
|
||||||
|
raise Forbidden("not an editor of this tree")
|
||||||
|
citation = (
|
||||||
|
await session.execute(
|
||||||
|
select(Citation).where(
|
||||||
|
Citation.id == citation_id,
|
||||||
|
Citation.tree_id == tree.id,
|
||||||
|
Citation.deleted_at.is_(None),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
).scalar_one_or_none()
|
||||||
|
if citation is None:
|
||||||
|
raise NotFound("citation not found")
|
||||||
|
for key in {"page", "detail", "confidence"} & changes.keys():
|
||||||
|
setattr(citation, key, changes[key])
|
||||||
|
record_audit(
|
||||||
|
session,
|
||||||
|
action="update",
|
||||||
|
entity_type="Citation",
|
||||||
|
entity_id=citation.id,
|
||||||
|
tree_id=tree.id,
|
||||||
|
actor_user_id=actor.id,
|
||||||
|
after=changes,
|
||||||
|
)
|
||||||
|
await session.commit()
|
||||||
|
await session.refresh(citation)
|
||||||
|
return citation
|
||||||
|
|
||||||
|
|
||||||
async def delete_citation(
|
async def delete_citation(
|
||||||
session: AsyncSession, *, actor: User, tree: Tree, citation_id: uuid.UUID
|
session: AsyncSession, *, actor: User, tree: Tree, citation_id: uuid.UUID
|
||||||
) -> None:
|
) -> None:
|
||||||
|
|||||||
@@ -97,6 +97,36 @@ async def get_media(
|
|||||||
return media
|
return media
|
||||||
|
|
||||||
|
|
||||||
|
async def update_media(
|
||||||
|
session: AsyncSession, *, actor: User, tree: Tree, media_id: uuid.UUID, changes: dict
|
||||||
|
) -> Media:
|
||||||
|
if not await privacy.can_edit_tree(session, user_id=actor.id, tree=tree):
|
||||||
|
raise Forbidden("not an editor of this tree")
|
||||||
|
media = (
|
||||||
|
await session.execute(
|
||||||
|
select(Media).where(
|
||||||
|
Media.id == media_id, Media.tree_id == tree.id, Media.deleted_at.is_(None)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
).scalar_one_or_none()
|
||||||
|
if media is None:
|
||||||
|
raise NotFound("media not found")
|
||||||
|
for key in {"title", "person_id", "event_id", "source_id"} & changes.keys():
|
||||||
|
setattr(media, key, changes[key])
|
||||||
|
record_audit(
|
||||||
|
session,
|
||||||
|
action="update",
|
||||||
|
entity_type="Media",
|
||||||
|
entity_id=media.id,
|
||||||
|
tree_id=tree.id,
|
||||||
|
actor_user_id=actor.id,
|
||||||
|
after=changes,
|
||||||
|
)
|
||||||
|
await session.commit()
|
||||||
|
await session.refresh(media)
|
||||||
|
return media
|
||||||
|
|
||||||
|
|
||||||
async def delete_media(
|
async def delete_media(
|
||||||
session: AsyncSession, *, actor: User, tree: Tree, media_id: uuid.UUID
|
session: AsyncSession, *, actor: User, tree: Tree, media_id: uuid.UUID
|
||||||
) -> None:
|
) -> None:
|
||||||
|
|||||||
@@ -107,6 +107,44 @@ async def list_relationships_for_person(
|
|||||||
return list((await session.execute(stmt)).scalars().all())
|
return list((await session.execute(stmt)).scalars().all())
|
||||||
|
|
||||||
|
|
||||||
|
async def update_relationship(
|
||||||
|
session: AsyncSession, *, actor: User, tree: Tree, relationship_id: uuid.UUID, changes: dict
|
||||||
|
) -> Relationship:
|
||||||
|
if not await privacy.can_edit_tree(session, user_id=actor.id, tree=tree):
|
||||||
|
raise Forbidden("not an editor of this tree")
|
||||||
|
relationship = (
|
||||||
|
await session.execute(
|
||||||
|
select(Relationship).where(
|
||||||
|
Relationship.id == relationship_id,
|
||||||
|
Relationship.tree_id == tree.id,
|
||||||
|
Relationship.deleted_at.is_(None),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
).scalar_one_or_none()
|
||||||
|
if relationship is None:
|
||||||
|
raise NotFound("relationship not found")
|
||||||
|
if (
|
||||||
|
"qualifier" in changes
|
||||||
|
and changes["qualifier"] is not None
|
||||||
|
and relationship.type is not RelationshipType.parent_child
|
||||||
|
):
|
||||||
|
raise Conflict("qualifier only applies to parent_child relationships")
|
||||||
|
for key in {"qualifier", "notes"} & changes.keys():
|
||||||
|
setattr(relationship, key, changes[key])
|
||||||
|
record_audit(
|
||||||
|
session,
|
||||||
|
action="update",
|
||||||
|
entity_type="Relationship",
|
||||||
|
entity_id=relationship.id,
|
||||||
|
tree_id=tree.id,
|
||||||
|
actor_user_id=actor.id,
|
||||||
|
after=changes,
|
||||||
|
)
|
||||||
|
await session.commit()
|
||||||
|
await session.refresh(relationship)
|
||||||
|
return relationship
|
||||||
|
|
||||||
|
|
||||||
async def delete_relationship(
|
async def delete_relationship(
|
||||||
session: AsyncSession, *, actor: User, tree: Tree, relationship_id: uuid.UUID
|
session: AsyncSession, *, actor: User, tree: Tree, relationship_id: uuid.UUID
|
||||||
) -> None:
|
) -> None:
|
||||||
|
|||||||
@@ -86,6 +86,42 @@ async def get_source(
|
|||||||
return source
|
return source
|
||||||
|
|
||||||
|
|
||||||
|
_SOURCE_FIELDS = {
|
||||||
|
"title", "author", "source_type", "repository", "url", "citation_text",
|
||||||
|
"publication_info", "quality_note",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async def update_source(
|
||||||
|
session: AsyncSession, *, actor: User, tree: Tree, source_id: uuid.UUID, changes: dict
|
||||||
|
) -> Source:
|
||||||
|
if not await privacy.can_edit_tree(session, user_id=actor.id, tree=tree):
|
||||||
|
raise Forbidden("not an editor of this tree")
|
||||||
|
source = (
|
||||||
|
await session.execute(
|
||||||
|
select(Source).where(
|
||||||
|
Source.id == source_id, Source.tree_id == tree.id, Source.deleted_at.is_(None)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
).scalar_one_or_none()
|
||||||
|
if source is None:
|
||||||
|
raise NotFound("source not found")
|
||||||
|
for key in _SOURCE_FIELDS & changes.keys():
|
||||||
|
setattr(source, key, changes[key])
|
||||||
|
record_audit(
|
||||||
|
session,
|
||||||
|
action="update",
|
||||||
|
entity_type="Source",
|
||||||
|
entity_id=source.id,
|
||||||
|
tree_id=tree.id,
|
||||||
|
actor_user_id=actor.id,
|
||||||
|
after=changes,
|
||||||
|
)
|
||||||
|
await session.commit()
|
||||||
|
await session.refresh(source)
|
||||||
|
return source
|
||||||
|
|
||||||
|
|
||||||
async def delete_source(
|
async def delete_source(
|
||||||
session: AsyncSession, *, actor: User, tree: Tree, source_id: uuid.UUID
|
session: AsyncSession, *, actor: User, tree: Tree, source_id: uuid.UUID
|
||||||
) -> None:
|
) -> None:
|
||||||
|
|||||||
@@ -62,6 +62,30 @@ async def get_tree(session: AsyncSession, *, viewer_id: uuid.UUID, tree_id: uuid
|
|||||||
return tree
|
return tree
|
||||||
|
|
||||||
|
|
||||||
|
async def update_tree(
|
||||||
|
session: AsyncSession, *, actor: User, tree_id: uuid.UUID, changes: dict
|
||||||
|
) -> Tree:
|
||||||
|
tree = await BaseRepository(session, Tree).get(tree_id)
|
||||||
|
if tree is None:
|
||||||
|
raise NotFound("tree not found")
|
||||||
|
if not await privacy.can_edit_tree(session, user_id=actor.id, tree=tree):
|
||||||
|
raise Forbidden("not an editor of this tree")
|
||||||
|
for key in {"name", "description", "visibility"} & changes.keys():
|
||||||
|
setattr(tree, key, changes[key])
|
||||||
|
record_audit(
|
||||||
|
session,
|
||||||
|
action="update",
|
||||||
|
entity_type="Tree",
|
||||||
|
entity_id=tree.id,
|
||||||
|
tree_id=tree.id,
|
||||||
|
actor_user_id=actor.id,
|
||||||
|
after=changes,
|
||||||
|
)
|
||||||
|
await session.commit()
|
||||||
|
await session.refresh(tree)
|
||||||
|
return tree
|
||||||
|
|
||||||
|
|
||||||
async def _owned_tree(session: AsyncSession, *, actor: User, tree_id: uuid.UUID) -> Tree:
|
async def _owned_tree(session: AsyncSession, *, actor: User, tree_id: uuid.UUID) -> Tree:
|
||||||
"""Load a tree (including soft-deleted) and require the actor be its owner."""
|
"""Load a tree (including soft-deleted) and require the actor be its owner."""
|
||||||
tree = await BaseRepository(session, Tree).get(tree_id, include_deleted=True)
|
tree = await BaseRepository(session, Tree).get(tree_id, include_deleted=True)
|
||||||
|
|||||||
@@ -0,0 +1,79 @@
|
|||||||
|
"""Update (the U in CRUD) for the remaining entities — rule #8."""
|
||||||
|
|
||||||
|
from tests.conftest import auth, register
|
||||||
|
|
||||||
|
|
||||||
|
async def _setup(client, email):
|
||||||
|
h = auth(await register(client, email))
|
||||||
|
tid = (await client.post("/api/v1/trees", json={"name": "T"}, headers=h)).json()["id"]
|
||||||
|
return h, tid
|
||||||
|
|
||||||
|
|
||||||
|
async def test_tree_update(client):
|
||||||
|
h, tid = await _setup(client, "u-tree@example.com")
|
||||||
|
r = await client.patch(
|
||||||
|
f"/api/v1/trees/{tid}", json={"name": "Renamed", "visibility": "unlisted"}, headers=h
|
||||||
|
)
|
||||||
|
assert r.status_code == 200
|
||||||
|
assert r.json()["name"] == "Renamed" and r.json()["visibility"] == "unlisted"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_source_update(client):
|
||||||
|
h, tid = await _setup(client, "u-src@example.com")
|
||||||
|
sid = (
|
||||||
|
await client.post(f"/api/v1/trees/{tid}/sources", json={"title": "Old"}, headers=h)
|
||||||
|
).json()["id"]
|
||||||
|
r = await client.patch(
|
||||||
|
f"/api/v1/trees/{tid}/sources/{sid}",
|
||||||
|
json={"title": "New", "repository": "NARA"},
|
||||||
|
headers=h,
|
||||||
|
)
|
||||||
|
assert r.status_code == 200
|
||||||
|
assert r.json()["title"] == "New" and r.json()["repository"] == "NARA"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_media_update(client):
|
||||||
|
h, tid = await _setup(client, "u-media@example.com")
|
||||||
|
mid = (
|
||||||
|
await client.post(
|
||||||
|
f"/api/v1/trees/{tid}/media",
|
||||||
|
files={"file": ("a.txt", b"x", "text/plain")},
|
||||||
|
data={"title": "old"},
|
||||||
|
headers=h,
|
||||||
|
)
|
||||||
|
).json()["id"]
|
||||||
|
r = await client.patch(f"/api/v1/trees/{tid}/media/{mid}", json={"title": "new"}, headers=h)
|
||||||
|
assert r.status_code == 200 and r.json()["title"] == "new"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_relationship_and_citation_update(client):
|
||||||
|
h, tid = await _setup(client, "u-rc@example.com")
|
||||||
|
|
||||||
|
async def mk(path, body):
|
||||||
|
return (await client.post(f"/api/v1/trees/{tid}/{path}", json=body, headers=h)).json()["id"]
|
||||||
|
|
||||||
|
p1 = await mk("persons", {"given": "A"})
|
||||||
|
p2 = await mk("persons", {"given": "B"})
|
||||||
|
rid = await mk(
|
||||||
|
"relationships",
|
||||||
|
{
|
||||||
|
"type": "parent_child",
|
||||||
|
"person_from_id": p1,
|
||||||
|
"person_to_id": p2,
|
||||||
|
"qualifier": "biological",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
r = await client.patch(
|
||||||
|
f"/api/v1/trees/{tid}/relationships/{rid}", json={"qualifier": "adoptive"}, headers=h
|
||||||
|
)
|
||||||
|
assert r.status_code == 200 and r.json()["qualifier"] == "adoptive"
|
||||||
|
|
||||||
|
src = await mk("sources", {"title": "S"})
|
||||||
|
cid = await mk("citations", {"source_id": src, "person_id": p1})
|
||||||
|
r2 = await client.patch(
|
||||||
|
f"/api/v1/trees/{tid}/citations/{cid}",
|
||||||
|
json={"page": "p.7", "confidence": "high"},
|
||||||
|
headers=h,
|
||||||
|
)
|
||||||
|
assert r2.status_code == 200
|
||||||
|
assert r2.json()["page"] == "p.7" and r2.json()["confidence"] == "high"
|
||||||
@@ -9,6 +9,7 @@ import type { components } from "@/lib/api/schema";
|
|||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { PersonCombobox } from "@/components/person-combobox";
|
||||||
|
|
||||||
type Person = components["schemas"]["PersonRead"];
|
type Person = components["schemas"]["PersonRead"];
|
||||||
type Event = components["schemas"]["EventRead"];
|
type Event = components["schemas"]["EventRead"];
|
||||||
@@ -431,7 +432,11 @@ export default function PersonDetailPage() {
|
|||||||
<div className="flex flex-wrap gap-2">
|
<div className="flex flex-wrap gap-2">
|
||||||
<Input className="w-40" placeholder="Given name" value={pGiven} onChange={(e) => setPGiven(e.target.value)} />
|
<Input className="w-40" placeholder="Given name" value={pGiven} onChange={(e) => setPGiven(e.target.value)} />
|
||||||
<Input className="w-40" placeholder="Surname" value={pSurname} onChange={(e) => setPSurname(e.target.value)} />
|
<Input className="w-40" placeholder="Surname" value={pSurname} onChange={(e) => setPSurname(e.target.value)} />
|
||||||
<Input className="w-32" placeholder="Gender" value={pGender} onChange={(e) => setPGender(e.target.value)} />
|
<select className={fieldCls} value={pGender} onChange={(e) => setPGender(e.target.value)}>
|
||||||
|
<option value="">Gender: —</option>
|
||||||
|
<option value="male">Male</option>
|
||||||
|
<option value="female">Female</option>
|
||||||
|
</select>
|
||||||
<select className={fieldCls} value={pLiving} onChange={(e) => setPLiving(e.target.value)}>
|
<select className={fieldCls} value={pLiving} onChange={(e) => setPLiving(e.target.value)}>
|
||||||
<option value="unknown">Status: unknown</option>
|
<option value="unknown">Status: unknown</option>
|
||||||
<option value="living">Living</option>
|
<option value="living">Living</option>
|
||||||
@@ -672,14 +677,12 @@ export default function PersonDetailPage() {
|
|||||||
<option value="partner">partner</option>
|
<option value="partner">partner</option>
|
||||||
<option value="sibling">sibling</option>
|
<option value="sibling">sibling</option>
|
||||||
</select>
|
</select>
|
||||||
<select className={fieldCls} value={relOther} onChange={(e) => setRelOther(e.target.value)}>
|
<PersonCombobox
|
||||||
<option value="">— person —</option>
|
people={others}
|
||||||
{others.map((p) => (
|
value={relOther}
|
||||||
<option key={p.id} value={p.id}>
|
onChange={setRelOther}
|
||||||
{p.primary_name ?? "Unnamed"}
|
placeholder="Search for a person…"
|
||||||
</option>
|
/>
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
{(relKind === "parent" || relKind === "child") && (
|
{(relKind === "parent" || relKind === "child") && (
|
||||||
<select className={fieldCls} value={relQual} onChange={(e) => setRelQual(e.target.value as Qualifier)}>
|
<select className={fieldCls} value={relQual} onChange={(e) => setRelQual(e.target.value as Qualifier)}>
|
||||||
{QUALIFIERS.map((q) => (
|
{QUALIFIERS.map((q) => (
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|||||||
import { api } from "@/lib/api/client";
|
import { api } from "@/lib/api/client";
|
||||||
import type { components } from "@/lib/api/schema";
|
import type { components } from "@/lib/api/schema";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
import { FanChart } from "@/components/fan-chart";
|
import { FanChart } from "@/components/fan-chart";
|
||||||
|
|
||||||
type Person = components["schemas"]["PersonRead"];
|
type Person = components["schemas"]["PersonRead"];
|
||||||
@@ -28,6 +29,9 @@ export default function TreePage() {
|
|||||||
const params = useParams<{ id: string }>();
|
const params = useParams<{ id: string }>();
|
||||||
const treeId = params.id;
|
const treeId = params.id;
|
||||||
const containerRef = useRef<HTMLDivElement>(null);
|
const containerRef = useRef<HTMLDivElement>(null);
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
const chartRef = useRef<any>(null);
|
||||||
|
const [query, setQuery] = useState("");
|
||||||
|
|
||||||
const [people, setPeople] = useState<Person[]>([]);
|
const [people, setPeople] = useState<Person[]>([]);
|
||||||
const [rels, setRels] = useState<Relationship[]>([]);
|
const [rels, setRels] = useState<Relationship[]>([]);
|
||||||
@@ -117,19 +121,20 @@ export default function TreePage() {
|
|||||||
if (cancelled || !containerRef.current) return;
|
if (cancelled || !containerRef.current) return;
|
||||||
containerRef.current.innerHTML = "";
|
containerRef.current.innerHTML = "";
|
||||||
const chart = f3.createChart(containerRef.current, data);
|
const chart = f3.createChart(containerRef.current, data);
|
||||||
chart
|
chart.setCardHtml().setCardDisplay([["first name", "last name"], ["birthday"]]);
|
||||||
.setCardHtml()
|
|
||||||
.setCardDisplay([["first name", "last name"], ["birthday"]])
|
|
||||||
.setOnCardClick((_e: unknown, d: { data?: { id?: string } }) => {
|
|
||||||
const id = d?.data?.id;
|
|
||||||
if (id) {
|
|
||||||
setFocusId(id);
|
|
||||||
chart.updateMainId(id);
|
|
||||||
chart.updateTree();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (mode === "portrait") chart.setOrientationVertical();
|
if (mode === "portrait") chart.setOrientationVertical();
|
||||||
else chart.setOrientationHorizontal();
|
else chart.setOrientationHorizontal();
|
||||||
|
// Show enough generations that a recenter reveals grandparents + children.
|
||||||
|
chart.setAncestryDepth?.(3);
|
||||||
|
chart.setProgenyDepth?.(2);
|
||||||
|
// Default card click recenters the whole hourglass; sync focus for the
|
||||||
|
// "Open profile" link after every (re)build.
|
||||||
|
chart.setAfterUpdate?.(() => {
|
||||||
|
const md = chart.getMainDatum?.();
|
||||||
|
const id = md?.id ?? md?.data?.id;
|
||||||
|
if (id) setFocusId(id);
|
||||||
|
});
|
||||||
|
chartRef.current = chart;
|
||||||
if (focusId) chart.updateMainId(focusId);
|
if (focusId) chart.updateMainId(focusId);
|
||||||
chart.updateTree({ initial: true });
|
chart.updateTree({ initial: true });
|
||||||
})();
|
})();
|
||||||
@@ -139,6 +144,27 @@ export default function TreePage() {
|
|||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [status, mode, people, rels, events]);
|
}, [status, mode, people, rels, events]);
|
||||||
|
|
||||||
|
// Jump the tree (or fan) to a person and rebuild the hourglass around them.
|
||||||
|
const goTo = useCallback(
|
||||||
|
(id: string) => {
|
||||||
|
setFocusId(id);
|
||||||
|
setQuery("");
|
||||||
|
if (mode !== "fan" && chartRef.current) {
|
||||||
|
chartRef.current.updateMainId?.(id);
|
||||||
|
chartRef.current.updateTree?.();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[mode],
|
||||||
|
);
|
||||||
|
|
||||||
|
const matches = useMemo(() => {
|
||||||
|
const q = query.trim().toLowerCase();
|
||||||
|
if (!q) return [];
|
||||||
|
return people
|
||||||
|
.filter((p) => (p.primary_name ?? "").toLowerCase().includes(q))
|
||||||
|
.slice(0, 8);
|
||||||
|
}, [query, people]);
|
||||||
|
|
||||||
const ModeButton = ({ m, label }: { m: Mode; label: string }) => (
|
const ModeButton = ({ m, label }: { m: Mode; label: string }) => (
|
||||||
<button
|
<button
|
||||||
onClick={() => setMode(m)}
|
onClick={() => setMode(m)}
|
||||||
@@ -153,7 +179,34 @@ export default function TreePage() {
|
|||||||
return (
|
return (
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div className="flex flex-wrap items-center justify-between gap-3">
|
<div className="flex flex-wrap items-center justify-between gap-3">
|
||||||
<h1 className="text-2xl font-semibold">Tree</h1>
|
<div className="flex items-center gap-3">
|
||||||
|
<h1 className="text-2xl font-semibold">Tree</h1>
|
||||||
|
<div className="relative">
|
||||||
|
<Input
|
||||||
|
value={query}
|
||||||
|
onChange={(e) => setQuery(e.target.value)}
|
||||||
|
placeholder="Find a person…"
|
||||||
|
className="w-56"
|
||||||
|
/>
|
||||||
|
{matches.length > 0 && (
|
||||||
|
<ul className="absolute z-20 mt-1 w-72 overflow-hidden rounded-lg border border-[var(--border)] bg-[var(--surface)] shadow-lg">
|
||||||
|
{matches.map((p) => (
|
||||||
|
<li key={p.id}>
|
||||||
|
<button
|
||||||
|
onClick={() => goTo(p.id)}
|
||||||
|
className="flex w-full items-center justify-between gap-3 px-3 py-2 text-left text-sm hover:bg-[var(--muted-bg,rgba(0,0,0,0.04))]"
|
||||||
|
>
|
||||||
|
<span>{p.primary_name ?? "Unnamed"}</span>
|
||||||
|
{yearOf(p.id) && (
|
||||||
|
<span className="text-xs text-[var(--muted)]">{yearOf(p.id)}</span>
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<div className="flex items-center rounded-lg border border-[var(--border)] p-0.5">
|
<div className="flex items-center rounded-lg border border-[var(--border)] p-0.5">
|
||||||
<ModeButton m="landscape" label="Landscape" />
|
<ModeButton m="landscape" label="Landscape" />
|
||||||
|
|||||||
@@ -0,0 +1,103 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useEffect, useMemo, useRef, useState } from "react";
|
||||||
|
|
||||||
|
import type { components } from "@/lib/api/schema";
|
||||||
|
|
||||||
|
type Person = components["schemas"]["PersonRead"];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A type-to-filter person picker. Shows a text input; as you type, a dropdown
|
||||||
|
* of matching people appears. Selecting one sets `value` (a person id) and
|
||||||
|
* fills the input with their name. Replaces a plain <select> when the list is
|
||||||
|
* long enough that scanning it by hand is painful.
|
||||||
|
*/
|
||||||
|
export function PersonCombobox({
|
||||||
|
people,
|
||||||
|
value,
|
||||||
|
onChange,
|
||||||
|
placeholder = "Search for a person…",
|
||||||
|
className,
|
||||||
|
}: {
|
||||||
|
people: Person[];
|
||||||
|
value: string;
|
||||||
|
onChange: (id: string) => void;
|
||||||
|
placeholder?: string;
|
||||||
|
className?: string;
|
||||||
|
}) {
|
||||||
|
const [query, setQuery] = useState("");
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
|
const wrapRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
const nameOf = useMemo(
|
||||||
|
() => new Map(people.map((p) => [p.id, p.primary_name ?? "Unnamed"])),
|
||||||
|
[people],
|
||||||
|
);
|
||||||
|
|
||||||
|
// Keep the input text in sync when the selection changes externally
|
||||||
|
// (e.g. cleared to "" after a successful add).
|
||||||
|
useEffect(() => {
|
||||||
|
if (!value) {
|
||||||
|
setQuery("");
|
||||||
|
} else if (!open) {
|
||||||
|
setQuery(nameOf.get(value) ?? "");
|
||||||
|
}
|
||||||
|
}, [value, open, nameOf]);
|
||||||
|
|
||||||
|
// Close on outside click.
|
||||||
|
useEffect(() => {
|
||||||
|
function onDoc(e: MouseEvent) {
|
||||||
|
if (wrapRef.current && !wrapRef.current.contains(e.target as Node)) setOpen(false);
|
||||||
|
}
|
||||||
|
document.addEventListener("mousedown", onDoc);
|
||||||
|
return () => document.removeEventListener("mousedown", onDoc);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const matches = useMemo(() => {
|
||||||
|
const q = query.trim().toLowerCase();
|
||||||
|
const pool = q
|
||||||
|
? people.filter((p) => (p.primary_name ?? "").toLowerCase().includes(q))
|
||||||
|
: people;
|
||||||
|
return pool.slice(0, 10);
|
||||||
|
}, [query, people]);
|
||||||
|
|
||||||
|
const base =
|
||||||
|
"h-9 w-56 rounded-md border border-[var(--border)] bg-[var(--surface)] px-2 text-sm placeholder:text-[var(--muted)] focus-visible:border-bronze focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-bronze/40";
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div ref={wrapRef} className="relative">
|
||||||
|
<input
|
||||||
|
className={`${base} ${className ?? ""}`}
|
||||||
|
value={query}
|
||||||
|
placeholder={placeholder}
|
||||||
|
onFocus={() => setOpen(true)}
|
||||||
|
onChange={(e) => {
|
||||||
|
setQuery(e.target.value);
|
||||||
|
setOpen(true);
|
||||||
|
if (value) onChange(""); // typing invalidates the prior pick
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{open && matches.length > 0 && (
|
||||||
|
<ul className="absolute z-30 mt-1 max-h-64 w-72 overflow-auto rounded-lg border border-[var(--border)] bg-[var(--surface)] shadow-lg">
|
||||||
|
{matches.map((p) => (
|
||||||
|
<li key={p.id}>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => {
|
||||||
|
onChange(p.id);
|
||||||
|
setQuery(p.primary_name ?? "Unnamed");
|
||||||
|
setOpen(false);
|
||||||
|
}}
|
||||||
|
className={`block w-full px-3 py-2 text-left text-sm hover:bg-[var(--muted-bg,rgba(0,0,0,0.04))] ${
|
||||||
|
p.id === value ? "text-bronze" : ""
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{p.primary_name ?? "Unnamed"}
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
Vendored
+241
-5
@@ -190,7 +190,8 @@ export interface paths {
|
|||||||
delete: operations["delete_tree_api_v1_trees__tree_id__delete"];
|
delete: operations["delete_tree_api_v1_trees__tree_id__delete"];
|
||||||
options?: never;
|
options?: never;
|
||||||
head?: never;
|
head?: never;
|
||||||
patch?: never;
|
/** Update Tree */
|
||||||
|
patch: operations["update_tree_api_v1_trees__tree_id__patch"];
|
||||||
trace?: never;
|
trace?: never;
|
||||||
};
|
};
|
||||||
"/api/v1/trees/{tree_id}/restore": {
|
"/api/v1/trees/{tree_id}/restore": {
|
||||||
@@ -366,7 +367,8 @@ export interface paths {
|
|||||||
delete: operations["delete_relationship_api_v1_trees__tree_id__relationships__relationship_id__delete"];
|
delete: operations["delete_relationship_api_v1_trees__tree_id__relationships__relationship_id__delete"];
|
||||||
options?: never;
|
options?: never;
|
||||||
head?: never;
|
head?: never;
|
||||||
patch?: never;
|
/** Update Relationship */
|
||||||
|
patch: operations["update_relationship_api_v1_trees__tree_id__relationships__relationship_id__patch"];
|
||||||
trace?: never;
|
trace?: never;
|
||||||
};
|
};
|
||||||
"/api/v1/trees/{tree_id}/sources": {
|
"/api/v1/trees/{tree_id}/sources": {
|
||||||
@@ -402,7 +404,8 @@ export interface paths {
|
|||||||
delete: operations["delete_source_api_v1_trees__tree_id__sources__source_id__delete"];
|
delete: operations["delete_source_api_v1_trees__tree_id__sources__source_id__delete"];
|
||||||
options?: never;
|
options?: never;
|
||||||
head?: never;
|
head?: never;
|
||||||
patch?: never;
|
/** Update Source */
|
||||||
|
patch: operations["update_source_api_v1_trees__tree_id__sources__source_id__patch"];
|
||||||
trace?: never;
|
trace?: never;
|
||||||
};
|
};
|
||||||
"/api/v1/trees/{tree_id}/citations": {
|
"/api/v1/trees/{tree_id}/citations": {
|
||||||
@@ -437,7 +440,8 @@ export interface paths {
|
|||||||
delete: operations["delete_citation_api_v1_trees__tree_id__citations__citation_id__delete"];
|
delete: operations["delete_citation_api_v1_trees__tree_id__citations__citation_id__delete"];
|
||||||
options?: never;
|
options?: never;
|
||||||
head?: never;
|
head?: never;
|
||||||
patch?: never;
|
/** Update Citation */
|
||||||
|
patch: operations["update_citation_api_v1_trees__tree_id__citations__citation_id__patch"];
|
||||||
trace?: never;
|
trace?: never;
|
||||||
};
|
};
|
||||||
"/api/v1/trees/{tree_id}/media": {
|
"/api/v1/trees/{tree_id}/media": {
|
||||||
@@ -489,7 +493,8 @@ export interface paths {
|
|||||||
delete: operations["delete_media_api_v1_trees__tree_id__media__media_id__delete"];
|
delete: operations["delete_media_api_v1_trees__tree_id__media__media_id__delete"];
|
||||||
options?: never;
|
options?: never;
|
||||||
head?: never;
|
head?: never;
|
||||||
patch?: never;
|
/** Update Media */
|
||||||
|
patch: operations["update_media_api_v1_trees__tree_id__media__media_id__patch"];
|
||||||
trace?: never;
|
trace?: never;
|
||||||
};
|
};
|
||||||
"/api/v1/trees/{tree_id}/gedcom/import": {
|
"/api/v1/trees/{tree_id}/gedcom/import": {
|
||||||
@@ -610,6 +615,14 @@ export interface components {
|
|||||||
*/
|
*/
|
||||||
created_at: string;
|
created_at: string;
|
||||||
};
|
};
|
||||||
|
/** CitationUpdate */
|
||||||
|
CitationUpdate: {
|
||||||
|
/** Page */
|
||||||
|
page?: string | null;
|
||||||
|
/** Detail */
|
||||||
|
detail?: string | null;
|
||||||
|
confidence?: components["schemas"]["CitationConfidence"] | null;
|
||||||
|
};
|
||||||
/** EventCreate */
|
/** EventCreate */
|
||||||
EventCreate: {
|
EventCreate: {
|
||||||
/** Event Type */
|
/** Event Type */
|
||||||
@@ -756,6 +769,17 @@ export interface components {
|
|||||||
/** Url */
|
/** Url */
|
||||||
url?: string | null;
|
url?: string | null;
|
||||||
};
|
};
|
||||||
|
/** MediaUpdate */
|
||||||
|
MediaUpdate: {
|
||||||
|
/** Title */
|
||||||
|
title?: string | null;
|
||||||
|
/** Person Id */
|
||||||
|
person_id?: string | null;
|
||||||
|
/** Event Id */
|
||||||
|
event_id?: string | null;
|
||||||
|
/** Source Id */
|
||||||
|
source_id?: string | null;
|
||||||
|
};
|
||||||
/**
|
/**
|
||||||
* ParentChildQualifier
|
* ParentChildQualifier
|
||||||
* @description Qualifies a parent_child edge so adoption/donor/blended families are
|
* @description Qualifies a parent_child edge so adoption/donor/blended families are
|
||||||
@@ -898,6 +922,12 @@ export interface components {
|
|||||||
* @enum {string}
|
* @enum {string}
|
||||||
*/
|
*/
|
||||||
RelationshipType: "parent_child" | "partnership" | "sibling";
|
RelationshipType: "parent_child" | "partnership" | "sibling";
|
||||||
|
/** RelationshipUpdate */
|
||||||
|
RelationshipUpdate: {
|
||||||
|
qualifier?: components["schemas"]["ParentChildQualifier"] | null;
|
||||||
|
/** Notes */
|
||||||
|
notes?: string | null;
|
||||||
|
};
|
||||||
/** SessionRead */
|
/** SessionRead */
|
||||||
SessionRead: {
|
SessionRead: {
|
||||||
user: components["schemas"]["UserRead"];
|
user: components["schemas"]["UserRead"];
|
||||||
@@ -962,6 +992,25 @@ export interface components {
|
|||||||
*/
|
*/
|
||||||
created_at: string;
|
created_at: string;
|
||||||
};
|
};
|
||||||
|
/** SourceUpdate */
|
||||||
|
SourceUpdate: {
|
||||||
|
/** Title */
|
||||||
|
title?: string | null;
|
||||||
|
/** Author */
|
||||||
|
author?: string | null;
|
||||||
|
/** Source Type */
|
||||||
|
source_type?: string | null;
|
||||||
|
/** Repository */
|
||||||
|
repository?: string | null;
|
||||||
|
/** Url */
|
||||||
|
url?: string | null;
|
||||||
|
/** Citation Text */
|
||||||
|
citation_text?: string | null;
|
||||||
|
/** Publication Info */
|
||||||
|
publication_info?: string | null;
|
||||||
|
/** Quality Note */
|
||||||
|
quality_note?: string | null;
|
||||||
|
};
|
||||||
/** TokenRequest */
|
/** TokenRequest */
|
||||||
TokenRequest: {
|
TokenRequest: {
|
||||||
/** Token */
|
/** Token */
|
||||||
@@ -999,6 +1048,14 @@ export interface components {
|
|||||||
*/
|
*/
|
||||||
created_at: string;
|
created_at: string;
|
||||||
};
|
};
|
||||||
|
/** TreeUpdate */
|
||||||
|
TreeUpdate: {
|
||||||
|
/** Name */
|
||||||
|
name?: string | null;
|
||||||
|
/** Description */
|
||||||
|
description?: string | null;
|
||||||
|
visibility?: components["schemas"]["TreeVisibility"] | null;
|
||||||
|
};
|
||||||
/**
|
/**
|
||||||
* TreeVisibility
|
* TreeVisibility
|
||||||
* @enum {string}
|
* @enum {string}
|
||||||
@@ -1414,6 +1471,41 @@ export interface operations {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
update_tree_api_v1_trees__tree_id__patch: {
|
||||||
|
parameters: {
|
||||||
|
query?: never;
|
||||||
|
header?: never;
|
||||||
|
path: {
|
||||||
|
tree_id: string;
|
||||||
|
};
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
requestBody: {
|
||||||
|
content: {
|
||||||
|
"application/json": components["schemas"]["TreeUpdate"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
responses: {
|
||||||
|
/** @description Successful Response */
|
||||||
|
200: {
|
||||||
|
headers: {
|
||||||
|
[name: string]: unknown;
|
||||||
|
};
|
||||||
|
content: {
|
||||||
|
"application/json": components["schemas"]["TreeRead"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description Validation Error */
|
||||||
|
422: {
|
||||||
|
headers: {
|
||||||
|
[name: string]: unknown;
|
||||||
|
};
|
||||||
|
content: {
|
||||||
|
"application/json": components["schemas"]["HTTPValidationError"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
restore_tree_api_v1_trees__tree_id__restore_post: {
|
restore_tree_api_v1_trees__tree_id__restore_post: {
|
||||||
parameters: {
|
parameters: {
|
||||||
query?: never;
|
query?: never;
|
||||||
@@ -1936,6 +2028,42 @@ export interface operations {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
update_relationship_api_v1_trees__tree_id__relationships__relationship_id__patch: {
|
||||||
|
parameters: {
|
||||||
|
query?: never;
|
||||||
|
header?: never;
|
||||||
|
path: {
|
||||||
|
tree_id: string;
|
||||||
|
relationship_id: string;
|
||||||
|
};
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
requestBody: {
|
||||||
|
content: {
|
||||||
|
"application/json": components["schemas"]["RelationshipUpdate"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
responses: {
|
||||||
|
/** @description Successful Response */
|
||||||
|
200: {
|
||||||
|
headers: {
|
||||||
|
[name: string]: unknown;
|
||||||
|
};
|
||||||
|
content: {
|
||||||
|
"application/json": components["schemas"]["RelationshipRead"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description Validation Error */
|
||||||
|
422: {
|
||||||
|
headers: {
|
||||||
|
[name: string]: unknown;
|
||||||
|
};
|
||||||
|
content: {
|
||||||
|
"application/json": components["schemas"]["HTTPValidationError"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
list_sources_api_v1_trees__tree_id__sources_get: {
|
list_sources_api_v1_trees__tree_id__sources_get: {
|
||||||
parameters: {
|
parameters: {
|
||||||
query?: never;
|
query?: never;
|
||||||
@@ -2064,6 +2192,42 @@ export interface operations {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
update_source_api_v1_trees__tree_id__sources__source_id__patch: {
|
||||||
|
parameters: {
|
||||||
|
query?: never;
|
||||||
|
header?: never;
|
||||||
|
path: {
|
||||||
|
tree_id: string;
|
||||||
|
source_id: string;
|
||||||
|
};
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
requestBody: {
|
||||||
|
content: {
|
||||||
|
"application/json": components["schemas"]["SourceUpdate"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
responses: {
|
||||||
|
/** @description Successful Response */
|
||||||
|
200: {
|
||||||
|
headers: {
|
||||||
|
[name: string]: unknown;
|
||||||
|
};
|
||||||
|
content: {
|
||||||
|
"application/json": components["schemas"]["SourceRead"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description Validation Error */
|
||||||
|
422: {
|
||||||
|
headers: {
|
||||||
|
[name: string]: unknown;
|
||||||
|
};
|
||||||
|
content: {
|
||||||
|
"application/json": components["schemas"]["HTTPValidationError"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
list_citations_api_v1_trees__tree_id__citations_get: {
|
list_citations_api_v1_trees__tree_id__citations_get: {
|
||||||
parameters: {
|
parameters: {
|
||||||
query?: never;
|
query?: never;
|
||||||
@@ -2160,6 +2324,42 @@ export interface operations {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
update_citation_api_v1_trees__tree_id__citations__citation_id__patch: {
|
||||||
|
parameters: {
|
||||||
|
query?: never;
|
||||||
|
header?: never;
|
||||||
|
path: {
|
||||||
|
tree_id: string;
|
||||||
|
citation_id: string;
|
||||||
|
};
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
requestBody: {
|
||||||
|
content: {
|
||||||
|
"application/json": components["schemas"]["CitationUpdate"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
responses: {
|
||||||
|
/** @description Successful Response */
|
||||||
|
200: {
|
||||||
|
headers: {
|
||||||
|
[name: string]: unknown;
|
||||||
|
};
|
||||||
|
content: {
|
||||||
|
"application/json": components["schemas"]["CitationRead"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description Validation Error */
|
||||||
|
422: {
|
||||||
|
headers: {
|
||||||
|
[name: string]: unknown;
|
||||||
|
};
|
||||||
|
content: {
|
||||||
|
"application/json": components["schemas"]["HTTPValidationError"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
list_media_api_v1_trees__tree_id__media_get: {
|
list_media_api_v1_trees__tree_id__media_get: {
|
||||||
parameters: {
|
parameters: {
|
||||||
query?: never;
|
query?: never;
|
||||||
@@ -2288,6 +2488,42 @@ export interface operations {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
update_media_api_v1_trees__tree_id__media__media_id__patch: {
|
||||||
|
parameters: {
|
||||||
|
query?: never;
|
||||||
|
header?: never;
|
||||||
|
path: {
|
||||||
|
tree_id: string;
|
||||||
|
media_id: string;
|
||||||
|
};
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
requestBody: {
|
||||||
|
content: {
|
||||||
|
"application/json": components["schemas"]["MediaUpdate"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
responses: {
|
||||||
|
/** @description Successful Response */
|
||||||
|
200: {
|
||||||
|
headers: {
|
||||||
|
[name: string]: unknown;
|
||||||
|
};
|
||||||
|
content: {
|
||||||
|
"application/json": components["schemas"]["MediaRead"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description Validation Error */
|
||||||
|
422: {
|
||||||
|
headers: {
|
||||||
|
[name: string]: unknown;
|
||||||
|
};
|
||||||
|
content: {
|
||||||
|
"application/json": components["schemas"]["HTTPValidationError"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
import_gedcom_api_v1_trees__tree_id__gedcom_import_post: {
|
import_gedcom_api_v1_trees__tree_id__gedcom_import_post: {
|
||||||
parameters: {
|
parameters: {
|
||||||
query?: never;
|
query?: never;
|
||||||
|
|||||||
@@ -408,6 +408,57 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"patch": {
|
||||||
|
"tags": [
|
||||||
|
"trees"
|
||||||
|
],
|
||||||
|
"summary": "Update Tree",
|
||||||
|
"operationId": "update_tree_api_v1_trees__tree_id__patch",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "tree_id",
|
||||||
|
"in": "path",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "uuid",
|
||||||
|
"title": "Tree Id"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"requestBody": {
|
||||||
|
"required": true,
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/TreeUpdate"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Successful Response",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/TreeRead"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"422": {
|
||||||
|
"description": "Validation Error",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/HTTPValidationError"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"delete": {
|
"delete": {
|
||||||
"tags": [
|
"tags": [
|
||||||
"trees"
|
"trees"
|
||||||
@@ -1239,6 +1290,67 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/api/v1/trees/{tree_id}/relationships/{relationship_id}": {
|
"/api/v1/trees/{tree_id}/relationships/{relationship_id}": {
|
||||||
|
"patch": {
|
||||||
|
"tags": [
|
||||||
|
"relationships"
|
||||||
|
],
|
||||||
|
"summary": "Update Relationship",
|
||||||
|
"operationId": "update_relationship_api_v1_trees__tree_id__relationships__relationship_id__patch",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "tree_id",
|
||||||
|
"in": "path",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "uuid",
|
||||||
|
"title": "Tree Id"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "relationship_id",
|
||||||
|
"in": "path",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "uuid",
|
||||||
|
"title": "Relationship Id"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"requestBody": {
|
||||||
|
"required": true,
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/RelationshipUpdate"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Successful Response",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/RelationshipRead"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"422": {
|
||||||
|
"description": "Validation Error",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/HTTPValidationError"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"delete": {
|
"delete": {
|
||||||
"tags": [
|
"tags": [
|
||||||
"relationships"
|
"relationships"
|
||||||
@@ -1434,6 +1546,67 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"patch": {
|
||||||
|
"tags": [
|
||||||
|
"sources"
|
||||||
|
],
|
||||||
|
"summary": "Update Source",
|
||||||
|
"operationId": "update_source_api_v1_trees__tree_id__sources__source_id__patch",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "tree_id",
|
||||||
|
"in": "path",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "uuid",
|
||||||
|
"title": "Tree Id"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "source_id",
|
||||||
|
"in": "path",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "uuid",
|
||||||
|
"title": "Source Id"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"requestBody": {
|
||||||
|
"required": true,
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/SourceUpdate"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Successful Response",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/SourceRead"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"422": {
|
||||||
|
"description": "Validation Error",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/HTTPValidationError"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"delete": {
|
"delete": {
|
||||||
"tags": [
|
"tags": [
|
||||||
"sources"
|
"sources"
|
||||||
@@ -1578,6 +1751,67 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/api/v1/trees/{tree_id}/citations/{citation_id}": {
|
"/api/v1/trees/{tree_id}/citations/{citation_id}": {
|
||||||
|
"patch": {
|
||||||
|
"tags": [
|
||||||
|
"citations"
|
||||||
|
],
|
||||||
|
"summary": "Update Citation",
|
||||||
|
"operationId": "update_citation_api_v1_trees__tree_id__citations__citation_id__patch",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "tree_id",
|
||||||
|
"in": "path",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "uuid",
|
||||||
|
"title": "Tree Id"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "citation_id",
|
||||||
|
"in": "path",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "uuid",
|
||||||
|
"title": "Citation Id"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"requestBody": {
|
||||||
|
"required": true,
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/CitationUpdate"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Successful Response",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/CitationRead"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"422": {
|
||||||
|
"description": "Validation Error",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/HTTPValidationError"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"delete": {
|
"delete": {
|
||||||
"tags": [
|
"tags": [
|
||||||
"citations"
|
"citations"
|
||||||
@@ -1773,6 +2007,67 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/api/v1/trees/{tree_id}/media/{media_id}": {
|
"/api/v1/trees/{tree_id}/media/{media_id}": {
|
||||||
|
"patch": {
|
||||||
|
"tags": [
|
||||||
|
"media"
|
||||||
|
],
|
||||||
|
"summary": "Update Media",
|
||||||
|
"operationId": "update_media_api_v1_trees__tree_id__media__media_id__patch",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "tree_id",
|
||||||
|
"in": "path",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "uuid",
|
||||||
|
"title": "Tree Id"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "media_id",
|
||||||
|
"in": "path",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "uuid",
|
||||||
|
"title": "Media Id"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"requestBody": {
|
||||||
|
"required": true,
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/MediaUpdate"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Successful Response",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/MediaRead"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"422": {
|
||||||
|
"description": "Validation Error",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/HTTPValidationError"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"delete": {
|
"delete": {
|
||||||
"tags": [
|
"tags": [
|
||||||
"media"
|
"media"
|
||||||
@@ -2212,6 +2507,44 @@
|
|||||||
],
|
],
|
||||||
"title": "CitationRead"
|
"title": "CitationRead"
|
||||||
},
|
},
|
||||||
|
"CitationUpdate": {
|
||||||
|
"properties": {
|
||||||
|
"page": {
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"title": "Page"
|
||||||
|
},
|
||||||
|
"detail": {
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"title": "Detail"
|
||||||
|
},
|
||||||
|
"confidence": {
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/CitationConfidence"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type": "object",
|
||||||
|
"title": "CitationUpdate"
|
||||||
|
},
|
||||||
"EventCreate": {
|
"EventCreate": {
|
||||||
"properties": {
|
"properties": {
|
||||||
"event_type": {
|
"event_type": {
|
||||||
@@ -2754,6 +3087,59 @@
|
|||||||
],
|
],
|
||||||
"title": "MediaRead"
|
"title": "MediaRead"
|
||||||
},
|
},
|
||||||
|
"MediaUpdate": {
|
||||||
|
"properties": {
|
||||||
|
"title": {
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"title": "Title"
|
||||||
|
},
|
||||||
|
"person_id": {
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"format": "uuid"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"title": "Person Id"
|
||||||
|
},
|
||||||
|
"event_id": {
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"format": "uuid"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"title": "Event Id"
|
||||||
|
},
|
||||||
|
"source_id": {
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"format": "uuid"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"title": "Source Id"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type": "object",
|
||||||
|
"title": "MediaUpdate"
|
||||||
|
},
|
||||||
"ParentChildQualifier": {
|
"ParentChildQualifier": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
@@ -3159,6 +3545,33 @@
|
|||||||
],
|
],
|
||||||
"title": "RelationshipType"
|
"title": "RelationshipType"
|
||||||
},
|
},
|
||||||
|
"RelationshipUpdate": {
|
||||||
|
"properties": {
|
||||||
|
"qualifier": {
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/ParentChildQualifier"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"notes": {
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"title": "Notes"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type": "object",
|
||||||
|
"title": "RelationshipUpdate"
|
||||||
|
},
|
||||||
"SessionRead": {
|
"SessionRead": {
|
||||||
"properties": {
|
"properties": {
|
||||||
"user": {
|
"user": {
|
||||||
@@ -3387,6 +3800,100 @@
|
|||||||
],
|
],
|
||||||
"title": "SourceRead"
|
"title": "SourceRead"
|
||||||
},
|
},
|
||||||
|
"SourceUpdate": {
|
||||||
|
"properties": {
|
||||||
|
"title": {
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"title": "Title"
|
||||||
|
},
|
||||||
|
"author": {
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"title": "Author"
|
||||||
|
},
|
||||||
|
"source_type": {
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"title": "Source Type"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"title": "Repository"
|
||||||
|
},
|
||||||
|
"url": {
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"title": "Url"
|
||||||
|
},
|
||||||
|
"citation_text": {
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"title": "Citation Text"
|
||||||
|
},
|
||||||
|
"publication_info": {
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"title": "Publication Info"
|
||||||
|
},
|
||||||
|
"quality_note": {
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"title": "Quality Note"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type": "object",
|
||||||
|
"title": "SourceUpdate"
|
||||||
|
},
|
||||||
"TokenRequest": {
|
"TokenRequest": {
|
||||||
"properties": {
|
"properties": {
|
||||||
"token": {
|
"token": {
|
||||||
@@ -3475,6 +3982,44 @@
|
|||||||
],
|
],
|
||||||
"title": "TreeRead"
|
"title": "TreeRead"
|
||||||
},
|
},
|
||||||
|
"TreeUpdate": {
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"title": "Name"
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"title": "Description"
|
||||||
|
},
|
||||||
|
"visibility": {
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/TreeVisibility"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type": "object",
|
||||||
|
"title": "TreeUpdate"
|
||||||
|
},
|
||||||
"TreeVisibility": {
|
"TreeVisibility": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
|
|||||||
Reference in New Issue
Block a user