diff --git a/migration/extract.py b/migration/extract.py
index 5dd7ccba..d67275a9 100644
--- a/migration/extract.py
+++ b/migration/extract.py
@@ -27,6 +27,39 @@ def replace_tooltips(body):
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('', 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(''))
+ })
+ newparts[i] = "ℹ️"
+ else:
+ newparts[i] = part
+ i += 1
+ return ("".join(newparts), placed)
+
+
def place_tooltips(body):
parts = body.split("&&&")
lll = len(parts)
@@ -203,7 +236,7 @@ def extract_dataimages(parts, prefix):
di = "data:image"
-def extract_md_images(body, oid):
+def extract_md_images(body, prefix):
newbody = ""
body = (
body.replace("\n! [](" + di, "\n 
@@ -212,7 +245,7 @@ def extract_md_images(body, oid):
)
parts = body.split(di)
if len(parts) > 1:
- newbody = extract_dataimages(parts, oid)
+ newbody = extract_dataimages(parts, prefix)
else:
newbody = body
return newbody
@@ -238,24 +271,24 @@ def cleanup(body):
return newbody
-def extract_md(body, oid=""):
+def extract_md(body, shout_dict = None):
newbody = body
if newbody:
- uid = oid or uuid.uuid4()
- newbody = extract_md_images(newbody, uid)
- if not newbody:
- raise Exception("extract_images error")
-
newbody = cleanup(newbody)
if not newbody:
raise Exception("cleanup error")
- newbody, placed = place_tooltips(newbody)
- if not newbody:
- raise Exception("place_tooltips error")
+ if shout_dict:
+
+ 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
@@ -342,7 +375,9 @@ def prepare_html_body(entry):
return body
-def extract_html(entry):
+def extract_html(entry, shout_id = None):
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"))
return body_html
diff --git a/migration/tables/content_items.py b/migration/tables/content_items.py
index d44b19dc..e638a690 100644
--- a/migration/tables/content_items.py
+++ b/migration/tables/content_items.py
@@ -47,18 +47,27 @@ def create_author_from_app(app):
if not user:
print('[migration] creating user...')
name = app.get('name')
- slug = translit(name, "ru", reversed=True).lower()
- slug = re.sub('[^0-9a-zA-Z]+', '-', slug)
- # check if nameslug is used
- user = session.query(User).where(User.slug == slug).first()
- # get slug from email
- if user:
- slug = app['email'].split('@')[0]
- 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()
+ if name:
+ slug = translit(name, "ru", reversed=True).lower()
+ slug = re.sub('[^0-9a-zA-Z]+', '-', slug)
+ # check if slug is used
+ if slug:
+ user = session.query(User).where(User.slug == slug).first()
+
+ # get slug from email
+ if user:
+ slug = app['email'].split('@')[0]
+ 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
if not user:
@@ -84,7 +93,7 @@ def create_author_from_app(app):
async def create_shout(shout_dict):
s = Shout.create(**shout_dict)
- author = shout_dict['authors'][0]
+ author = s.authors[0]
with local_session() as session:
srf = session.query(ShoutReactionsFollower).where(
ShoutReactionsFollower.shout == s.id
@@ -109,8 +118,9 @@ async def get_user(entry, storage):
userdata = anondict
# cleanup slug
slug = userdata.get("slug", "")
- slug = re.sub('[^0-9a-zA-Z]+', '-', slug)
- userdata["slug"] = slug
+ if slug:
+ slug = re.sub('[^0-9a-zA-Z]+', '-', slug)
+ userdata["slug"] = slug
user = await process_user(userdata, storage, user_oid)
return user, user_oid
diff --git a/migration/tables/remarks.py b/migration/tables/remarks.py
index 78f52c92..026b95c6 100644
--- a/migration/tables/remarks.py
+++ b/migration/tables/remarks.py
@@ -1,31 +1,42 @@
from base.orm import local_session
from migration.extract import extract_md
from migration.html2text import html2text
-from orm.remark import Remark
+from orm.reaction import Reaction, ReactionKind
def migrate(entry, storage):
post_oid = entry['contentItem']
print(post_oid)
shout_dict = storage['shouts']['by_oid'].get(post_oid)
- remark = {
- "shout": shout_dict['id'],
- "body": extract_md(
- html2text(entry['body']),
- entry['_id']
- ),
- "desc": extract_md(
- html2text(
- entry['textAfter'] or '' + \
- entry['textBefore'] or '' + \
- entry['textSelected'] or ''
+ if shout_dict:
+ print(shout_dict['body'])
+ remark = {
+ "shout": shout_dict['id'],
+ "body": extract_md(
+ html2text(entry['body']),
+ shout_dict
),
- entry["_id"]
- )
- }
+ "kind": ReactionKind.REMARK
+ }
- with local_session() as session:
- rmrk = Remark.create(**remark)
- session.commit()
- del rmrk["_sa_instance_state"]
- return rmrk
+ if entry.get('textBefore'):
+ remark['range'] = str(
+ shout_dict['body']
+ .index(
+ 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
diff --git a/migration/tables/topics.py b/migration/tables/topics.py
index 3287adb7..17804376 100644
--- a/migration/tables/topics.py
+++ b/migration/tables/topics.py
@@ -10,7 +10,7 @@ def migrate(entry):
"slug": entry["slug"],
"oid": entry["_id"],
"title": entry["title"].replace(" ", " "),
- "body": extract_md(html2text(body_orig), entry["_id"])
+ "body": extract_md(html2text(body_orig))
}
with local_session() as session:
diff --git a/orm/reaction.py b/orm/reaction.py
index f484808a..3ff769cd 100644
--- a/orm/reaction.py
+++ b/orm/reaction.py
@@ -11,7 +11,7 @@ class ReactionKind(Enumeration):
DISAGREE = 2 # -1
PROOF = 3 # +1
DISPROOF = 4 # -1
- ASK = 5 # +0 bookmark
+ ASK = 5 # +0
PROPOSE = 6 # +0
QUOTE = 7 # +0 bookmark
COMMENT = 8 # +0
@@ -19,6 +19,8 @@ class ReactionKind(Enumeration):
REJECT = 0 # -1
LIKE = 11 # +1
DISLIKE = 12 # -1
+ REMARK = 13 # 0
+ FOOTNOTE = 14 # 0
# TYPE = # rating diff
diff --git a/orm/remark.py b/orm/remark.py
deleted file mode 100644
index 9432a3f5..00000000
--- a/orm/remark.py
+++ /dev/null
@@ -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")
diff --git a/resolvers/inbox/chats.py b/resolvers/inbox/chats.py
index 828f769c..853defab 100644
--- a/resolvers/inbox/chats.py
+++ b/resolvers/inbox/chats.py
@@ -75,7 +75,7 @@ async def create_chat(_, info, title="", members=[]):
if chat:
chat = json.loads(chat)
if chat['title'] == "":
- print('[inbox] craeteChat found old chat')
+ print('[inbox] createChat found old chat')
print(chat)
break
if chat:
diff --git a/resolvers/inbox/load.py b/resolvers/inbox/load.py
index fd746df9..b5909d8c 100644
--- a/resolvers/inbox/load.py
+++ b/resolvers/inbox/load.py
@@ -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):
chat_users = []
auth: AuthCredentials = info.context["request"].auth
-
try:
+ onliners = await redis.execute("SMEMBERS", "users-online")
chat_users += await followed_authors(auth.user_id)
limit = limit - len(chat_users)
except Exception:
pass
with local_session() as session:
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 {
- "members": chat_users,
+ "members": members,
"error": None
}
diff --git a/resolvers/zine/reactions.py b/resolvers/zine/reactions.py
index f935920c..94ead0ca 100644
--- a/resolvers/zine/reactions.py
+++ b/resolvers/zine/reactions.py
@@ -157,6 +157,23 @@ async def create_reaction(_, info, reaction={}):
reaction['createdBy'] = auth.user_id
with local_session() as session:
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.commit()
@@ -230,9 +247,9 @@ async def delete_reaction(_, info, reaction=None):
return {"error": "access denied"}
r.deletedAt = datetime.now(tz=timezone.utc)
session.commit()
- return {
- "reaction": r
- }
+ return {
+ "reaction": r
+ }
@query.field("loadReactionsBy")
diff --git a/resolvers/zine/remark.py b/resolvers/zine/remark.py
deleted file mode 100644
index 6f5f9d48..00000000
--- a/resolvers/zine/remark.py
+++ /dev/null
@@ -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
diff --git a/schema.graphql b/schema.graphql
index 080cb371..cf66d24d 100644
--- a/schema.graphql
+++ b/schema.graphql
@@ -407,6 +407,9 @@ enum ReactionKind {
PROPOSE
ASK
+ REMARK
+ FOOTNOTE
+
ACCEPT
REJECT
}