Skip to content

Commit

Permalink
* askbot/conf/group_settings.py:
Browse files Browse the repository at this point in the history
 - adds setting PER_EMAIL_DOMAIN_GROUPS_ENABLED
 - adds setting PER_EMAIL_DOMAIN_GROUP_DEFAULT_VISIBILITY - visibility of groups created for email domains
   options: 0 - admins, 1 - mods, 2 - members + admins and mods, 3 - public (default)
* askbot/const/__init__.py:
 - adds constants for group visibility, PEP8
* askbot/models/analytics.py:
 - adds models for daily summaries for users and groups
 - models BaseSummary (abstract), DailySummary (abstract), UserDailySummary, GroupDailySummary
* askbot/models/user.py:
 - adds function get_organization_name_from_domain
 - adds visibility field to Askbot Group
* adds management command askbot_create_per_email_domain_groups
* migration 0028:
 - adds UserDailySummary and GroupDailySummary models
* migration 0029:
 - adds visibility field to the Askbot Group model

 todo: add tests for the askbot_create_per_email_domain_groups, Group.objects.get_or_create
  • Loading branch information
evgenyfadeev committed Jun 30, 2024
1 parent 1a12ac8 commit 5f1a856
Show file tree
Hide file tree
Showing 8 changed files with 196 additions and 17 deletions.
24 changes: 23 additions & 1 deletion askbot/conf/group_settings.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
"""Group settings"""
from django.utils.translation import gettext_lazy as _
from livesettings import values as livesettings
from askbot.conf.settings_wrapper import settings
from askbot.conf.super_groups import LOGIN_USERS_COMMUNICATION
from livesettings import values as livesettings
from askbot import const

GROUP_SETTINGS = livesettings.ConfigurationGroup(
'GROUP_SETTINGS',
Expand Down Expand Up @@ -64,3 +65,24 @@ def group_name_update_callback(old_name, new_name):
'"[email protected]"')
)
)

settings.register(
livesettings.BooleanValue(
GROUP_SETTINGS,
'PER_EMAIL_DOMAIN_GROUPS_ENABLED',
default=False,
description=_('Enable per email domain user groups'),
help_text=_('If enabled, groups will be created for each email domain name')
)
)

settings.register(
livesettings.StringValue(
GROUP_SETTINGS,
'PER_EMAIL_DOMAIN_GROUP_DEFAULT_VISIBILITY',
choices=const.GROUP_VISIBILITY_CHOICES,
default=const.GROUP_VISIBILITY_PUBLIC,
description=_('Default visibility of groups created for the email domains'),
help_text=_('Administrators can change the visibility of these groups individually later')
)
)
18 changes: 15 additions & 3 deletions askbot/const/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,11 +179,11 @@
TAG_CHARS = r'\wp{M}+.#-'
TAG_FIRST_CHARS = r'[\wp{M}]'
TAG_FORBIDDEN_FIRST_CHARS = r'#'
TAG_REGEX_BARE = r'%s[%s]+' % (TAG_FIRST_CHARS, TAG_CHARS)
TAG_REGEX = r'^%s$' % TAG_REGEX_BARE
TAG_REGEX_BARE = rf'{TAG_FIRST_CHARS}[{TAG_CHARS}]+'
TAG_REGEX = rf'^{TAG_REGEX_BARE}$'

TAG_STRIP_CHARS = ', '
TAG_SPLIT_REGEX = r'[%s]+' % TAG_STRIP_CHARS
TAG_SPLIT_REGEX = rf'[{TAG_STRIP_CHARS}]+'
TAG_SEP = ',' # has to be valid TAG_SPLIT_REGEX char and MUST NOT be in const.TAG_CHARS
#!!! see const.message_keys.TAG_WRONG_CHARS_MESSAGE

