mirror of https://github.com/logos-co/open-law.git
issue Add mention user feature without quick search for comments #105 Change tags syntax from [tag] to #tag #99
This commit is contained in:
parent
6c0d4b7efd
commit
c6dcfe5f93
|
@ -70,13 +70,13 @@ def create_app(environment="development"):
|
|||
# Jinja globals
|
||||
from app.controllers.jinja_globals import (
|
||||
form_hidden_tag,
|
||||
display_tags,
|
||||
display_inline_elements,
|
||||
build_qa_url_using_interpretation,
|
||||
recursive_render,
|
||||
)
|
||||
|
||||
app.jinja_env.globals["form_hidden_tag"] = form_hidden_tag
|
||||
app.jinja_env.globals["display_tags"] = display_tags
|
||||
app.jinja_env.globals["display_inline_elements"] = display_inline_elements
|
||||
app.jinja_env.globals["build_qa_url"] = build_qa_url_using_interpretation
|
||||
app.jinja_env.globals["recursive_render"] = recursive_render
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ import re
|
|||
from flask import current_app
|
||||
from flask_wtf import FlaskForm
|
||||
from flask import url_for, render_template
|
||||
from sqlalchemy import func
|
||||
|
||||
from app import models as m
|
||||
|
||||
|
@ -15,21 +16,45 @@ def form_hidden_tag():
|
|||
return form.hidden_tag()
|
||||
|
||||
|
||||
# Using: {{ display_tags("Some text with [tags] here") }}
|
||||
def display_tags(text: str):
|
||||
tags = current_app.config["TAG_REGEX"].findall(text)
|
||||
# Using: {{ display_inline_elements("Some text with [tags] here") }}
|
||||
def display_inline_elements(text: str):
|
||||
users_mentions = current_app.config["USER_MENTION_REGEX"].findall(text)
|
||||
classes = [
|
||||
"!no-underline",
|
||||
"cursor-pointer",
|
||||
"multiple-input-word",
|
||||
"bg-sky-100",
|
||||
"border",
|
||||
"border-sky-300",
|
||||
"dark:!text-black",
|
||||
"rounded",
|
||||
"text-center",
|
||||
"py-1/2",
|
||||
"px-1",
|
||||
]
|
||||
classes = " ".join(classes)
|
||||
for users_mention in users_mentions:
|
||||
username = users_mention.replace("@", "").lower()
|
||||
user: m.User = m.User.query.filter(
|
||||
(func.lower(m.User.username) == username)
|
||||
).first()
|
||||
if user:
|
||||
username_to_display = users_mention.replace(username, user.username)
|
||||
url = url_for("user.profile", user_id=user.id)
|
||||
text = text.replace(
|
||||
users_mention,
|
||||
f"<a href='{url}' class='{classes}'>{username_to_display}</a>",
|
||||
)
|
||||
|
||||
tags = current_app.config["TAG_REGEX"].findall(text)
|
||||
classes = ["text-orange-500", "!no-underline"]
|
||||
classes = " ".join(classes)
|
||||
|
||||
for tag in tags:
|
||||
for tag in set(tags):
|
||||
url = url_for(
|
||||
"search.tag_search_interpretations",
|
||||
tag_name=tag.lower().replace("[", "").replace("]", ""),
|
||||
"search.tag_search_interpretations", tag_name=tag.lower().replace("#", "")
|
||||
)
|
||||
text = text.replace(
|
||||
tag,
|
||||
f"<a href='{url}' class='{classes}'>{tag}</a>",
|
||||
text = re.sub(
|
||||
rf"({tag})\b", f"<a href='{url}' class='{classes}'>{tag}</a>", text
|
||||
)
|
||||
|
||||
return text
|
||||
|
|
|
@ -50,9 +50,9 @@ def set_comment_tags(comment: m.Comment, tags: list[str]):
|
|||
comment_tags = m.CommentTags.query.filter_by(comment_id=comment.id).all()
|
||||
for tag in comment_tags:
|
||||
db.session.delete(tag)
|
||||
tags_names = [tag.lower().replace("[", "").replace("]", "") for tag in tags]
|
||||
tags_names = [tag.lower().replace("#", "") for tag in tags]
|
||||
|
||||
for tag_name in tags_names:
|
||||
for tag_name in set(tags_names):
|
||||
try:
|
||||
tag = get_or_create_tag(tag_name)
|
||||
except ValueError as e:
|
||||
|
@ -77,7 +77,7 @@ def set_interpretation_tags(interpretation: m.InterpretationTag, tags: list[str]
|
|||
).all()
|
||||
for tag in interpretation_tags:
|
||||
db.session.delete(tag)
|
||||
tags_names = [tag.lower().replace("[", "").replace("]", "") for tag in tags]
|
||||
tags_names = [tag.lower().replace("#", "") for tag in tags]
|
||||
|
||||
for tag_name in tags_names:
|
||||
try:
|
||||
|
|
File diff suppressed because one or more lines are too long
144849
app/static/js/main.js
144849
app/static/js/main.js
File diff suppressed because one or more lines are too long
|
@ -103,6 +103,8 @@
|
|||
|
||||
<!-- DO NOT REMOVE THIS! -->
|
||||
<!-- Adding tailwind classes that are not in html, but will be added by jinja -->
|
||||
<span class="hidden text-orange-500 !no-underline"></span>
|
||||
<span class="hid">
|
||||
<span class="border-sky-300 bg-sky-100 !dark:text-black hidden text-orange-500 !no-underline !dark:bg-blue-600 !dark:hover:bg-blue-700 !dark:text-white"></span>
|
||||
</span>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
{% else %}
|
||||
<div class="ql-snow truncate md:max-w-xl">
|
||||
<div class="dark:text-white h-30 ql-editor-readonly !px-0">
|
||||
<p>{{display_tags(section.approved_interpretation.text)|safe }}</p>
|
||||
<p>{{display_inline_elements(section.approved_interpretation.text)|safe }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
|
@ -101,9 +101,9 @@
|
|||
{% for comment in section.approved_comments %}
|
||||
<div class="p-5 ml-6">
|
||||
<div class="dark:text-white h-30">
|
||||
<div class="ql-snow mb-2 truncate md:max-w-xl">
|
||||
<div class="ql-snow mb-2 md:max-w-xl">
|
||||
<div class="dark:text-white h-30 ql-editor-readonly !px-0">
|
||||
<p>{{ display_tags(comment.text)|safe }}</p>
|
||||
<p>{{ display_inline_elements(comment.text)|safe }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -155,9 +155,9 @@
|
|||
</div>
|
||||
</div>
|
||||
<!-- prettier-ignore -->
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<!-- prettier-ignore -->
|
||||
{% for section in collection.active_sections %}
|
||||
|
@ -170,7 +170,7 @@
|
|||
{% else %}
|
||||
<div class="ql-snow truncate md:max-w-xl mb-3">
|
||||
<div class="dark:text-white h-30 ql-editor-readonly !px-0">
|
||||
<p>{{ display_tags(section.approved_interpretation.text)|safe }}</p>
|
||||
<p>{{ display_inline_elements(section.approved_interpretation.text)|safe }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- prettier-ignore -->
|
||||
|
@ -252,9 +252,9 @@
|
|||
{% for comment in section.approved_comments %}
|
||||
<div class="p-5 ml-6">
|
||||
<div class="dark:text-white h-30">
|
||||
<div class="ql-snow mb-2 truncate md:max-w-xl">
|
||||
<div class="ql-snow mb-2 md:max-w-xl">
|
||||
<div class="dark:text-white h-30 ql-editor-readonly !px-0">
|
||||
<p>{{ display_tags(comment.text)|safe }}</p>
|
||||
<p>{{ display_inline_elements(comment.text)|safe }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -160,9 +160,9 @@
|
|||
</div>
|
||||
<!-- prettier-ignore -->
|
||||
<dt class="flex justify-center w-full mb-1 text-gray-500 md:text-lg dark:text-gray-400 flex-col">
|
||||
<div class="ql-snow mb-2 truncate md:max-w-xl">
|
||||
<div class="ql-snow mb-2 md:max-w-xl">
|
||||
<div class="dark:text-white h-30 ql-editor-readonly !px-0">
|
||||
<p>{{ display_tags(interpretation.text)|safe }}</p>
|
||||
<p>{{ display_inline_elements(interpretation.text)|safe }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -69,7 +69,7 @@
|
|||
<p>{{ interpretation.section.label }}</p>
|
||||
</a>
|
||||
<div class="dark:text-white h-30 ql-editor-readonly">
|
||||
<p>{{ display_tags(interpretation.text)|safe }}</p>
|
||||
<p>{{ display_inline_elements(interpretation.text)|safe }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex mt-auto align-center justify-between md:w-full">
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
{{ section.label }}
|
||||
</h1>
|
||||
<div class="ql-editor-readonly text-lg dark:text-white p-3">
|
||||
{{display_tags(interpretation.text)|safe}}
|
||||
{{display_inline_elements(interpretation.text)|safe}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-1">
|
||||
|
@ -132,9 +132,9 @@
|
|||
<dt class="flex justify-center w-full mb-1 text-gray-500 md:text-lg dark:text-gray-400 flex-col">
|
||||
<div>
|
||||
<div class="dark:text-white h-30">
|
||||
<div class="ql-snow mb-2 truncate md:max-w-xl">
|
||||
<div class="ql-snow mb-2 md:max-w-xl">
|
||||
<div class="dark:text-white h-30 ql-editor-readonly !px-0">
|
||||
<p>{{ display_tags(comment.text)|safe }}</p>
|
||||
<p>{{ display_inline_elements(comment.text)|safe }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -195,7 +195,7 @@
|
|||
<div class="p-5 mb-2 flex justify-between items-end bg-slate-100 dark:bg-slate-600 rounded-lg">
|
||||
<div class="ql-snow">
|
||||
<div class="inline-block mb-4 ql-editor-readonly !p-0">
|
||||
{{display_tags(child.text)|safe}}
|
||||
{{display_inline_elements(child.text)|safe}}
|
||||
</div>
|
||||
<span>
|
||||
<div>
|
||||
|
|
|
@ -73,7 +73,7 @@
|
|||
<p>{{ interpretation.section.label }}</p>
|
||||
</a>
|
||||
<div class="dark:text-white h-30 ql-editor-readonly">
|
||||
<p>{{ display_tags(interpretation.text)|safe }}</p>
|
||||
<p>{{ display_inline_elements(interpretation.text)|safe }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex mt-auto align-center justify-between md:w-full border-t border-gray-200 dark:border-gray-700">
|
||||
|
|
|
@ -91,7 +91,7 @@
|
|||
<p>{{ interpretation.section.label }}</p>
|
||||
</a>
|
||||
<div class="dark:text-white h-30 ql-editor-readonly">
|
||||
<p>{{ display_tags(interpretation.text)|safe }}</p>
|
||||
<p>{{ display_inline_elements(interpretation.text)|safe }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex mt-auto align-center justify-between md:w-full">
|
||||
|
|
|
@ -79,7 +79,7 @@
|
|||
<p>{{ interpretation.section.label }}</p>
|
||||
</a>
|
||||
<div class="dark:text-white h-30 ql-editor-readonly">
|
||||
<p>{{ display_tags(interpretation.text)|safe }}</p>
|
||||
<p>{{ display_inline_elements(interpretation.text)|safe }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex mt-auto align-center justify-between md:w-full">
|
||||
|
|
|
@ -121,7 +121,7 @@
|
|||
<p>{{ interpretation.section.label }}</p>
|
||||
</a>
|
||||
<div class="dark:text-white h-30 ql-editor-readonly">
|
||||
<p>{{ display_tags(interpretation.text)|safe }}</p>
|
||||
<p>{{ display_inline_elements(interpretation.text)|safe }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex mt-auto align-center justify-between md:w-full">
|
||||
|
|
|
@ -61,6 +61,8 @@ def create_comment(
|
|||
|
||||
tags = current_app.config["TAG_REGEX"].findall(text)
|
||||
set_comment_tags(comment, tags)
|
||||
# TODO Send notifications
|
||||
# users_mentions = current_app.config["USER_MENTION_REGEX"].findall(text)
|
||||
|
||||
flash("Success!", "success")
|
||||
return redirect(redirect_url)
|
||||
|
|
|
@ -69,7 +69,7 @@ def interpretation_create(
|
|||
plain_text = clean_html(text).lower()
|
||||
tags = current_app.config["TAG_REGEX"].findall(text)
|
||||
for tag in tags:
|
||||
word = tag.lower().replace("[", "").replace("]", "")
|
||||
word = tag.lower().replace("#", "")
|
||||
plain_text = plain_text.replace(tag.lower(), word)
|
||||
|
||||
interpretation: m.Interpretation = m.Interpretation(
|
||||
|
@ -135,7 +135,7 @@ def interpretation_edit(
|
|||
plain_text = clean_html(text).lower()
|
||||
tags = current_app.config["TAG_REGEX"].findall(text)
|
||||
for tag in tags:
|
||||
word = tag.lower().replace("[", "").replace("]", "")
|
||||
word = tag.lower().replace("#", "")
|
||||
plain_text = plain_text.replace(tag.lower(), word)
|
||||
|
||||
interpretation.plain_text = plain_text
|
||||
|
|
|
@ -30,7 +30,8 @@ class BaseConfig(BaseSettings):
|
|||
HTTP_PROVIDER_URL: str
|
||||
|
||||
# regex
|
||||
TAG_REGEX = re.compile(r"\[.*?\]")
|
||||
TAG_REGEX = re.compile(r"(#+[a-zA-Z0-9(_)]{1,})") # #tag
|
||||
USER_MENTION_REGEX = re.compile(r"(?<!\w)@\w+") # @word
|
||||
|
||||
@staticmethod
|
||||
def configure(app: Flask):
|
||||
|
|
|
@ -1135,8 +1135,8 @@ def test_interpretation_in_home_last_inter_section(
|
|||
version_id=book.last_version.id,
|
||||
).save()
|
||||
|
||||
label_1 = "Test Interpretation #1 Label"
|
||||
text_1 = "Test Interpretation #1 Text"
|
||||
label_1 = "Test Interpretation no1 Label"
|
||||
text_1 = "Test Interpretation no1 Text"
|
||||
|
||||
response: Response = client.post(
|
||||
f"/book/{book.id}/{section_in_subcollection.id}/create_interpretation",
|
||||
|
|
|
@ -86,7 +86,7 @@ def test_create_tags_on_comment_create_and_edit(client: FlaskClient):
|
|||
section = db.session.get(m.Section, 1)
|
||||
interpretation = db.session.get(m.Interpretation, 1)
|
||||
|
||||
tags = "[tag1] [tag2] [tag3]"
|
||||
tags = "#tag1 #tag2 #tag3"
|
||||
response: Response = client.post(
|
||||
f"/book/{book.id}/{interpretation.id}/create_comment",
|
||||
data=dict(
|
||||
|
@ -102,9 +102,7 @@ def test_create_tags_on_comment_create_and_edit(client: FlaskClient):
|
|||
assert comment
|
||||
assert comment.tags
|
||||
|
||||
splitted_tags = [
|
||||
tag.lower().replace("[", "").replace("]", "") for tag in tags.split()
|
||||
]
|
||||
splitted_tags = [tag.lower().replace("#", "") for tag in tags.split()]
|
||||
assert len(comment.tags) == 3
|
||||
for tag in comment.tags:
|
||||
tag: m.Tag
|
||||
|
@ -113,7 +111,7 @@ def test_create_tags_on_comment_create_and_edit(client: FlaskClient):
|
|||
tags_from_db: m.Tag = m.Tag.query.all()
|
||||
assert len(tags_from_db) == 3
|
||||
|
||||
tags = "[tag1] [tag5] [tag7]"
|
||||
tags = "#tag1 #tag5 #tag7"
|
||||
response: Response = client.post(
|
||||
f"/book/{book.id}/{interpretation.id}/comment_edit",
|
||||
data=dict(text="some text" + tags, comment_id=comment.id),
|
||||
|
@ -125,9 +123,7 @@ def test_create_tags_on_comment_create_and_edit(client: FlaskClient):
|
|||
assert comment
|
||||
assert comment.tags
|
||||
|
||||
splitted_tags = [
|
||||
tag.lower().replace("[", "").replace("]", "") for tag in tags.split()
|
||||
]
|
||||
splitted_tags = [tag.lower().replace("#", "") for tag in tags.split()]
|
||||
assert len(comment.tags) == 3
|
||||
for tag in comment.tags:
|
||||
tag: m.Tag
|
||||
|
@ -144,8 +140,8 @@ def test_create_tags_on_interpretation_create_and_edit(client: FlaskClient):
|
|||
book = db.session.get(m.Book, 1)
|
||||
section = db.session.get(m.Section, 1)
|
||||
|
||||
tags = "[tag1] [tag2] [tag3]"
|
||||
text_1 = "Test Interpretation #1 Text"
|
||||
tags = "#tag1 #tag2 #tag3"
|
||||
text_1 = "Test Interpretation no1 Text"
|
||||
|
||||
response: Response = client.post(
|
||||
f"/book/{book.id}/{section.id}/create_interpretation",
|
||||
|
@ -160,9 +156,7 @@ def test_create_tags_on_interpretation_create_and_edit(client: FlaskClient):
|
|||
assert interpretation
|
||||
assert interpretation.tags
|
||||
|
||||
splitted_tags = [
|
||||
tag.lower().replace("[", "").replace("]", "") for tag in tags.split()
|
||||
]
|
||||
splitted_tags = [tag.lower().replace("#", "") for tag in tags.split()]
|
||||
assert len(interpretation.tags) == 3
|
||||
for tag in interpretation.tags:
|
||||
tag: m.Tag
|
||||
|
@ -171,7 +165,7 @@ def test_create_tags_on_interpretation_create_and_edit(client: FlaskClient):
|
|||
tags_from_db: m.Tag = m.Tag.query.all()
|
||||
assert len(tags_from_db) == 3
|
||||
|
||||
tags = "[tag-4] [tag5] [tag3]"
|
||||
tags = "#tag4 #tag3 #tag5"
|
||||
response: Response = client.post(
|
||||
f"/book/{book.id}/{interpretation.id}/edit_interpretation",
|
||||
data=dict(interpretation_id=interpretation.id, text=text_1 + tags),
|
||||
|
@ -184,9 +178,7 @@ def test_create_tags_on_interpretation_create_and_edit(client: FlaskClient):
|
|||
).first()
|
||||
assert interpretation
|
||||
|
||||
splitted_tags = [
|
||||
tag.lower().replace("[", "").replace("]", "") for tag in tags.split()
|
||||
]
|
||||
splitted_tags = [tag.lower().replace("#", "") for tag in tags.split()]
|
||||
assert len(interpretation.tags) == 3
|
||||
for tag in interpretation.tags:
|
||||
tag: m.Tag
|
||||
|
|
Loading…
Reference in New Issue