From 49ffe3ab752abe17c7d88fc320ee48e3ea900c31 Mon Sep 17 00:00:00 2001
From: Thomas Sileo
Date: Thu, 15 Aug 2019 14:47:41 +0200
Subject: [PATCH] Finish support for multiple answers polls
---
blueprints/api.py | 2 ++
blueprints/tasks.py | 7 ++----
core/activitypub.py | 49 +++++++++++++++++++--------------------
core/inbox.py | 4 +---
templates/new.html | 4 +---
templates/utils.html | 34 +++++++++++++++++++++++++--
utils/template_filters.py | 5 ++++
7 files changed, 67 insertions(+), 38 deletions(-)
diff --git a/blueprints/api.py b/blueprints/api.py
index 45cc598..4b711d1 100644
--- a/blueprints/api.py
+++ b/blueprints/api.py
@@ -493,11 +493,13 @@ def api_new_question() -> _Response:
)
}
of = _user_api_arg("of")
+ print(of)
if of == "anyOf":
choices["anyOf"] = answers
else:
choices["oneOf"] = answers
+ print(choices)
raw_question = dict(
attributedTo=MY_PERSON.id,
cc=list(set(cc)),
diff --git a/blueprints/tasks.py b/blueprints/tasks.py
index ab2d160..f0ae6a6 100644
--- a/blueprints/tasks.py
+++ b/blueprints/tasks.py
@@ -20,7 +20,6 @@ from core.activitypub import SIG_AUTH
from core.activitypub import Box
from core.activitypub import _actor_hash
from core.activitypub import _add_answers_to_question
-from core.activitypub import no_cache
from core.activitypub import post_to_outbox
from core.activitypub import update_cached_actor
from core.db import update_one_activity
@@ -142,8 +141,7 @@ def task_cache_object() -> _Response:
obj = activity.get_object()
# Refetch the object actor (without cache)
- with no_cache():
- obj_actor = ap.fetch_remote_activity(obj.get_actor().id)
+ obj_actor = ap.fetch_remote_activity(obj.get_actor().id, no_cache=True)
cache = {MetaKey.OBJECT: obj.to_dict(embed=True)}
@@ -269,8 +267,7 @@ def task_cache_actor() -> _Response:
app.logger.info(f"activity={activity!r}")
# Reload the actor without caching (in case it got upated)
- with no_cache():
- actor = ap.fetch_remote_activity(activity.get_actor().id)
+ actor = ap.fetch_remote_activity(activity.get_actor().id, no_cache=True)
# Fetch the Open Grah metadata if it's a `Create`
if activity.has_type(ap.ActivityType.CREATE):
diff --git a/core/activitypub.py b/core/activitypub.py
index 8a5367d..221dc69 100644
--- a/core/activitypub.py
+++ b/core/activitypub.py
@@ -2,12 +2,11 @@ import binascii
import hashlib
import logging
import os
-from contextlib import contextmanager
+import time
from datetime import datetime
from datetime import timezone
from typing import Any
from typing import Dict
-from typing import Iterator
from typing import List
from typing import Optional
from urllib.parse import urljoin
@@ -45,22 +44,10 @@ _NewMeta = Dict[str, Any]
SIG_AUTH = HTTPSigAuth(KEY)
-_ACTIVITY_CACHE_ENABLED = True
ACTORS_CACHE = LRUCache(maxsize=256)
MY_PERSON = ap.Person(**ME)
-@contextmanager
-def no_cache() -> Iterator[None]:
- """Context manager for disabling the "DB cache" when fetching AP activities."""
- global _ACTIVITY_CACHE_ENABLED
- _ACTIVITY_CACHE_ENABLED = False
- try:
- yield
- finally:
- _ACTIVITY_CACHE_ENABLED = True
-
-
def _remove_id(doc: ap.ObjectType) -> ap.ObjectType:
"""Helper for removing MongoDB's `_id` field."""
doc = doc.copy()
@@ -177,6 +164,7 @@ def post_to_inbox(activity: ap.BaseActivity) -> None:
return
save(Box.INBOX, activity)
+ time.sleep(1)
logger.info(f"spawning tasks for {activity!r}")
if not activity.has_type([ap.ActivityType.DELETE, ap.ActivityType.UPDATE]):
Tasks.cache_actor(activity.id)
@@ -202,6 +190,7 @@ def post_to_outbox(activity: ap.BaseActivity) -> str:
activity.reset_object_cache()
save(Box.OUTBOX, activity)
+ time.sleep(5)
Tasks.cache_actor(activity.id)
Tasks.finish_post_to_outbox(activity.id)
return activity.id
@@ -361,8 +350,8 @@ class MicroblogPubBackend(Backend):
logger.info(f"dereference {iri} via HTTP")
return super().fetch_iri(iri)
- def fetch_iri(self, iri: str, no_cache=False) -> ap.ObjectType:
- if not no_cache and _ACTIVITY_CACHE_ENABLED:
+ def fetch_iri(self, iri: str, **kwargs: Any) -> ap.ObjectType:
+ if not kwargs.pop("no_cache", False):
# Fetch the activity by checking the local DB first
data = self._fetch_iri(iri)
logger.debug(f"_fetch_iri({iri!r}) == {data!r}")
@@ -397,21 +386,26 @@ class MicroblogPubBackend(Backend):
logger.info("invalid choice")
return
+ # Hash the choice/answer (so we can use it as a key)
+ answer_key = _answer_key(choice)
+
+ is_single_choice = bool(question._data.get("oneOf", []))
+ dup_query = {
+ "activity.object.actor": create.get_actor().id,
+ "meta.answer_to": question.id,
+ **({} if is_single_choice else {"meta.poll_answer_choice": choice}),
+ }
+
+ print(f"dup_q={dup_query}")
# Check for duplicate votes
- if DB.activities.find_one(
- {
- "activity.object.actor": create.get_actor().id,
- "meta.answer_to": question.id,
- }
- ):
+ if DB.activities.find_one(dup_query):
logger.info("duplicate response")
return
# Update the DB
- answer_key = _answer_key(choice)
DB.activities.update_one(
- {"activity.object.id": question.id},
+ {"meta.object_id": question.id},
{
"$inc": {
"meta.question_replies": 1,
@@ -425,6 +419,7 @@ class MicroblogPubBackend(Backend):
{
"$set": {
"meta.answer_to": question.id,
+ "meta.poll_answer_choice": choice,
"meta.stream": False,
"meta.poll_answer": True,
}
@@ -462,7 +457,11 @@ class MicroblogPubBackend(Backend):
# Keep track of our own votes
DB.activities.update_one(
{"activity.object.id": reply.id, "box": "inbox"},
- {"$set": {"meta.voted_for": create.get_object().name}},
+ {
+ "$set": {
+ f"meta.poll_answers_sent.{_answer_key(create.get_object().name)}": True
+ }
+ },
)
return None
diff --git a/core/inbox.py b/core/inbox.py
index 1d948c7..850a807 100644
--- a/core/inbox.py
+++ b/core/inbox.py
@@ -8,7 +8,6 @@ from little_boxes.errors import NotAnActivityError
import config
from core.activitypub import _answer_key
-from core.activitypub import no_cache
from core.activitypub import post_to_outbox
from core.activitypub import update_cached_actor
from core.db import DB
@@ -93,8 +92,7 @@ def _update_process_inbox(update: ap.Update, new_meta: _NewMeta) -> None:
)
elif obj.has_type(ap.ACTOR_TYPES):
- with no_cache():
- actor = ap.fetch_remote_activity(obj.get_actor().id)
+ actor = ap.fetch_remote_activity(obj.get_actor().id, no_cache=True)
update_cached_actor(actor)
else:
diff --git a/templates/new.html b/templates/new.html
index 0fa98fc..ce85c02 100644
--- a/templates/new.html
+++ b/templates/new.html
@@ -49,12 +49,10 @@
-
-
+
{% for i in range(4) %}
diff --git a/templates/utils.html b/templates/utils.html
index b8c106b..0fccbf6 100644
--- a/templates/utils.html
+++ b/templates/utils.html
@@ -88,7 +88,7 @@
{% set pct = cnt * 100.0 / total_votes %}
{% endif %}
- {% if session.logged_in and not meta.voted_for and not (real_end_time | gtnow) and not (obj.id | is_from_outbox) %}
+ {% if session.logged_in and not meta.poll_answers_sent and not (real_end_time | gtnow) and not (obj.id | is_from_outbox) %}
{% endfor %}
+ {% if obj.anyOf %}
+
+ {% for anyOf in obj.anyOf %}
+ {% set pct = 0 %}
+ {% if total_votes > 0 %}
+ {% set cnt = anyOf.name | get_answer_count(obj, meta) %}
+ {% set pct = cnt * 100.0 / total_votes %}
+ {% endif %}
+
+ {% set already_voted = anyOf.name | poll_answer_key in meta.poll_answers_sent %}
+ {% if session.logged_in and not already_voted and not (real_end_time | gtnow) and not (obj.id | is_from_outbox) %}
+
+ {% elif session.logged_in and already_voted %}
+
+ {% endif %}
+
+
+ {{ '%0.0f'| format(pct) }}%
+ {{ anyOf.name }} {% if anyOf.name | poll_answer_key in meta.poll_answers_sent %}(your vote){% endif %}
+
+