Skip to content

Commit

Permalink
add unregistered participants field to project insights to account fo…
Browse files Browse the repository at this point in the history
…r the

new feature in the poll module which allows participating without
account
  • Loading branch information
goapunk committed Nov 4, 2024
1 parent ba0482f commit 3481b63
Show file tree
Hide file tree
Showing 12 changed files with 195 additions and 32 deletions.
31 changes: 31 additions & 0 deletions apps/projects/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
from django.utils import timezone

from adhocracy4.comments.models import Comment
from adhocracy4.polls.models import Answer
from adhocracy4.polls.models import Vote
from adhocracy4.projects.models import Project
from adhocracy4.reports.models import Report


Expand Down Expand Up @@ -41,3 +44,31 @@ def get_num_reported_unread_comments(project):
.filter(num_reports__gt=0)
.count()
)


def get_num_unregistered_participants(project: Project) -> int:
"""Returns the number of unregistered users which participated in project.
Parameters
----------
project : The project to get the number for.
"""

answers = (
Answer.objects.filter(
question__poll__module__project=project, content_id__isnull=False
)
.values_list("content_id", flat=True)
.distinct()
.order_by()
)
votes = (
Vote.objects.filter(
choice__question__poll__module__project=project, content_id__isnull=False
)
.exclude(content_id__in=answers)
.values_list("content_id", flat=True)
.distinct()
.order_by()
)
return len(answers) + len(votes)
3 changes: 3 additions & 0 deletions apps/projects/insights.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from apps.interactiveevents.models import LiveQuestion
from apps.mapideas.models import MapIdea
from apps.projects.helpers import get_all_comments_project
from apps.projects.helpers import get_num_unregistered_participants
from apps.projects.models import ProjectInsight
from apps.topicprio.models import Topic

Expand All @@ -26,6 +27,7 @@ def create_insight(project: Project) -> ProjectInsight:
ideas = Idea.objects.filter(module__in=modules)
map_ideas = MapIdea.objects.filter(module__in=modules)
comments = get_all_comments_project(project=project)
unregistered_participants = get_num_unregistered_participants(project=project)
proposals = Proposal.objects.filter(module__in=modules)
polls = Poll.objects.filter(module__in=modules)
votes = Vote.objects.filter(choice__question__poll__in=polls)
Expand Down Expand Up @@ -66,6 +68,7 @@ def create_insight(project: Project) -> ProjectInsight:
insight, _ = ProjectInsight.objects.get_or_create(project=project)

insight.comments = comments.count()
insight.unregistered_participants = unregistered_participants
insight.ratings = sum(x.count() for x in rating_objects)
insight.written_ideas = sum(x.count() for x in idea_objects)
insight.poll_answers = votes.count() + answers.count()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 4.2.16 on 2024-11-04 15:18

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("a4_candy_projects", "0006_initialize_insights"),
]

operations = [
migrations.AddField(
model_name="projectinsight",
name="unregistered_participants",
field=models.PositiveIntegerField(default=0),
),
]
6 changes: 5 additions & 1 deletion apps/projects/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ class ProjectInsight(base.TimeStampedModel):
Project, related_name="insight", on_delete=models.CASCADE
)
active_participants = models.ManyToManyField(settings.AUTH_USER_MODEL)
unregistered_participants = models.PositiveIntegerField(default=0)
comments = models.PositiveIntegerField(default=0)
ratings = models.PositiveIntegerField(default=0)
written_ideas = models.PositiveIntegerField(default=0)
Expand Down Expand Up @@ -134,7 +135,10 @@ def create_insight_context(insight: ProjectInsight) -> dict:
show_ideas = bool(blueprint_types.intersection({"BS", "IC", "MBS", "MIC", "PB"}))

counts = [
(_("active participants"), insight.active_participants.count()),
(
_("active participants"),
insight.active_participants.count() + insight.unregistered_participants,
),
(_("comments"), insight.comments),
(_("ratings"), insight.ratings),
]
Expand Down
13 changes: 12 additions & 1 deletion apps/projects/signals.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,5 +93,16 @@ def increase_poll_answers_count(sender, instance, created, **kwargs):

insight, _ = ProjectInsight.objects.get_or_create(project=project)
insight.poll_answers += 1
insight.active_participants.add(instance.creator.id)
if instance.creator:
insight.active_participants.add(instance.creator.id)
else:
answers = Answer.objects.filter(
question__poll__module__project=project, content_id=instance.content_id
).count()
votes = Vote.objects.filter(
choice__question__poll__module__project=project,
content_id=instance.content_id,
).count()
if answers + votes == 1:
insight.unregistered_participants += 1
insight.save()
4 changes: 2 additions & 2 deletions apps/userdashboard/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@


class Config(AppConfig):
name = 'apps.userdashboard'
label = 'a4_candy_userdashboard'
name = "apps.userdashboard"
label = "a4_candy_userdashboard"
7 changes: 2 additions & 5 deletions apps/userdashboard/routers.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,8 @@

class ModerationDetailRouterMixin(CustomRouterMixin):

prefix_regex = (
r'userdashboard/moderation/(?P<project_pk>[\d]+)/{prefix}'
)
prefix_regex = r"userdashboard/moderation/(?P<project_pk>[\d]+)/{prefix}"


class ModerationDetailDefaultRouter(ModerationDetailRouterMixin,
routers.DefaultRouter):
class ModerationDetailDefaultRouter(ModerationDetailRouterMixin, routers.DefaultRouter):
pass
13 changes: 7 additions & 6 deletions apps/userdashboard/rules.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@
from adhocracy4.modules.predicates import is_allowed_moderate_project
from apps.users.predicates import is_moderator

rules.add_perm('a4_candy_userdashboard.view_moderation_dashboard',
is_moderator)
rules.add_perm("a4_candy_userdashboard.view_moderation_dashboard", is_moderator)

rules.add_perm('a4_candy_userdashboard.view_moderation_comment',
is_allowed_moderate_project)
rules.add_perm(
"a4_candy_userdashboard.view_moderation_comment", is_allowed_moderate_project
)

rules.add_perm('a4_candy_userdashboard.change_moderation_comment',
is_allowed_moderate_project)
rules.add_perm(
"a4_candy_userdashboard.change_moderation_comment", is_allowed_moderate_project
)
40 changes: 25 additions & 15 deletions apps/userdashboard/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,29 @@
from . import views

urlpatterns = [
path('overview/',
views.UserDashboardOverviewView.as_view(),
name='userdashboard-overview'),
path('moderation/',
views.UserDashboardModerationView.as_view(),
name='userdashboard-moderation'),
path('overview/activities/',
views.UserDashboardActivitiesView.as_view(),
name='userdashboard-activities'),
path('overview/following/',
views.UserDashboardFollowingView.as_view(),
name='userdashboard-following'),
re_path(r'^moderation/detail/(?P<slug>[-\w_]+)/$',
views.UserDashboardModerationDetailView.as_view(),
name='userdashboard-moderation-detail'),
path(
"overview/",
views.UserDashboardOverviewView.as_view(),
name="userdashboard-overview",
),
path(
"moderation/",
views.UserDashboardModerationView.as_view(),
name="userdashboard-moderation",
),
path(
"overview/activities/",
views.UserDashboardActivitiesView.as_view(),
name="userdashboard-activities",
),
path(
"overview/following/",
views.UserDashboardFollowingView.as_view(),
name="userdashboard-following",
),
re_path(
r"^moderation/detail/(?P<slug>[-\w_]+)/$",
views.UserDashboardModerationDetailView.as_view(),
name="userdashboard-moderation-detail",
),
]
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"@fortawesome/fontawesome-free": "5.15.4",
"@maplibre/maplibre-gl-leaflet": "0.0.22",
"@react-leaflet/core": "^2.1.0",
"adhocracy4": "git+https://github.com/liqd/adhocracy4#e03c69ae5ce37155c147d58cc6dca491afa5927f",
"adhocracy4": "git+https://github.com/liqd/adhocracy4#jd-2024-10-poll-allow-unregistered",
"autoprefixer": "10.4.19",
"bootstrap": "5.2.3",
"css-loader": "7.1.2",
Expand Down
2 changes: 1 addition & 1 deletion requirements/base.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# A4
git+https://github.com/liqd/adhocracy4.git@e03c69ae5ce37155c147d58cc6dca491afa5927f#egg=adhocracy4
git+https://github.com/liqd/adhocracy4.git@jd-2024-10-poll-allow-unregistered#egg=adhocracy4

# Additional requirements
brotli==1.1.0
Expand Down
88 changes: 88 additions & 0 deletions tests/projects/test_insights.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import uuid

import pytest

from apps.dashboard.blueprints import blueprints
Expand Down Expand Up @@ -263,3 +265,89 @@ def test_create_insight_for_ideas(
assert insight.ratings == 3
assert insight.comments == 3
assert insight.active_participants.count() == 4


@pytest.mark.django_db
@pytest.mark.parametrize("insight_provider", [create_insight, get_insight])
def test_unregistered_poll_users_are_tracked_as_participants(
project_factory,
module_factory,
user_factory,
poll_factory,
question_factory,
choice_factory,
answer_factory,
vote_factory,
insight_provider,
):
project = project_factory(name="complex_example")
modules = module_factory.create_batch(size=2, project=project)
users = user_factory.create_batch(size=5)
unregistered_users = [uuid.uuid4(), uuid.uuid4()]
poll = poll_factory(module=modules[1])
question = question_factory(poll=poll, is_open=True)

answers = [
answer_factory(question=question, creator=users[0]),
answer_factory(question=question, creator=users[1]),
answer_factory(
question=question, creator=None, content_id=unregistered_users[0]
),
answer_factory(
question=question, creator=None, content_id=unregistered_users[0]
),
]

question = question_factory(poll=poll, multiple_choice=True)
choices = choice_factory.create_batch(size=3, question=question)
votes = [
vote_factory(choice=choices[0], creator=users[3]),
vote_factory(choice=choices[1], creator=users[4]),
vote_factory(choice=choices[2], creator=users[2]),
vote_factory(choice=choices[0], creator=None, content_id=unregistered_users[0]),
vote_factory(choice=choices[1], creator=None, content_id=unregistered_users[1]),
]

insight = insight_provider(project=project)

assert insight.poll_answers == len(answers) + len(votes)
assert insight.active_participants.count() == len(users)
assert insight.unregistered_participants == len(unregistered_users)


@pytest.mark.django_db
@pytest.mark.parametrize("insight_provider", [create_insight, get_insight])
def test_create_insight_contexts_combines_unregistered_users_and_registered_users(
project_factory,
module_factory,
user_factory,
poll_factory,
question_factory,
choice_factory,
answer_factory,
vote_factory,
insight_provider,
):
project = project_factory(name="complex_example")
modules = module_factory.create_batch(size=2, project=project)
users = user_factory.create_batch(size=5)
unregistered_users = [uuid.uuid4(), uuid.uuid4()]
poll = poll_factory(module=modules[1])
question = question_factory(poll=poll, is_open=True)

answer_factory(question=question, creator=users[0]),
answer_factory(question=question, creator=users[1]),
answer_factory(question=question, creator=None, content_id=unregistered_users[0]),
answer_factory(question=question, creator=None, content_id=unregistered_users[0]),

question = question_factory(poll=poll, multiple_choice=True)
choices = choice_factory.create_batch(size=3, question=question)
vote_factory(choice=choices[0], creator=users[3]),
vote_factory(choice=choices[1], creator=users[4]),
vote_factory(choice=choices[2], creator=users[2]),
vote_factory(choice=choices[0], creator=None, content_id=unregistered_users[0]),
vote_factory(choice=choices[1], creator=None, content_id=unregistered_users[1]),

insight = insight_provider(project=project)
context = create_insight_context(insight)
assert context["counts"][0][1] == len(users) + len(unregistered_users)

0 comments on commit 3481b63

Please sign in to comment.