tmp working changes from laptop
This commit is contained in:
parent
2bd75328a1
commit
2fd92ca0b8
|
@ -0,0 +1,131 @@
|
||||||
|
"""
|
||||||
|
Submodule for citation logic.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import enum
|
||||||
|
from typing import Sequence, Set, List, Dict, Union
|
||||||
|
|
||||||
|
from amanuensis.backend import *
|
||||||
|
from amanuensis.db import Lexicon, Article
|
||||||
|
from amanuensis.parser import *
|
||||||
|
|
||||||
|
|
||||||
|
class CitationNodeType(enum.Enum):
|
||||||
|
ExtantWritten = 0
|
||||||
|
"""Title of an article that was previously written."""
|
||||||
|
|
||||||
|
ExtantPhantom = 1
|
||||||
|
"""Unwritten title cited by an extant written article."""
|
||||||
|
|
||||||
|
NewDraft = 2
|
||||||
|
"""Title of a pending article with a new title."""
|
||||||
|
|
||||||
|
PhantomDraft = 3
|
||||||
|
"""Title of a pending article with a phantom title."""
|
||||||
|
|
||||||
|
PhantomPending = 4
|
||||||
|
"""Unwritten title cited by a draft article."""
|
||||||
|
|
||||||
|
|
||||||
|
class CitationNode:
|
||||||
|
"""
|
||||||
|
Represents an article in the context of citations. Phantom articles do not
|
||||||
|
correspond to articles in the database.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, title: str, node_type: CitationNodeType) -> None:
|
||||||
|
self.title: str = title
|
||||||
|
self.cites: Set[CitationNode] = set()
|
||||||
|
self.cited_by: Set[CitationNode] = set()
|
||||||
|
self.node_type: CitationNodeType = node_type
|
||||||
|
|
||||||
|
|
||||||
|
class CitationMap:
|
||||||
|
"""Represents metadata about which articles cite each other."""
|
||||||
|
|
||||||
|
def __init__(self) -> None:
|
||||||
|
self.by_title: Dict[str, CitationNode] = {}
|
||||||
|
|
||||||
|
def __contains__(self, title: str) -> bool:
|
||||||
|
return title in self.by_title
|
||||||
|
|
||||||
|
def __getitem__(self, title: str) -> CitationNode:
|
||||||
|
return self.by_title[title]
|
||||||
|
|
||||||
|
def get_or_add(self, title: str, node_type: CitationNodeType) -> CitationNode:
|
||||||
|
"""
|
||||||
|
Get the citation node for a title. If one does not exist, create it
|
||||||
|
with the given type.
|
||||||
|
"""
|
||||||
|
if title not in self.by_title:
|
||||||
|
self.add(title, node_type)
|
||||||
|
return self.by_title[title]
|
||||||
|
|
||||||
|
def add(self, title: str, node_type: CitationNodeType) -> None:
|
||||||
|
"""
|
||||||
|
Create a citation node with the given title and type.
|
||||||
|
"""
|
||||||
|
self.by_title[title] = CitationNode(title, node_type)
|
||||||
|
|
||||||
|
def create_citation(
|
||||||
|
self, citer: Union[CitationNode, str], cited: Union[CitationNode, str]
|
||||||
|
) -> None:
|
||||||
|
"""Add a citation between two titles."""
|
||||||
|
if isinstance(citer, str):
|
||||||
|
citer = self.by_title[citer]
|
||||||
|
if isinstance(cited, str):
|
||||||
|
cited = self.by_title[cited]
|
||||||
|
citer.cites.add(cited)
|
||||||
|
cited.cited_by.add(citer)
|
||||||
|
|
||||||
|
|
||||||
|
class GetCitations(RenderableVisitor):
|
||||||
|
"""Returns a list of all article titles cited in this article."""
|
||||||
|
def __init__(self) -> None:
|
||||||
|
self.citations: List[str] = []
|
||||||
|
|
||||||
|
def CitationSpan(self, span):
|
||||||
|
self.citations.append(span.cite_target)
|
||||||
|
|
||||||
|
def ParsedArticle(self, span):
|
||||||
|
return self.citations
|
||||||
|
|
||||||
|
|
||||||
|
def create_citation_map(lexicon: Lexicon, articles: Sequence[Article]) -> CitationMap:
|
||||||
|
"""
|
||||||
|
Generates mappings useful for tracking citations.
|
||||||
|
"""
|
||||||
|
article: Article
|
||||||
|
citemap = CitationMap()
|
||||||
|
|
||||||
|
# Add extant articles to the citation map
|
||||||
|
for article in articles:
|
||||||
|
if article.turn < lexicon.current_turn:
|
||||||
|
citemap.add(article.title, CitationNodeType.ExtantWritten)
|
||||||
|
|
||||||
|
# Add phantoms created by extant articles
|
||||||
|
for article in articles:
|
||||||
|
if article.turn < lexicon.current_turn:
|
||||||
|
parsed = parse_raw_markdown(article.body)
|
||||||
|
citeds = parsed.render(GetCitations())
|
||||||
|
for cited in citeds:
|
||||||
|
cited_node = citemap.get_or_add(cited, CitationNodeType.ExtantPhantom)
|
||||||
|
citemap.create_citation(article.title, cited_node)
|
||||||
|
|
||||||
|
# Add drafts, noting new and phantom drafts
|
||||||
|
for article in articles:
|
||||||
|
if article.turn >= lexicon.current_turn:
|
||||||
|
draft_node = citemap.get_or_add(article.title, CitationNodeType.NewDraft)
|
||||||
|
if draft_node.node_type == CitationNodeType.ExtantPhantom:
|
||||||
|
draft_node.node_type = CitationNodeType.PhantomDraft
|
||||||
|
|
||||||
|
# Add phantoms created by drafts
|
||||||
|
for article in articles:
|
||||||
|
if article.turn >= lexicon.current_turn:
|
||||||
|
parsed = parse_raw_markdown(article.body)
|
||||||
|
citeds = parsed.render(GetCitations())
|
||||||
|
for cited in citeds:
|
||||||
|
cited_node = citemap.get_or_add(cited, CitationNodeType.PhantomPending)
|
||||||
|
citemap.create_citation(article.title, cited_node)
|
||||||
|
|
||||||
|
return citemap
|
|
@ -4,15 +4,18 @@ from typing import Sequence
|
||||||
from amanuensis.db import *
|
from amanuensis.db import *
|
||||||
from amanuensis.parser import *
|
from amanuensis.parser import *
|
||||||
|
|
||||||
|
from .citation import CitationMap, CitationNodeType, create_citation_map
|
||||||
|
|
||||||
|
|
||||||
class ConstraintCheck(RenderableVisitor):
|
class ConstraintCheck(RenderableVisitor):
|
||||||
"""Analyzes an article for content-based constraint violations."""
|
"""Analyzes an article for content-based constraint violations."""
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self.word_count: int = 0
|
self.word_count: int = 0
|
||||||
self.signatures: int = 0
|
self.signatures: int = 0
|
||||||
|
|
||||||
def TextSpan(self, span):
|
def TextSpan(self, span):
|
||||||
self.word_count += len(re.split(r'\s+', span.innertext.strip()))
|
self.word_count += len(re.split(r"\s+", span.innertext.strip()))
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def SignatureParagraph(self, span):
|
def SignatureParagraph(self, span):
|
||||||
|
@ -50,24 +53,44 @@ class ConstraintMessage:
|
||||||
return {"severity": self.severity, "message": self.message}
|
return {"severity": self.severity, "message": self.message}
|
||||||
|
|
||||||
|
|
||||||
def title_constraint_check(title: str) -> Sequence[ConstraintMessage]:
|
def constraint_check(
|
||||||
"""Perform checks that apply to the article title."""
|
article: Article,
|
||||||
|
parsed: Renderable,
|
||||||
|
citemap: CitationMap,
|
||||||
|
) -> Sequence[ConstraintMessage]:
|
||||||
|
""""""
|
||||||
messages = []
|
messages = []
|
||||||
|
# TODO: Need
|
||||||
|
# player index assignments for article turn
|
||||||
|
# extant article titles
|
||||||
|
# pending article titles
|
||||||
|
# phantom article titles
|
||||||
|
# pending phantom article titles
|
||||||
|
# index capacities
|
||||||
|
title = article.title
|
||||||
|
# author_id = article.character_id
|
||||||
|
|
||||||
|
###
|
||||||
|
# Constraints that apply to the title
|
||||||
|
###
|
||||||
|
|
||||||
# I: Current index assignments
|
# I: Current index assignments
|
||||||
# TODO
|
# TODO
|
||||||
|
|
||||||
# E: No title
|
# E: No title
|
||||||
if not title:
|
if not article.title:
|
||||||
messages.append(ConstraintMessage.error("Missing title"))
|
messages.append(ConstraintMessage.error("Missing title"))
|
||||||
|
|
||||||
|
if citemap[title].node_type == CitationNodeType.NewDraft:
|
||||||
# I: This article is new
|
# I: This article is new
|
||||||
# TODO
|
messages.append(ConstraintMessage.info("Writing a new article"))
|
||||||
# E: And new articles are forbidden
|
|
||||||
|
# E: New articles are forbidden
|
||||||
# TODO
|
# TODO
|
||||||
|
|
||||||
|
if citemap[title].node_type == CitationNodeType.PhantomDraft:
|
||||||
# I: This article is a phantom
|
# I: This article is a phantom
|
||||||
# TODO
|
messages.append(ConstraintMessage.info("Writing a phantom article")
|
||||||
|
|
||||||
# I: This article is an addendum
|
# I: This article is an addendum
|
||||||
# TODO
|
# TODO
|
||||||
|
@ -98,14 +121,8 @@ def title_constraint_check(title: str) -> Sequence[ConstraintMessage]:
|
||||||
# W: The article's title matches a character's name
|
# W: The article's title matches a character's name
|
||||||
# TODO
|
# TODO
|
||||||
|
|
||||||
return messages
|
|
||||||
|
|
||||||
|
|
||||||
def content_constraint_check(parsed: Renderable) -> Sequence[ConstraintMessage]:
|
|
||||||
check_result: ConstraintCheck = parsed.render(ConstraintCheck())
|
check_result: ConstraintCheck = parsed.render(ConstraintCheck())
|
||||||
|
|
||||||
messages = []
|
|
||||||
|
|
||||||
# I: Word count
|
# I: Word count
|
||||||
messages.append(ConstraintMessage.info(f"Word count: {check_result.word_count}"))
|
messages.append(ConstraintMessage.info(f"Word count: {check_result.word_count}"))
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue