hotfix: proposals, chatmembers, footnotes, etc

This commit is contained in:
tonyrewin 2023-01-25 09:37:37 +03:00
commit d3703ab17c
11 changed files with 145 additions and 120 deletions

View File

@ -27,6 +27,39 @@ def replace_tooltips(body):
return newbody return newbody
def extract_footnotes(body, shout_dict):
parts = body.split("&&&")
lll = len(parts)
newparts = list(parts)
placed = False
if lll & 1:
if lll > 1:
i = 1
print("[extract] found %d footnotes in body" % (lll - 1))
for part in parts[1:]:
if i & 1:
placed = True
if 'a class="footnote-url" href=' in part:
print("[extract] footnote: " + part)
fn = 'a class="footnote-url" href="'
exxtracted_link = part.split(fn, 1)[1].split('"', 1)[0]
extracted_body = part.split(fn, 1)[1].split('>', 1)[1].split('</a>', 1)[0]
print("[extract] footnote link: " + extracted_link)
with local_session() as session:
Reaction.create({
"shout": shout_dict['id'],
"kind": ReactionKind.FOOTNOTE,
"body": extracted_body,
"range": str(body.index(fn + link) - len('<')) + ':' + str(body.index(extracted_body) + len('</a>'))
})
newparts[i] = "<a href='#'></a>"
else:
newparts[i] = part
i += 1
return ("".join(newparts), placed)
def place_tooltips(body): def place_tooltips(body):
parts = body.split("&&&") parts = body.split("&&&")
lll = len(parts) lll = len(parts)
@ -203,7 +236,7 @@ def extract_dataimages(parts, prefix):
di = "data:image" di = "data:image"
def extract_md_images(body, oid): def extract_md_images(body, prefix):
newbody = "" newbody = ""
body = ( body = (
body.replace("\n! [](" + di, "\n ![](" + di) body.replace("\n! [](" + di, "\n ![](" + di)
@ -212,7 +245,7 @@ def extract_md_images(body, oid):
) )
parts = body.split(di) parts = body.split(di)
if len(parts) > 1: if len(parts) > 1:
newbody = extract_dataimages(parts, oid) newbody = extract_dataimages(parts, prefix)
else: else:
newbody = body newbody = body
return newbody return newbody
@ -238,24 +271,24 @@ def cleanup(body):
return newbody return newbody
def extract_md(body, oid=""): def extract_md(body, shout_dict = None):
newbody = body newbody = body
if newbody: if newbody:
uid = oid or uuid.uuid4()
newbody = extract_md_images(newbody, uid)
if not newbody:
raise Exception("extract_images error")
newbody = cleanup(newbody) newbody = cleanup(newbody)
if not newbody: if not newbody:
raise Exception("cleanup error") raise Exception("cleanup error")
newbody, placed = place_tooltips(newbody) if shout_dict:
if not newbody:
raise Exception("place_tooltips error") uid = shout_dict['id'] or uuid.uuid4()
newbody = extract_md_images(newbody, uid)
if not newbody:
raise Exception("extract_images error")
newbody, placed = extract_footnotes(body, shout_dict)
if not newbody:
raise Exception("extract_footnotes error")
if placed:
newbody = "import Tooltip from '$/components/Article/Tooltip'\n\n" + newbody
return newbody return newbody
@ -342,7 +375,9 @@ def prepare_html_body(entry):
return body return body
def extract_html(entry): def extract_html(entry, shout_id = None):
body_orig = (entry.get("body") or "").replace('\(', '(').replace('\)', ')') body_orig = (entry.get("body") or "").replace('\(', '(').replace('\)', ')')
if shout_id:
extract_footnotes(body_orig, shout_id)
body_html = str(BeautifulSoup(body_orig, features="html.parser")) body_html = str(BeautifulSoup(body_orig, features="html.parser"))
return body_html return body_html

View File

@ -47,18 +47,27 @@ def create_author_from_app(app):
if not user: if not user:
print('[migration] creating user...') print('[migration] creating user...')
name = app.get('name') name = app.get('name')
slug = translit(name, "ru", reversed=True).lower() if name:
slug = re.sub('[^0-9a-zA-Z]+', '-', slug) slug = translit(name, "ru", reversed=True).lower()
# check if nameslug is used slug = re.sub('[^0-9a-zA-Z]+', '-', slug)
user = session.query(User).where(User.slug == slug).first() # check if slug is used
# get slug from email if slug:
if user: user = session.query(User).where(User.slug == slug).first()
slug = app['email'].split('@')[0]
user = session.query(User).where(User.slug == slug).first() # get slug from email
# one more try if user:
if user: slug = app['email'].split('@')[0]
slug += '-author' user = session.query(User).where(User.slug == slug).first()
user = session.query(User).where(User.slug == slug).first() # one more try
if user:
slug += '-author'
user = session.query(User).where(User.slug == slug).first()
else:
print(f'[migration] author @{slug} is found by email')
else:
print(f'[migration] author @{slug} is found')
# create user with application data # create user with application data
if not user: if not user:
@ -84,7 +93,7 @@ def create_author_from_app(app):
async def create_shout(shout_dict): async def create_shout(shout_dict):
s = Shout.create(**shout_dict) s = Shout.create(**shout_dict)
author = shout_dict['authors'][0] author = s.authors[0]
with local_session() as session: with local_session() as session:
srf = session.query(ShoutReactionsFollower).where( srf = session.query(ShoutReactionsFollower).where(
ShoutReactionsFollower.shout == s.id ShoutReactionsFollower.shout == s.id
@ -109,8 +118,9 @@ async def get_user(entry, storage):
userdata = anondict userdata = anondict
# cleanup slug # cleanup slug
slug = userdata.get("slug", "") slug = userdata.get("slug", "")
slug = re.sub('[^0-9a-zA-Z]+', '-', slug) if slug:
userdata["slug"] = slug slug = re.sub('[^0-9a-zA-Z]+', '-', slug)
userdata["slug"] = slug
user = await process_user(userdata, storage, user_oid) user = await process_user(userdata, storage, user_oid)
return user, user_oid return user, user_oid

View File

@ -1,31 +1,42 @@
from base.orm import local_session from base.orm import local_session
from migration.extract import extract_md from migration.extract import extract_md
from migration.html2text import html2text from migration.html2text import html2text
from orm.remark import Remark from orm.reaction import Reaction, ReactionKind
def migrate(entry, storage): def migrate(entry, storage):
post_oid = entry['contentItem'] post_oid = entry['contentItem']
print(post_oid) print(post_oid)
shout_dict = storage['shouts']['by_oid'].get(post_oid) shout_dict = storage['shouts']['by_oid'].get(post_oid)
remark = { if shout_dict:
"shout": shout_dict['id'], print(shout_dict['body'])
"body": extract_md( remark = {
html2text(entry['body']), "shout": shout_dict['id'],
entry['_id'] "body": extract_md(
), html2text(entry['body']),
"desc": extract_md( shout_dict
html2text(
entry['textAfter'] or '' + \
entry['textBefore'] or '' + \
entry['textSelected'] or ''
), ),
entry["_id"] "kind": ReactionKind.REMARK
) }
}
with local_session() as session: if entry.get('textBefore'):
rmrk = Remark.create(**remark) remark['range'] = str(
session.commit() shout_dict['body']
del rmrk["_sa_instance_state"] .index(
return rmrk entry['textBefore'] or ''
)
) + ':' + str(
shout_dict['body']
.index(
entry['textAfter'] or ''
) + len(
entry['textAfter'] or ''
)
)
with local_session() as session:
rmrk = Reaction.create(**remark)
session.commit()
del rmrk["_sa_instance_state"]
return rmrk
return

View File

@ -10,7 +10,7 @@ def migrate(entry):
"slug": entry["slug"], "slug": entry["slug"],
"oid": entry["_id"], "oid": entry["_id"],
"title": entry["title"].replace("&nbsp;", " "), "title": entry["title"].replace("&nbsp;", " "),
"body": extract_md(html2text(body_orig), entry["_id"]) "body": extract_md(html2text(body_orig))
} }
with local_session() as session: with local_session() as session:

View File

@ -11,7 +11,7 @@ class ReactionKind(Enumeration):
DISAGREE = 2 # -1 DISAGREE = 2 # -1
PROOF = 3 # +1 PROOF = 3 # +1
DISPROOF = 4 # -1 DISPROOF = 4 # -1
ASK = 5 # +0 bookmark ASK = 5 # +0
PROPOSE = 6 # +0 PROPOSE = 6 # +0
QUOTE = 7 # +0 bookmark QUOTE = 7 # +0 bookmark
COMMENT = 8 # +0 COMMENT = 8 # +0
@ -19,6 +19,8 @@ class ReactionKind(Enumeration):
REJECT = 0 # -1 REJECT = 0 # -1
LIKE = 11 # +1 LIKE = 11 # +1
DISLIKE = 12 # -1 DISLIKE = 12 # -1
REMARK = 13 # 0
FOOTNOTE = 14 # 0
# TYPE = <reaction index> # rating diff # TYPE = <reaction index> # rating diff

View File

