From f13f40c89acb46b89dc0da258ff5d0040a736ad8 Mon Sep 17 00:00:00 2001 From: tonyrewin Date: Thu, 11 Aug 2022 14:22:10 +0300 Subject: [PATCH] server-cli-todo-cleanup --- auth/email.py | 2 +- auth/validations.py | 2 - main.py | 2 +- migration/extract.py | 10 ++-- migration/html2text/__init__.py | 10 ++-- migration/tables/comments.py | 4 +- migration/tables/content_items.py | 4 +- orm/collection.py | 2 + orm/notification.py | 2 +- resolvers/collection.py | 80 +++++++++++++++++++++++++++++++ resolvers/reactions.py | 2 +- resolvers/zine.py | 4 +- schema.graphql | 5 +- server.py | 3 +- services/auth/users.py | 1 - services/stat/reacted.py | 1 - services/stat/viewed.py | 2 - services/zine/shoutauthor.py | 3 +- 18 files changed, 106 insertions(+), 33 deletions(-) create mode 100644 resolvers/collection.py diff --git a/auth/email.py b/auth/email.py index 9c9b7220..dcc815af 100644 --- a/auth/email.py +++ b/auth/email.py @@ -14,7 +14,7 @@ email_templates = {"confirm_email" : "", "auth_email" : "", "reset_password_emai def load_email_templates(): for name in email_templates: - filename = "auth/templates/%s.tmpl" % name # TODO: check path + filename = "auth/templates/%s.tmpl" % name with open(filename) as f: email_templates[name] = f.read() print("[auth.email] templates loaded") diff --git a/auth/validations.py b/auth/validations.py index 38a2f620..fc2b7353 100644 --- a/auth/validations.py +++ b/auth/validations.py @@ -25,5 +25,3 @@ class CreateUser(BaseModel): # age: Optional[int] # phone: Optional[Text] password: Optional[Text] - -# TODO: update validations diff --git a/main.py b/main.py index 9fa77251..1c1cb9ed 100644 --- a/main.py +++ b/main.py @@ -40,7 +40,7 @@ async def shutdown(): await redis.disconnect() routes = [ - Route("/oauth/{provider}", endpoint=oauth_login), # TODO: isolate auth microservice + Route("/oauth/{provider}", endpoint=oauth_login), Route("/oauth_authorize", endpoint=oauth_authorize), Route("/email_authorize", endpoint=email_authorize) ] diff --git a/migration/extract.py b/migration/extract.py index 0c09ae0c..eff2f22f 100644 --- a/migration/extract.py +++ b/migration/extract.py @@ -9,11 +9,11 @@ s3 = 'https://discours-io.s3.amazonaws.com/' cdn = 'https://assets.discours.io' def replace_tooltips(body): - # FIXME: if you prefer regexp + # change if you prefer regexp newbody = body matches = list(re.finditer(TOOLTIP_REGEX, body, re.IGNORECASE | re.MULTILINE))[1:] for match in matches: - newbody = body.replace(match.group(1), '') # FIXME: doesn't work + newbody = body.replace(match.group(1), '') # NOTE: doesn't work if len(matches) > 0: print('[extract] found %d tooltips' % len(matches)) return newbody @@ -54,7 +54,7 @@ cache = {} def reextract_images(body, oid): - # FIXME: if you prefer regexp + # change if you prefer regexp matches = list(re.finditer(IMG_REGEX, body, re.IGNORECASE | re.MULTILINE))[1:] i = 0 for match in matches: @@ -63,7 +63,7 @@ def reextract_images(body, oid): name = oid + str(i) link = public + '/upload/image-' + name + '.' + ext img = match.group(4) - title = match.group(1) # FIXME: this is not the title + title = match.group(1) # NOTE: this is not the title if img not in cache: content = base64.b64decode(img + '==') print(str(len(img)) + ' image bytes been written') @@ -72,7 +72,7 @@ def reextract_images(body, oid): i += 1 else: print('[extract] image cached ' + cache[img]) - body.replace(str(match), '![' + title + '](' + cdn + link + ')') # FIXME: this does not work + body.replace(str(match), '![' + title + '](' + cdn + link + ')') # WARNING: this does not work return body IMAGES = { diff --git a/migration/html2text/__init__.py b/migration/html2text/__init__.py index 26810d42..bffbcaa5 100644 --- a/migration/html2text/__init__.py +++ b/migration/html2text/__init__.py @@ -30,8 +30,7 @@ from .utils import ( __version__ = (2020, 1, 16) -# TODO: -# Support decoded entities with UNIFIABLE. +# TODO: Support decoded entities with UNIFIABLE. class HTML2Text(html.parser.HTMLParser): @@ -503,7 +502,7 @@ class HTML2Text(html.parser.HTMLParser): self.handle_emphasis(start, tag_style, parent_style) if tag in ["kbd", "code", "tt"] and not self.pre: - self.o("`") # TODO: `` `this` `` + self.o("`") # `` `this` `` self.code = not self.code if tag == "abbr": @@ -681,7 +680,7 @@ class HTML2Text(html.parser.HTMLParser): # Indent two spaces per list, except use three spaces for an # unordered list inside an ordered list. # https://spec.commonmark.org/0.28/#motivation - # TODO: line up
  1. s > 9 correctly. + # WARNING: does not line up
    1. s > 9 correctly. parent_list = None for list in self.list: self.o( @@ -761,7 +760,6 @@ class HTML2Text(html.parser.HTMLParser): self.out("\n[/code]") self.p() - # TODO: Add docstring for these one letter functions def pbr(self) -> None: "Pretty print has a line break" if self.p_p == 0: @@ -807,7 +805,7 @@ class HTML2Text(html.parser.HTMLParser): return if self.startpre: - # self.out(" :") #TODO: not output when already one there + # self.out(" :") # not an output when already one there if not data.startswith("\n") and not data.startswith("\r\n"): #
      stuff...
       					data = "\n" + data
      diff --git a/migration/tables/comments.py b/migration/tables/comments.py
      index cce282d3..648170b2 100644
      --- a/migration/tables/comments.py
      +++ b/migration/tables/comments.py
      @@ -47,8 +47,8 @@ def migrate(entry, storage):
       		}
       	'''
       	reaction_dict = {}
      -	# FIXME: comment_dict['createdAt'] = ts if not entry.get('createdAt') else date_parse(entry.get('createdAt'))
      -	# print('[migration] comment original date %r' % entry.get('createdAt'))
      +	reaction_dict['createdAt'] = ts if not entry.get('createdAt') else date_parse(entry.get('createdAt'))
      +	print('[migration] reaction original date %r' % entry.get('createdAt'))
       	# print('[migration] comment date %r ' % comment_dict['createdAt'])
       	reaction_dict['body'] = html2text(entry.get('body', ''))
       	reaction_dict['oid'] = entry['_id']
      diff --git a/migration/tables/content_items.py b/migration/tables/content_items.py
      index 0fa23961..849df0bb 100644
      --- a/migration/tables/content_items.py
      +++ b/migration/tables/content_items.py
      @@ -118,8 +118,8 @@ def migrate(entry, storage):
       	s = object()
       	shout_dict = r.copy() 
       	user = None
      -	del shout_dict['topics'] # FIXME: AttributeError: 'str' object has no attribute '_sa_instance_state'
      -	#del shout_dict['rating'] # FIXME: TypeError: 'rating' is an invalid keyword argument for Shout
      +	del shout_dict['topics'] # NOTE: AttributeError: 'str' object has no attribute '_sa_instance_state'
      +	#del shout_dict['rating'] # NOTE: TypeError: 'rating' is an invalid keyword argument for Shout
       	#del shout_dict['ratings']
       	email = userdata.get('email')
       	slug = userdata.get('slug')
      diff --git a/orm/collection.py b/orm/collection.py
      index 26c5eb39..95b8a6f4 100644
      --- a/orm/collection.py
      +++ b/orm/collection.py
      @@ -17,4 +17,6 @@ class Collection(Base):
       	title: str = Column(String, nullable=False, comment="Title")
       	body: str = Column(String, nullable=True, comment="Body")
       	pic: str = Column(String, nullable=True, comment="Picture")
      +	createdAt: datetime = Column(DateTime, default=datetime.now, comment="Created At")
      +	createdBy: str = Column(ForeignKey('user.id'), comment="Created By")
       
      diff --git a/orm/notification.py b/orm/notification.py
      index d41bd874..1996f700 100644
      --- a/orm/notification.py
      +++ b/orm/notification.py
      @@ -8,4 +8,4 @@ class Notification(Base):
       	template: str = Column(String, nullable = False)
       	variables: JSONType = Column(JSONType, nullable = True) # [ , .. ]
       
      -	# FIXME looks like frontend code
      \ No newline at end of file
      +	# looks like frontend code
      \ No newline at end of file
      diff --git a/resolvers/collection.py b/resolvers/collection.py
      new file mode 100644
      index 00000000..c00c3ac5
      --- /dev/null
      +++ b/resolvers/collection.py
      @@ -0,0 +1,80 @@
      +from orm.collection import Collection, CollectionFollower
      +from base.orm import local_session
      +from orm.user import User
      +from base.resolvers import mutation, query
      +from auth.authenticate import login_required
      +from datetime import datetime
      +from typing import Collection, List
      +from sqlalchemy import and_
      +
      +@mutation.field("createCollection")
      +@login_required
      +async def create_collection(_, info, input):
      +	auth = info.context["request"].auth
      +	user_id = auth.user_id
      +	collection = Collection.create(
      +		slug = input.get('slug', ''),
      +		title = input.get('title', ''),
      +		desc = input.get('desc', ''),
      +		pic = input.get('pic', '')
      +		)
      +
      +	return {"collection": collection}
      +
      +@mutation.field("updateCollection")
      +@login_required
      +async def update_collection(_, info, input):
      +	auth = info.context["request"].auth
      +	user_id = auth.user_id
      +	collection_slug = input.get('slug', '')
      +	with local_session() as session:
      +		owner = session.query(User).filter(User.id == user_id) # note list here
      +		collection = session.query(Collection).filter(Collection.slug == collection_slug).first()
      +		editors = [e.slug for e in collection.editors]
      +		if not collection:
      +			return {"error": "invalid collection id"}
      +		if collection.createdBy not in (owner + editors):
      +			return {"error": "access denied"}
      +		collection.title = input.get('title', '')
      +		collection.desc = input.get('desc', '')
      +		collection.pic = input.get('pic', '')
      +		collection.updatedAt = datetime.now()
      +		session.commit()
      +
      +@mutation.field("deleteCollection")
      +@login_required
      +async def delete_collection(_, info, slug):
      +	auth = info.context["request"].auth
      +	user_id = auth.user_id
      +	with local_session() as session:
      +		collection = session.query(Collection).filter(Collection.slug == slug).first()
      +		if not collection:
      +			return {"error": "invalid collection slug"}
      +		if collection.owner != user_id:
      +			return {"error": "access denied"}
      +		collection.deletedAt = datetime.now()
      +		session.commit()
      +
      +	return {}
      +
      +@query.field("getCollection")
      +async def get_collection(_, info, userslug, colslug):
      +	with local_session() as session:
      +		user = session.query(User).filter(User.slug == userslug).first()
      +		if user:
      +			collection = session.\
      +				query(Collection).\
      +				where(and_(Collection.createdBy == user.id, Collection.slug == colslug)).\
      +				first()
      +		if not collection:
      +			return {"error": "collection not found"}
      +	return collection
      +
      +@query.field("getMyColelctions")
      +@login_required
      +async def get_collections(_, info):
      +	auth = info.context["request"].auth
      +	user_id = auth.user_id
      +	with local_session() as session:
      +		collections = session.query(Collection).when(Collection.createdBy == user_id).all()
      +	return collections
      \ No newline at end of file
      diff --git a/resolvers/reactions.py b/resolvers/reactions.py
      index fd1a1fe7..2e68c7c1 100644
      --- a/resolvers/reactions.py
      +++ b/resolvers/reactions.py
      @@ -75,7 +75,7 @@ async def update_reaction(_, info, inp):
               reaction.body = inp['body']
               reaction.updatedAt = datetime.now()
               if reaction.kind != inp['kind']:
      -            # TODO: change mind detection
      +            # NOTE: change mind detection can be here
                   pass
               if inp.get('range'):
                   reaction.range = inp.get('range')
      diff --git a/resolvers/zine.py b/resolvers/zine.py
      index 931cf369..1bce3936 100644
      --- a/resolvers/zine.py
      +++ b/resolvers/zine.py
      @@ -51,7 +51,7 @@ async def view_shout(_, info, slug):
       @query.field("getShoutBySlug")
       async def get_shout_by_slug(_, info, slug):
       	shout = None
      -	# TODO: append captions anyhow
      +	# FIXME: append captions anyhow
       	with local_session() as session:
       		shout = session.query(Shout).\
       			options([
      @@ -65,7 +65,7 @@ async def get_shout_by_slug(_, info, slug):
       
       	if not shout:
       		print(f"[resolvers.zine] error: shout with slug {slug} not exist")
      -		return {} #TODO return error field
      +		return {"error" : "shout not found"}
       	
       	return shout
       
      diff --git a/schema.graphql b/schema.graphql
      index b527543b..a4128701 100644
      --- a/schema.graphql
      +++ b/schema.graphql
      @@ -189,9 +189,6 @@ type Mutation {
         unfollow(what: FollowingEntity!, slug: String!): Result!
       
         # TODO: transform reaction with body to shout
      -
      -  # NOTE: so-named 'collections' are tuned feeds
      -  # TODO: Feed entity and CRUM: createFeed updateFeed deleteFeed mergeFeeds
       }
       
       ################################### Query
      @@ -229,7 +226,7 @@ type Query {
         shoutsByCommunities(slugs: [String]!, page: Int!, size: Int!): [Shout]!
         myCandidates(page: Int!, size: Int!): [Shout]! # test
         topViewed(page: Int!, size: Int!): [Shout]!
      -  # TODO: topReacted(page: Int!, size: Int!): [Shout]!
      +  # topReacted(page: Int!, size: Int!): [Shout]!
         topMonth(page: Int!, size: Int!): [Shout]!
         topOverall(page: Int!, size: Int!): [Shout]!
         recentPublished(page: Int!, size: Int!): [Shout]! # homepage
      diff --git a/server.py b/server.py
      index 6f28f3ef..1e211bb7 100644
      --- a/server.py
      +++ b/server.py
      @@ -4,7 +4,8 @@ from settings import PORT
       import sys
       
       if __name__ == '__main__':
      -	x = sys.argv[1] or ""
      +	x = ''
      +	if len(sys.argv) > 1: x = sys.argv[1]
       	if x == "dev":
       		print("DEV MODE")
       		headers = [
      diff --git a/services/auth/users.py b/services/auth/users.py
      index 89393380..5ff6d4c4 100644
      --- a/services/auth/users.py
      +++ b/services/auth/users.py
      @@ -12,7 +12,6 @@ class UserStorage:
       		self = UserStorage
       		users = session.query(User).\
       			options(selectinload(User.roles), selectinload(User.ratings)).all()
      -			# TODO: add shouts and reactions counters
       		self.users = dict([(user.id, user) for user in users])
       		print('[service.auth] %d users' % len(self.users))
       
      diff --git a/services/stat/reacted.py b/services/stat/reacted.py
      index 95711215..87ceecda 100644
      --- a/services/stat/reacted.py
      +++ b/services/stat/reacted.py
      @@ -48,7 +48,6 @@ class ReactedStorage:
       				self.this_day_reactions[shout] = reaction
       		
       		print('[service.reacted] watching %d shouts' % len(reactions))
      -		# TODO: add reactions ?
       
       	@staticmethod
       	async def get_shout(shout_slug):
      diff --git a/services/stat/viewed.py b/services/stat/viewed.py
      index 46700240..a0ac5956 100644
      --- a/services/stat/viewed.py
      +++ b/services/stat/viewed.py
      @@ -17,7 +17,6 @@ class ViewedByDay(Base):
       class ViewedStorage:
       	viewed = {
       		'shouts': {},
      -		# TODO: ? 'reactions': {},
       		'topics': {} # TODO: get sum views for all shouts in topic
       	}
       	this_day_views = {}
      @@ -43,7 +42,6 @@ class ViewedStorage:
       				self.this_day_views[shout] = view
       		
       		print('[service.viewed] watching %d shouts' % len(views))
      -		# TODO: add reactions ?
       
       	@staticmethod
       	async def get_shout(shout_slug):
      diff --git a/services/zine/shoutauthor.py b/services/zine/shoutauthor.py
      index 7858b143..99787637 100644
      --- a/services/zine/shoutauthor.py
      +++ b/services/zine/shoutauthor.py
      @@ -20,7 +20,8 @@ class ShoutAuthorStorage:
       				self.authors_by_shout[shout].append(user)
       			else:
       				self.authors_by_shout[shout] = [user]
      -		print('[service.shoutauthor] %d shouts ' % len(self.authors_by_shout))
      +		print('[service.shoutauthor] %d authors ' % len(self.authors_by_shout))
      +  # FIXME: [service.shoutauthor] 4251 authors 
       
       	@staticmethod
       	async def get_authors(shout):