{"".join(span.recurse(self))}
' + + def SignatureParagraph(self, span): + return ( + '' + f'{"".join(span.recurse(self))}' + '
' + ) + + # Return the visitor from the top-level article span after saving the full + # text parsed from the child spans + def ParsedArticle(self, span): + self.contents = '\n'.join(span.recurse(self)) + return self + + +class ConstraintCheck(RenderableVisitor): + """Analyzes an article for content-based constraint violations.""" + def __init__(self) -> None: + self.word_count: int = 0 + self.signatures: int = 0 + self.tmp: bool = False + + def TextSpan(self, span): + self.word_count += len(re.split(r'\s+', span.innertext.strip())) + return self + + def BoldSpan(self, span): + self.tmp = True + return self + + def SignatureParagraph(self, span): + self.signatures += 1 + return self + + +def constraint_check(parsed: Renderable): + check_result: ConstraintCheck = parsed.render(ConstraintCheck()) + errors = [] + errors.append({ + "severity": 0, + "message": f"Word count: {check_result.word_count}" + }) + if check_result.signatures < 1: + errors.append({ + "severity": 1, + "message": "Missing signature paragraph" + }) + if check_result.signatures > 1: + errors.append({ + "severity": 1, + "message": "More than one signature paragraph" + }) + if check_result.tmp: + errors.append({ + "severity": 2, + "message": "Temporary error" + }) + return errors + + +@bp.get("/") +@lexicon_param +@player_required +def select(lexicon_name): + return {} + + @bp.get("/{"".join(span.recurse(self))}
' +# def BodyParagraph(self, span): +# return f'{"".join(span.recurse(self))}
' - def SignatureParagraph(self, span): - return ( - '' - f'{"".join(span.recurse(self))}' - '
' - ) +# def SignatureParagraph(self, span): +# return ( +# '' +# f'{"".join(span.recurse(self))}' +# '
' +# ) - def BoldSpan(self, span): - return f'{"".join(span.recurse(self))}' +# def BoldSpan(self, span): +# return f'{"".join(span.recurse(self))}' - def ItalicSpan(self, span): - return f'{"".join(span.recurse(self))}' +# def ItalicSpan(self, span): +# return f'{"".join(span.recurse(self))}' - def CitationSpan(self, span): - if span.cite_target in self.article_map: - if self.article_map.get(span.cite_target): - link_class = '[extant]' - else: - link_class = '[phantom]' - else: - link_class = '[new]' - self.citations.append(f'{span.cite_target} {link_class}') - return f'{"".join(span.recurse(self))}[{len(self.citations)}]' +# def CitationSpan(self, span): +# if span.cite_target in self.article_map: +# if self.article_map.get(span.cite_target): +# link_class = '[extant]' +# else: +# link_class = '[phantom]' +# else: +# link_class = '[new]' +# self.citations.append(f'{span.cite_target} {link_class}') +# return f'{"".join(span.recurse(self))}[{len(self.citations)}]' -def load_editor(lexicon: LexiconModel, aid: str): - """ - Load the editor page - """ - if aid: - # Article specfied, load editor in edit mode - article = get_draft(lexicon, aid) - if not article: - flash("Draft not found") - return redirect(url_for('session.session', name=lexicon.cfg.name)) - # Check that the player owns this article - character = lexicon.cfg.character.get(article.character) - if character.player != current_user.uid: - flash("Access forbidden") - return redirect(url_for('session.session', name=lexicon.cfg.name)) - return render_template( - 'session.editor.jinja', - character=character, - article=article, - jsonfmt=lambda obj: Markup(json.dumps(obj))) +# def update_draft(lexicon: LexiconModel, article_json): +# """ +# Update a draft and perform analysis on it +# """ +# # Check if the update is permitted +# aid = article_json.get('aid') +# article = get_draft(lexicon, aid) +# if not article: +# raise ValueError("missing article") +# if lexicon.cfg.character.get(article.character).player != current_user.uid: +# return ValueError("bad user") +# if article.status.approved: +# raise ValueError("bad status") - # Article not specified, load editor in load mode - characters = list(get_player_characters(lexicon, current_user.uid)) - articles = list(get_player_drafts(lexicon, current_user.uid)) - return render_template( - 'session.editor.jinja', - characters=characters, - articles=articles) +# # Perform the update +# title = article_json.get('title') +# contents = article_json.get('contents') +# status = article_json.get('status') +# parsed = parse_raw_markdown(contents) -def new_draft(lexicon: LexiconModel, cid: str): - """ - Create a new draft and open it in the editor - """ - if cid: - new_aid = uuid.uuid4().hex - # TODO harden this - character = lexicon.cfg.character.get(cid) - article = { - "version": "0", - "aid": new_aid, - "lexicon": lexicon.lid, - "character": cid, - "title": "", - "turn": 1, - "status": { - "ready": False, - "approved": False - }, - "contents": f"\n\n{character.signature}", - } - filename = f"{cid}.{new_aid}" - with lexicon.ctx.draft.new(filename) as j: - j.update(article) - return redirect(url_for( - 'session.editor', - name=lexicon.cfg.name, - cid=cid, - aid=new_aid)) +# # HTML parsing +# preview = parsed.render(PreviewHtmlRenderer(lexicon)) +# # Constraint analysis +# title_infos, title_warnings, title_errors = title_constraint_analysis( +# lexicon, current_user, article.character, title) +# content_infos, content_warnings, content_errors = content_constraint_analysis( +# lexicon, current_user, article.character, parsed) +# if any(title_errors) or any(content_errors): +# status['ready'] = False - # Character not specified - flash('Character not found') - return redirect(url_for('session.session', name=lexicon.cfg.name)) +# # Article update +# filename = f'{article.character}.{aid}' +# with lexicon.ctx.draft.edit(filename) as draft: +# draft.title = normalize_title(title) +# draft.contents = contents +# draft.status.ready = status.get('ready', False) - -def update_draft(lexicon: LexiconModel, article_json): - """ - Update a draft and perform analysis on it - """ - # Check if the update is permitted - aid = article_json.get('aid') - article = get_draft(lexicon, aid) - if not article: - raise ValueError("missing article") - if lexicon.cfg.character.get(article.character).player != current_user.uid: - return ValueError("bad user") - if article.status.approved: - raise ValueError("bad status") - - # Perform the update - title = article_json.get('title') - contents = article_json.get('contents') - status = article_json.get('status') - - parsed = parse_raw_markdown(contents) - - # HTML parsing - preview = parsed.render(PreviewHtmlRenderer(lexicon)) - # Constraint analysis - title_infos, title_warnings, title_errors = title_constraint_analysis( - lexicon, current_user, article.character, title) - content_infos, content_warnings, content_errors = content_constraint_analysis( - lexicon, current_user, article.character, parsed) - if any(title_errors) or any(content_errors): - status['ready'] = False - - # Article update - filename = f'{article.character}.{aid}' - with lexicon.ctx.draft.edit(filename) as draft: - draft.title = normalize_title(title) - draft.contents = contents - draft.status.ready = status.get('ready', False) - - # Return canonical information to editor - return { - 'title': draft.title, - 'status': { - 'ready': draft.status.ready, - 'approved': draft.status.approved, - }, - 'rendered': preview.contents, - 'citations': preview.citations, - 'info': title_infos + content_infos, - 'warning': title_warnings + content_warnings, - 'error': title_errors + content_errors, - } +# # Return canonical information to editor +# return { +# 'title': draft.title, +# 'status': { +# 'ready': draft.status.ready, +# 'approved': draft.status.approved, +# }, +# 'rendered': preview.contents, +# 'citations': preview.citations, +# 'info': title_infos + content_infos, +# 'warning': title_warnings + content_warnings, +# 'error': title_errors + content_errors, +# }