@ -1,15 +0,0 @@
from datetime import datetime
from enum import Enum as Enumeration
from sqlalchemy import Column, DateTime, Enum, ForeignKey, String
from base.orm import Base
class Remark(Base):
__tablename__ = "remark"
body = Column(String, nullable=False)
desc = Column(String, default='')
shout = Column(ForeignKey("shout.id"), nullable=True, index=True, comment="Shout")

View File

@ -75,7 +75,7 @@ async def create_chat(_, info, title="", members=[]):
if chat: if chat:
chat = json.loads(chat) chat = json.loads(chat)
if chat['title'] == "": if chat['title'] == "":
print('[inbox] craeteChat found old chat') print('[inbox] createChat found old chat')
print(chat) print(chat)
break break
if chat: if chat:

View File

@ -124,15 +124,25 @@ async def load_messages_by(_, info, by, limit: int = 10, offset: int = 0):
async def load_recipients(_, info, limit=50, offset=0): async def load_recipients(_, info, limit=50, offset=0):
chat_users = [] chat_users = []
auth: AuthCredentials = info.context["request"].auth auth: AuthCredentials = info.context["request"].auth
try: try:
onliners = await redis.execute("SMEMBERS", "users-online")
chat_users += await followed_authors(auth.user_id) chat_users += await followed_authors(auth.user_id)
limit = limit - len(chat_users) limit = limit - len(chat_users)
except Exception: except Exception:
pass pass
with local_session() as session: with local_session() as session:
chat_users += session.query(User).where(User.emailConfirmed).limit(limit).offset(offset) chat_users += session.query(User).where(User.emailConfirmed).limit(limit).offset(offset)
members = []
for a in chat_users:
members.append({
"id": a.id,
"slug": a.slug,
"userpic": a.userpic,
"name": a.name,
"lastSeen": a.lastSeen,
"online": a.id in onliners
})
return { return {
"members": chat_users, "members": members,
"error": None "error": None
} }

View File

@ -157,6 +157,23 @@ async def create_reaction(_, info, reaction={}):
reaction['createdBy'] = auth.user_id reaction['createdBy'] = auth.user_id
with local_session() as session: with local_session() as session:
r = Reaction.create(**reaction) r = Reaction.create(**reaction)
shout = session.query(Shout).where(Shout.id == r.shout).one()
# Proposal accepting logix
if r.replyTo is not None and \
r.kind == ReactionKind.ACCEPT and \
user_id in shout.dict()['authors']:
replied_reaction = session.query(Reaction).when(Reaction.id == r.replyTo).first()
if replied_reaction and replied_reaction.kind == ReactionKind.PROPOSE:
if replied_reaction.range:
old_body = shout.body
start, end = replied_reaction.range.split(':')
start = int(start)
end = int(end)
new_body = old_body[:start] + replied_reaction.body + old_body[end:]
shout.body = new_body
# TODO: update git version control
session.add(r) session.add(r)
session.commit() session.commit()
@ -230,9 +247,9 @@ async def delete_reaction(_, info, reaction=None):
return {"error": "access denied"} return {"error": "access denied"}
r.deletedAt = datetime.now(tz=timezone.utc) r.deletedAt = datetime.now(tz=timezone.utc)
session.commit() session.commit()
return { return {
"reaction": r "reaction": r
} }
@query.field("loadReactionsBy") @query.field("loadReactionsBy")

View File

@ -1,48 +0,0 @@
from datetime import datetime, timedelta, timezone
from sqlalchemy.orm import joinedload, aliased
from sqlalchemy.sql.expression import desc, asc, select, func
from base.orm import local_session
from base.resolvers import query, mutation
from base.exceptions import ObjectNotExist
from orm.remark import Remark
@mutation.field("createRemark")
@login_required
async def create_remark(_, info, slug, body):
auth = info.context["request"].auth
user_id = auth.user_id
with local_session() as session:
tt = Remark.create(slug=slug, body=body)
session.commit()
return
@mutation.field("updateRemark")
@login_required
async def update_remark(_, info, slug, body = ''):
auth = info.context["request"].auth
user_id = auth.user_id
with local_session() as session:
rmrk = session.query(Remark).where(Remark.slug == slug).one()
if body:
tt.body = body
session.add(rmrk)
session.commit()
return
@mutation.field("deleteRemark")
@login_required
async def delete_remark(_, info, slug):
auth = info.context["request"].auth
user_id = auth.user_id
with local_session() as session:
rmrk = session.query(Remark).where(Remark.slug == slug).one()
rmrk.remove()
session.commit()
return
@query.field("loadRemark")
@login_required
async def load_remark(_, info, slug):
pass

View File

@ -407,6 +407,9 @@ enum ReactionKind {
PROPOSE PROPOSE
ASK ASK
REMARK
FOOTNOTE
ACCEPT ACCEPT
REJECT REJECT
} }