Expand Down Expand Up @@ -649,3 +649,15 @@
"""

PROFILE_WEBSITE_URL_MAX_LENGTH = 200

GROUP_VISIBILITY_ADMINS = 0
GROUP_VISIBILITY_MODS = 1
GROUP_VISIBILITY_MEMBERS = 2
GROUP_VISIBILITY_PUBLIC = 3

GROUP_VISIBILITY_CHOICES = (
(GROUP_VISIBILITY_ADMINS, _('Visible to administrators')),
(GROUP_VISIBILITY_MODS, _('Visible to moderators and administrators')),
(GROUP_VISIBILITY_MEMBERS, _('Visible to own members, moderators and administrators')),
(GROUP_VISIBILITY_PUBLIC, _('Visible to everyone')),
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
"""A management command that creates groups for each email domain in the database."""
from django.core.management.base import BaseCommand
from askbot.conf import settings
from askbot.models import User
from askbot.models.analytics import get_organization_domains
from askbot.models.user import get_organization_name_from_domain
from askbot.utils.console import ProgressBar

class Command(BaseCommand): # pylint: disable=missing-docstring
help = 'Create groups for each email domain in the database.'

def handle(self, *args, **options): # pylint: disable=missing-docstring, unused-argument
"""Obtains a list of unique email domains names.
Creates a group for each domain name, if such group does not exist.
Group visibility is set to the value of settings.PER_EMAIL_DOMAIN_GROUP_DEFAULT_VISIBILITY.
"""
domains = get_organization_domains()
count = len(domains)
message = 'Initializing groups by the email address domain names'
for domain in ProgressBar(domains, count, message):
organization_name = get_organization_name_from_domain(domain)
group = User.objects.get_or_create_group(
organization_name,
visibility=settings.PER_EMAIL_DOMAIN_GROUP_DEFAULT_VISIBILITY
)
print('Group {0} created.'.format(group.name))

51 changes: 51 additions & 0 deletions askbot/migrations/0028_userdailysummary_groupdailysummary.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Generated by Django 4.2.4 on 2024-06-24 21:15

from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('askbot', '0027_populate_analytics_events'),
]

operations = [
migrations.CreateModel(
name='UserDailySummary',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('num_questions', models.PositiveIntegerField()),
('num_answers', models.PositiveIntegerField()),
('num_upvotes', models.PositiveIntegerField()),
('num_downvotes', models.PositiveIntegerField()),
('question_views', models.PositiveIntegerField()),
('time_on_site', models.DurationField()),
('date', models.DateField(db_index=True)),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='GroupDailySummary',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('num_questions', models.PositiveIntegerField()),
('num_answers', models.PositiveIntegerField()),
('num_upvotes', models.PositiveIntegerField()),
('num_downvotes', models.PositiveIntegerField()),
('question_views', models.PositiveIntegerField()),
('time_on_site', models.DurationField()),
('date', models.DateField(db_index=True)),
('num_users', models.PositiveIntegerField()),
('group', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='askbot.group')),
],
options={
'abstract': False,
},
),
]
18 changes: 18 additions & 0 deletions askbot/migrations/0029_group_visibility.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 4.2.4 on 2024-06-24 21:17

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('askbot', '0028_userdailysummary_groupdailysummary'),
]

operations = [
migrations.AddField(
model_name='group',
name='visibility',
field=models.SmallIntegerField(choices=[(0, 'Visible to administrators'), (1, 'Visible to moderators and administrators'), (2, 'Visible to own members, moderators and administrators'), (3, 'Visible to everyone')], default=3),
),
]
39 changes: 38 additions & 1 deletion askbot/models/analytics.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from django.contrib.contenttypes.fields import GenericForeignKey
from django.conf import settings as django_settings
from django.utils.translation import gettext_lazy as _
from askbot.models.user import Group as AskbotGroup

#for convenience, here are the activity types from used in the Activity object
#TYPE_ACTIVITY_ASK_QUESTION = 1
Expand Down Expand Up @@ -118,6 +119,7 @@ def __str__(self):
email = self.user.email # pylint: disable=no-member
return f"Session: {email} {created_at} - {updated_at}"


class Event(models.Model):
"""Analytics event"""
session = models.ForeignKey(Session, on_delete=models.CASCADE)
Expand All @@ -129,4 +131,39 @@ class Event(models.Model):

def __str__(self):
timestamp = self.timestamp.isoformat() # pylint: disable=no-member
return f"Event: {self.event_type_display} {timestamp}"
return f"Event: {self.event_type_display} {timestamp}" # pylint: disable=no-member


class BaseSummary(models.Model):
"""
An abstract model for per-interval summaries.
An interval name is defined in the subclass.
"""
num_questions = models.PositiveIntegerField()
num_answers = models.PositiveIntegerField()
num_upvotes = models.PositiveIntegerField()
num_downvotes = models.PositiveIntegerField()
question_views = models.PositiveIntegerField()
time_on_site = models.DurationField()

class Meta: # pylint: disable=too-few-public-methods, missing-class-docstring
abstract = True


class DailySummary(BaseSummary):
"""An abstract class for daily summaries."""
date = models.DateField(db_index=True)

class Meta: # pylint: disable=too-few-public-methods, missing-class-docstring
abstract = True


class UserDailySummary(DailySummary):
"""User summary for each day with activity."""
user = models.ForeignKey(User, on_delete=models.CASCADE)


class GroupDailySummary(DailySummary):
"""Group summary for each day with activity."""
group = models.ForeignKey(AskbotGroup, on_delete=models.CASCADE)
num_users = models.PositiveIntegerField()
31 changes: 20 additions & 11 deletions askbot/models/user.py
Original file line number Diff line number Diff line change
@@ -1,28 +1,33 @@
import datetime
import logging
import re
from collections import defaultdict
from django.db import models
from django.db.models import Q
from django.db.utils import IntegrityError
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes import fields
from django.contrib.auth.models import User
from django.contrib.auth.models import Group as AuthGroup
from django.core import exceptions
from django.forms import EmailField, URLField
from django.forms import EmailField
from django.utils import translation, timezone
from django.utils.translation import gettext as _
from django.utils.translation import gettext_lazy
from django.utils.html import strip_tags
from askbot import const
from askbot.conf import settings as askbot_settings
from askbot.utils import functions
from askbot.models.base import BaseQuerySetManager
from askbot.models.analytics import Session
from collections import defaultdict
from askbot.models.tag import get_tags_by_names, Tag

PERSONAL_GROUP_NAME_PREFIX = '_personal_'

def get_organization_name_from_domain(domain):
"""Returns organization name from domain.
The organization name is the second level domain name,
sentence-cased.
"""
base_domain = domain.split('.')[-2]
return base_domain.capitalize()

class InvitedModerator(object):
"""Mock user class to represent invited moderators"""
def __init__(self, username, email):
Expand Down Expand Up @@ -606,6 +611,9 @@ class Group(AuthGroup):
can_upload_images = models.BooleanField(default=False)

openness = models.SmallIntegerField(default=CLOSED, choices=OPENNESS_CHOICES)
visibility = models.SmallIntegerField(default=const.GROUP_VISIBILITY_PUBLIC,
choices=const.GROUP_VISIBILITY_CHOICES)

# preapproved email addresses and domain names to auto-join groups
# trick - the field is padded with space and all tokens are space separated
preapproved_emails = models.TextField(
Expand Down Expand Up @@ -710,7 +718,8 @@ def save(self, *args, **kwargs):
super(Group, self).save(*args, **kwargs)


class BulkTagSubscriptionManager(BaseQuerySetManager):
class BulkTagSubscriptionManager(BaseQuerySetManager): # pylint: disable=too-few-public-methods
"""Manager class for the BulkTagSubscription model"""

def create(
self,
Expand All @@ -730,15 +739,13 @@ def create(
tag_name_list = []

if tag_names:
from askbot.models.tag import get_tags_by_names
tags, new_tag_names = get_tags_by_names(tag_names, language_code)
if new_tag_names:
assert(tag_author)

tags_id_list= [tag.id for tag in tags]
tag_name_list = [tag.name for tag in tags]

from askbot.models.tag import Tag
new_tags = Tag.objects.create_in_bulk(
tag_names=new_tag_names,
user=tag_author,
Expand Down Expand Up @@ -771,6 +778,7 @@ def create(


class BulkTagSubscription(models.Model):
"""Subscribes users in bulk to a list of tags"""
date_added = models.DateField(auto_now_add=True)
tags = models.ManyToManyField('Tag')
users = models.ManyToManyField(User)
Expand All @@ -779,9 +787,10 @@ class BulkTagSubscription(models.Model):
objects = BulkTagSubscriptionManager()

def tag_list(self):
return [tag.name for tag in self.tags.all()]
"""Returns list of tag names"""
return [tag.name for tag in self.tags.all()] # pylint: disable=no-member

class Meta:
class Meta: # pylint: disable=too-few-public-methods, missing-docstring
app_label = 'askbot'
ordering = ['-date_added']

5 changes: 4 additions & 1 deletion askbot/tests/test_markup.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,4 +130,7 @@ def test_convert_mixed_text(self):
"""
"""<a href="http://example.com"><div>http://example.com</div></a>
"""
self.assertHTMLEqual(self.conv(text), expected)
import pdb
pdb.set_trace()
converted = self.conv(text)
self.assertHTMLEqual(converted, expected)

0 comments on commit 5f1a856

Please sign in to comment.