add
Some checks failed
deploy / deploy (push) Failing after 6s

This commit is contained in:
Untone 2024-01-22 18:42:45 +03:00
parent 7b5330625b
commit 9bd458c47c
2 changed files with 134 additions and 46 deletions

View File

@ -48,16 +48,16 @@ async def create_shout(_, info, inp):
current_time = int(time.time()) current_time = int(time.time())
new_shout = Shout( new_shout = Shout(
**{ **{
"title": inp.get("title"), "title": inp.get("title", ""),
"subtitle": inp.get("subtitle"), "subtitle": inp.get("subtitle", ""),
"lead": inp.get("lead"), "lead": inp.get("lead", ""),
"description": inp.get("description"), "description": inp.get("description", ""),
"body": inp.get("body", ""), "body": inp.get("body", ""),
"layout": inp.get("layout"), "layout": inp.get("layout", "article"),
"created_by": author.id, "created_by": author.id,
"authors": [], "authors": [],
"slug": inp.get("slug") or f"draft-{time.time()}", "slug": inp.get("slug") or f"draft-{time.time()}",
"topics": inp.get("topics"), "topics": inp.get("topics", []),
"visibility": ShoutVisibility.AUTHORS.value, "visibility": ShoutVisibility.AUTHORS.value,
"created_at": current_time, # Set created_at as Unix timestamp "created_at": current_time, # Set created_at as Unix timestamp
} }

View File

@ -1,4 +1,5 @@
import asyncio import asyncio
import threading
from logging import Logger from logging import Logger
import time import time
from datetime import datetime, timedelta, timezone from datetime import datetime, timedelta, timezone
@ -6,13 +7,19 @@ from os import environ
import logging import logging
from gql import Client, gql from gql import Client, gql
from gql.transport.aiohttp import AIOHTTPTransport from gql.transport.aiohttp import AIOHTTPTransport
from graphql import DocumentNode
from orm.shout import Shout, ShoutTopic from orm.shout import Shout, ShoutTopic
from orm.topic import Topic from orm.topic import Topic
from services.db import local_session from services.db import local_session
logging.basicConfig(
logging.basicConfig() format="[%(threadName)-12.12s] [%(levelname)-5.5s] %(message)s",
level=logging.DEBUG,
handlers=[
logging.StreamHandler(),
],
)
logger = logging.getLogger("\t[services.viewed]\t") logger = logging.getLogger("\t[services.viewed]\t")
logger.setLevel(logging.DEBUG) logger.setLevel(logging.DEBUG)
@ -46,8 +53,20 @@ load_pages = gql(
} } """ } } """
) )
schema_str = open("schemas/ackee.graphql").read() create_record_mutation_string = """
createRecord(domainId: $domainId, input: $input) {
payload {
id
}
}
"""
create_record_mutation = gql(f"mutation {{{create_record_mutation_string}}}")
schema_str = open("schemas/stat.graphql").read()
token = environ.get("ACKEE_TOKEN", "") token = environ.get("ACKEE_TOKEN", "")
domain_id = environ.get("ACKEE_DOMAIN_ID", "")
ackee_site = environ.get("ACKEE_SITE", "https://testing.discours.io/")
def create_client(headers=None, schema=None): def create_client(headers=None, schema=None):
@ -62,10 +81,11 @@ class ViewedStorage:
lock = asyncio.Lock() lock = asyncio.Lock()
by_shouts = {} by_shouts = {}
by_topics = {} by_topics = {}
by_reactions = {} by_authors = {}
views = None views = None
pages = None pages = None
domains = None domains = None
facts = None
period = 60 * 60 # every hour period = 60 * 60 # every hour
client: Client | None = None client: Client | None = None
auth_result = None auth_result = None
@ -93,6 +113,7 @@ class ViewedStorage:
try: try:
start = time.time() start = time.time()
self = ViewedStorage self = ViewedStorage
async with self.lock:
if self.client: if self.client:
# Use asyncio.run to execute asynchronous code in the main entry point # Use asyncio.run to execute asynchronous code in the main entry point
self.pages = await asyncio.to_thread(self.client.execute, load_pages) self.pages = await asyncio.to_thread(self.client.execute, load_pages)
@ -108,7 +129,8 @@ class ViewedStorage:
slug = p.split("discours.io/")[-1] slug = p.split("discours.io/")[-1]
shouts[slug] = page["count"] shouts[slug] = page["count"]
for slug in shouts.keys(): for slug in shouts.keys():
await ViewedStorage.increment(slug, shouts[slug]) self.by_shouts[slug] = self.by_shouts.get(slug, 0) + 1
self.update_topics(slug)
logger.info("%d pages collected " % len(shouts.keys())) logger.info("%d pages collected " % len(shouts.keys()))
end = time.time() end = time.time()
@ -116,36 +138,37 @@ class ViewedStorage:
except Exception: except Exception:
import traceback import traceback
traceback.print_exc() traceback.print_exc()
@staticmethod @staticmethod
async def get_facts(): async def get_facts():
self = ViewedStorage self = ViewedStorage
facts = [] self.facts = []
try: try:
if self.client: if self.client:
async with self.lock: async with self.lock:
facts = await self.client.execute(load_facts) self.facts = await asyncio.to_thread(self.client.execute, load_pages)
except Exception as er: except Exception as er:
logger.error(f" - get_facts error: {er}") logger.error(f" - get_facts error: {er}")
return facts or [] return self.facts or []
@staticmethod @staticmethod
async def get_shout(shout_slug): async def get_shout(shout_slug) -> int:
"""getting shout views metric by slug""" """getting shout views metric by slug"""
self = ViewedStorage self = ViewedStorage
async with self.lock: async with self.lock:
return self.by_shouts.get(shout_slug, 0) return self.by_shouts.get(shout_slug, 0)
@staticmethod @staticmethod
async def get_reaction(shout_slug, reaction_id): async def get_shout_media(shout_slug) -> Dict[str, int]:
"""getting reaction views metric by slug""" """getting shout plays metric by slug"""
self = ViewedStorage self = ViewedStorage
async with self.lock: async with self.lock:
return self.by_reactions.get(shout_slug, {}).get(reaction_id, 0) return self.by_shouts.get(shout_slug, 0)
@staticmethod @staticmethod
async def get_topic(topic_slug): async def get_topic(topic_slug) -> int:
"""getting topic views value summed""" """getting topic views value summed"""
self = ViewedStorage self = ViewedStorage
topic_views = 0 topic_views = 0
@ -154,11 +177,22 @@ class ViewedStorage:
topic_views += self.by_topics[topic_slug].get(shout_slug, 0) topic_views += self.by_topics[topic_slug].get(shout_slug, 0)
return topic_views return topic_views
@staticmethod
async def get_authors(author_slug) -> int:
"""getting author views value summed"""
self = ViewedStorage
author_views = 0
async with self.lock:
for shout_slug in self.by_authors.get(author_slug, {}).keys():
author_views += self.by_authors[author_slug].get(shout_slug, 0)
return author_views
@staticmethod @staticmethod
def update_topics(shout_slug): def update_topics(shout_slug):
"""updates topics counters by shout slug""" """updates topics counters by shout slug"""
self = ViewedStorage self = ViewedStorage
with local_session() as session: with local_session() as session:
# grouped by topics
for [_shout_topic, topic] in ( for [_shout_topic, topic] in (
session.query(ShoutTopic, Topic).join(Topic).join(Shout).where(Shout.slug == shout_slug).all() session.query(ShoutTopic, Topic).join(Topic).join(Shout).where(Shout.slug == shout_slug).all()
): ):
@ -166,21 +200,75 @@ class ViewedStorage:
self.by_topics[topic.slug] = {} self.by_topics[topic.slug] = {}
self.by_topics[topic.slug][shout_slug] = self.by_shouts[shout_slug] self.by_topics[topic.slug][shout_slug] = self.by_shouts[shout_slug]
@staticmethod # grouped by authors
async def increment(shout_slug, amount=1, viewer="ackee"): for [_shout_author, author] in (
"""the only way to change views counter""" session.query(ShoutAuthor, Author).join(Author).join(Shout).where(Shout.slug == shout_slug).all()
self = ViewedStorage ):
async with self.lock: if not self.by_authors.get(author.slug):
self.by_shouts[shout_slug] = self.by_shouts.get(shout_slug, 0) + amount self.by_authors[author.slug] = {}
self.update_topics(shout_slug) self.by_authors[author.slug][shout_slug] = self.by_shouts[shout_slug]
@staticmethod @staticmethod
async def increment_reaction(shout_slug, reaction_id, amount=1, viewer="ackee"): async def increment(shout_slug):
"""the only way to change views counter""" """the proper way to change counter"""
resource = ackee_site + shout_slug
self = ViewedStorage self = ViewedStorage
async with self.lock: async with self.lock:
self.by_reactions[shout_slug][reaction_id] = self.by_reactions[shout_slug].get(reaction_id, 0) + amount self.by_shouts[shout_slug] = self.by_shouts.get(shout_slug, 0) + 1
self.update_topics(shout_slug) self.update_topics(shout_slug)
variables = {"domainId": domain_id, "input": {"siteLocation": resource}}
if self.client:
try:
await asyncio.to_thread(self.client.execute, create_record_mutation, variables)
except Exception as e:
logger.error(f"Error during threaded execution: {e}")
@staticmethod
async def increment_amount(shout_slug, amount):
"""the migration way to change counter with batching"""
resource = ackee_site + shout_slug
self = ViewedStorage
gql_string = ""
batch_size = 100
if not isinstance(amount, int):
try:
amount = int(amount)
if not isinstance(amount, int):
amount = 1
except:
pass
self.by_shouts[shout_slug] = self.by_shouts.get(shout_slug, 0) + amount
self.update_topics(shout_slug)
logger.info(f"{int(amount/100) + 1} requests")
for i in range(amount):
alias = f"mutation{i + 1}"
gql_string += f"{alias}: {create_record_mutation_string
.replace('$domainId', f'"{domain_id}"')
.replace('$input', f'{{siteLocation: "{resource}"}}')
}\n"
# Execute the batch every 100 records
if (i + 1) % batch_size == 0 or (i + 1) == amount:
await self.exec(f"mutation {{\n{gql_string}\n}}")
gql_string = "" # Reset the gql_string for the next batch
# Throttle the requests to 3 per second
await asyncio.sleep(1 / 3)
logger.info(f"Incremented {amount} records for shout_slug: {shout_slug}")
@staticmethod
async def exec(gql_string: str):
self = ViewedStorage
async with self.lock:
if self.client:
try:
await asyncio.to_thread(self.client.execute, gql(gql_string))
except Exception as e:
logger.error(f"Error during threaded execution: {e}")
@staticmethod @staticmethod
async def worker(): async def worker():
@ -192,7 +280,7 @@ class ViewedStorage:
while True: while True:
try: try:
logger.info(" - updating views...") logger.info(" - updating records...")
await self.update_pages() await self.update_pages()
failed = 0 failed = 0
except Exception: except Exception: