Example #1
0
class Conversations(SavannahFilterView):
    def __init__(self, request, community_id):
        super().__init__(request, community_id)
        self.active_tab = "conversations"
        self.charts = set()

        self._membersChart = None
        self._channelsChart = None
        self._tagsChart = None
        self._rolesChart = None
        self._responseTimes = None

        self.RESULTS_PER_PAGE = 25

        try:
            self.page = int(request.GET.get('page', 1))
        except:
            self.page = 1

        if 'conversation_search' in request.GET:
            self.conversation_search = request.GET.get('conversation_search',
                                                       "").lower()
        else:
            self.conversation_search = None
        self.result_count = 0

    @property
    def all_conversations(self):
        conversations = Conversation.objects.filter(
            channel__source__community=self.community)
        conversations = conversations.filter(timestamp__gte=self.rangestart,
                                             timestamp__lte=self.rangeend)
        if self.tag:
            conversations = conversations.filter(tags=self.tag)

        if self.member_tag:
            conversations = conversations.filter(speaker__tags=self.member_tag)

        if self.role:
            conversations = conversations.filter(speaker__role=self.role)

        if self.conversation_search:
            conversations = conversations.filter(
                content__icontains=self.conversation_search)

        self.result_count = conversations.count()
        conversations = conversations.select_related(
            'channel', 'channel__source',
            'speaker').prefetch_related('tags').order_by('-timestamp')
        start = (self.page - 1) * self.RESULTS_PER_PAGE
        return conversations[start:start + self.RESULTS_PER_PAGE]

    @property
    def has_pages(self):
        return self.result_count > self.RESULTS_PER_PAGE

    @property
    def last_page(self):
        pages = int(self.result_count / self.RESULTS_PER_PAGE) + 1
        return pages

    @property
    def page_links(self):
        pages = int(self.result_count / self.RESULTS_PER_PAGE) + 1
        offset = 1
        if self.page > 5:
            offset = self.page - 5
        if offset + 9 > pages:
            offset = pages - 9
        if offset < 1:
            offset = 1
        return [page + offset for page in range(min(10, pages))]

    def getResponseTimes(self):
        if not self._responseTimes:
            replies = Conversation.objects.filter(
                speaker__community_id=self.community,
                thread_start__isnull=True,
                timestamp__gte=self.rangestart,
                timestamp__lte=self.rangeend)
            if self.tag:
                replies = replies.filter(
                    Q(tags=self.tag) | Q(replies__tags=self.tag))

            if self.member_tag:
                replies = replies.filter(
                    replies__speaker__tags=self.member_tag)

            if self.role:
                replies = replies.filter(replies__speaker__role=self.role)

            if self.conversation_search:
                replies = replies.filter(
                    Q(content__icontains=self.conversation_search)
                    | Q(replies__content__icontains=self.conversation_search))

            replies = replies.annotate(
                first_response=Min('replies__timestamp'))
            replies = replies.filter(first_response__isnull=False,
                                     first_response__gt=F('timestamp'))
            response_time = ExpressionWrapper(
                F('first_response') - F('timestamp'),
                output_field=fields.DurationField())
            replies = replies.annotate(response_time=response_time)
            self._responseTimes = replies.aggregate(avg=Avg('response_time'),
                                                    min=Min('response_time'),
                                                    max=Max('response_time'))
        return self._responseTimes

    @property
    def min_response_time(self):
        response_times = self.getResponseTimes()
        if response_times['min'] is None:
            return None
        return response_times['min'] - datetime.timedelta(
            microseconds=response_times['min'].microseconds)

    @property
    def max_response_time(self):
        response_times = self.getResponseTimes()
        if response_times['max'] is None:
            return None
        return response_times['max'] - datetime.timedelta(
            microseconds=response_times['max'].microseconds)

    @property
    def avg_response_time(self):
        response_times = self.getResponseTimes()
        if response_times['avg'] is None:
            return None
        return response_times['avg'] - datetime.timedelta(
            microseconds=response_times['avg'].microseconds)

    def getConversationsChart(self):
        if not self._membersChart:
            months = list()
            counts = dict()

            conversations = Conversation.objects.filter(
                channel__source__community=self.community,
                timestamp__gte=self.rangestart,
                timestamp__lte=self.rangeend)
            if self.tag:
                conversations = conversations.filter(tags=self.tag)

            if self.member_tag:
                conversations = conversations.filter(
                    speaker__tags=self.member_tag)

            if self.role:
                conversations = conversations.filter(speaker__role=self.role)

            if self.conversation_search:
                conversations = conversations.filter(
                    content__icontains=self.conversation_search)
            conversations = conversations.order_by("timestamp")

            for m in conversations:
                month = self.trunc_date(m.timestamp)
                if month not in months:
                    months.append(month)
                if month not in counts:
                    counts[month] = 1
                else:
                    counts[month] += 1
            self._membersChart = (months, counts)
        return self._membersChart

    @property
    def conversations_chart_months(self):
        (months, counts) = self.getConversationsChart()
        return self.timespan_chart_keys(months)

    @property
    def conversations_chart_counts(self):
        (months, counts) = self.getConversationsChart()
        return [
            counts.get(month, 0) for month in self.timespan_chart_keys(months)
        ]

    def channelsChart(self):
        if not self._channelsChart:
            channels = list()
            counts = dict()
            channels = Channel.objects.filter(source__community=self.community)
            convo_filter = Q(conversation__timestamp__gte=self.rangestart,
                             conversation__timestamp__lte=self.rangeend)
            if self.tag:
                convo_filter = convo_filter & Q(conversation__tags=self.tag)
            if self.member_tag:
                convo_filter = convo_filter & Q(
                    conversation__speaker__tags=self.member_tag)
            if self.role:
                convo_filter = convo_filter & Q(
                    conversation__speaker__role=self.role)
            if self.conversation_search:
                convo_filter = convo_filter & Q(
                    conversation__content__icontains=self.conversation_search)

            channels = channels.annotate(
                conversation_count=Count('conversation', filter=convo_filter))

            channels = channels.annotate(
                source_connector=F('source__connector'),
                source_icon=F('source__icon_name'),
                color=F('tag__color'))
            for c in channels.order_by("-conversation_count"):
                if c.conversation_count == 0:
                    continue
                counts[c] = c.conversation_count
            self._channelsChart = PieChart("channelsChart",
                                           title="Conversations by Channel",
                                           limit=8)
            for channel, count in sorted(counts.items(),
                                         key=operator.itemgetter(1),
                                         reverse=True):
                self._channelsChart.add(
                    "%s (%s)" %
                    (channel.name,
                     ConnectionManager.display_name(channel.source_connector)),
                    count, channel.color)
        self.charts.add(self._channelsChart)
        return self._channelsChart

    def tagsChart(self):
        if not self._tagsChart:
            counts = dict()
            tags = Tag.objects.filter(community=self.community)
            convo_filter = Q(conversation__timestamp__gte=self.rangestart,
                             conversation__timestamp__lte=self.rangeend)
            if self.tag:
                convo_filter = convo_filter & Q(conversation__tags=self.tag)
            if self.member_tag:
                convo_filter = convo_filter & Q(
                    conversation__speaker__tags=self.member_tag)
            if self.role:
                convo_filter = convo_filter & Q(
                    conversation__speaker__role=self.role)
            if self.conversation_search:
                convo_filter = convo_filter & Q(
                    conversation__content__icontains=self.conversation_search)

            tags = tags.annotate(
                conversation_count=Count('conversation', filter=convo_filter))

            for t in tags:
                counts[t] = t.conversation_count
            self._tagsChart = PieChart("tagsChart",
                                       title="Conversations by Tag",
                                       limit=12)
            for tag, count in sorted(counts.items(),
                                     key=operator.itemgetter(1),
                                     reverse=True):
                if count > 0:
                    self._tagsChart.add(tag.name, count, tag.color)
        self.charts.add(self._tagsChart)
        return self._tagsChart

    def rolesChart(self):
        if not self._rolesChart:
            counts = dict()
            colors = {
                Member.COMMUNITY: savannah_colors.MEMBER.COMMUNITY,
                Member.STAFF: savannah_colors.MEMBER.STAFF,
                Member.BOT: savannah_colors.MEMBER.BOT
            }
            members = Member.objects.filter(community=self.community)
            convo_filter = Q(speaker_in__timestamp__gte=self.rangestart,
                             speaker_in__timestamp__lte=self.rangeend)
            if self.tag:
                convo_filter = convo_filter & Q(speaker_in__tags=self.tag)
            if self.member_tag:
                members = members.filter(tags=self.member_tag)
            if self.role:
                members = members.filter(role=self.role)
            if self.conversation_search:
                convo_filter = convo_filter & Q(
                    speaker_in__content__icontains=self.conversation_search)
            #convo_filter = convo_filter & Q(speaker_in__speaker_id=F('id'))
            members = members.annotate(conversation_count=Count(
                'speaker_in', filter=convo_filter)).filter(
                    conversation_count__gt=0)

            for m in members:
                if m.role in counts:
                    counts[m.role] += m.conversation_count
                else:
                    counts[m.role] = m.conversation_count
            self._rolesChart = PieChart("rolesChart",
                                        title="Conversations by Role")
            for role, count in sorted(counts.items(),
                                      key=operator.itemgetter(1),
                                      reverse=True):
                self._rolesChart.add(Member.ROLE_NAME[role], count,
                                     colors[role])
        self.charts.add(self._rolesChart)
        return self._rolesChart

    @property
    def most_active(self):
        activity_counts = dict()
        members = Member.objects.filter(community=self.community)
        convo_filter = Q(speaker_in__timestamp__gte=self.rangestart,
                         speaker_in__timestamp__lte=self.rangeend)
        if self.tag:
            convo_filter = convo_filter & Q(speaker_in__tags=self.tag)
        if self.member_tag:
            members = members.filter(tags=self.member_tag)
        if self.role:
            members = members.filter(role=self.role)
        if self.conversation_search:
            convo_filter = convo_filter & Q(
                speaker_in__content__icontains=self.conversation_search)

        members = members.annotate(conversation_count=Count(
            'speaker_in', filter=convo_filter)).filter(
                conversation_count__gt=0).prefetch_related('tags').order_by(
                    '-conversation_count')
        return members[:20]

    @property
    def most_connected(self):
        if self.conversation_search:
            return []
        members = Member.objects.filter(community=self.community)
        connection_filter = Q(
            memberconnection__last_connected__gte=self.rangestart,
            memberconnection__last_connected__lte=self.rangeend)
        if self.tag:
            connection_filter = connection_filter & Q(
                connections__tags=self.tag)
        if self.member_tag:
            members = members.filter(tags=self.member_tag)
        if self.role:
            members = members.filter(role=self.role)
        members = members.annotate(connection_count=Count(
            'connections', filter=connection_filter)).filter(
                connection_count__gt=0).prefetch_related('tags').order_by(
                    '-connection_count')
        return members[:20]

    @login_required
    def as_view(request, community_id):
        view = Conversations(request, community_id)
        return render(request, 'savannahv2/conversations.html', view.context)
Example #2
0
class MemberProfile(SavannahView):
    def __init__(self, request, member_id):
        self.member = get_object_or_404(Member, id=member_id)
        super().__init__(request, self.member.community_id)
        self.active_tab = "members"

        self.RESULTS_PER_PAGE = 100
        self._engagementChart = None
        self._channelsChart = None
        try:
            self.page = int(request.GET.get('page', 1))
        except:
            self.page = 1
        self.tag = None
        self.member_tage = None
        self.role = None
        
    @property
    def is_watched(self):
        return MemberWatch.objects.filter(manager=self.request.user, member=self.member).count() > 0
        
    @property 
    def member_levels(self):
        return MemberLevel.objects.filter(community=self.community, member=self.member).order_by('-project__default_project', '-level', 'timestamp')

    def open_tasks(self):
        return Task.objects.filter(stakeholders=self.member, done__isnull=True)

    @property
    def all_gifts(self):
        return Gift.objects.filter(community=self.community, member=self.member)

    @property
    def all_conversations(self):
        conversations = Conversation.objects.filter(channel__source__community=self.member.community, speaker=self.member)
        if self.tag:
            conversations = conversations.filter(tags=self.tag)
        if self.role:
            conversations = conversations.filter(participants__role=self.role)

        conversations = conversations.annotate(tag_count=Count('tags'), channel_name=F('channel__name'), channel_icon=F('channel__source__icon_name')).order_by('-timestamp')
        return conversations[:20]

    @property
    def all_contributions(self):
        contributions = Contribution.objects.filter(community=self.member.community, author=self.member).annotate(tag_count=Count('tags'), channel_name=F('channel__name'), channel_icon=F('channel__source__icon_name')).order_by('-timestamp')
        if self.tag:
            contributions = contributions.filter(tags=self.tag)
        if self.role:
            contributions = contributions.filter(author__role=self.role)

        contributions = contributions.annotate(tag_count=Count('tags'), channel_name=F('channel__name'), channel_icon=F('channel__source__icon_name')).order_by('-timestamp')
        return contributions[:10]

    def getEngagementChart(self):
        if not self._engagementChart:
            conversations_counts = dict()
            activity_counts = dict()
            conversations = conversations = Conversation.objects.filter(channel__source__community=self.member.community, speaker=self.member, timestamp__gte=datetime.datetime.now() - datetime.timedelta(days=90))
            if self.tag:
                conversations = conversations.filter(tags=self.tag)
            if self.role:
                conversations = conversations.filter(speaker__role=self.role)

            conversations = conversations.order_by("timestamp")
            for c in conversations:
                month = str(c.timestamp)[:10]
                if month not in conversations_counts:
                    conversations_counts[month] = 1
                else:
                    conversations_counts[month] += 1

            activity = Contribution.objects.filter(community=self.member.community, author=self.member, timestamp__gte=datetime.datetime.now() - datetime.timedelta(days=90))
            if self.tag:
                activity = activity.filter(tags=self.tag)
            if self.role:
                activity = activity.filter(author__role=self.role)

            activity = activity.order_by("timestamp")

            for a in activity:
                month = str(a.timestamp)[:10]
                if month not in activity_counts:
                    activity_counts[month] = 1
                else:
                    activity_counts[month] += 1
            self._engagementChart = (conversations_counts, activity_counts)
        return self._engagementChart
        
    @property
    def engagement_chart_months(self):
        base = datetime.datetime.today()
        date_list = [base - datetime.timedelta(days=x) for x in range(90)]
        date_list.reverse()
        return [str(day)[:10] for day in date_list]

    @property
    def engagement_chart_conversations(self):
        (conversations_counts, activity_counts) = self.getEngagementChart()
        base = datetime.datetime.today()
        date_list = [base - datetime.timedelta(days=x) for x in range(90)]
        date_list.reverse()
        return [conversations_counts.get(str(day)[:10], 0) for day in date_list]

    @property
    def engagement_chart_activities(self):
        (conversations_counts, activity_counts) = self.getEngagementChart()
        base = datetime.datetime.today()
        date_list = [base - datetime.timedelta(days=x) for x in range(90)]
        date_list.reverse()
        return [activity_counts.get(str(day)[:10], 0) for day in date_list]

    def channels_chart(self):
        channel_names = dict()
        if not self._channelsChart:
            channels = list()
            counts = dict()
            from_colors = ['4e73df', '1cc88a', '36b9cc', '7dc5fe', 'cceecc']
            next_color = 0
            channels = Channel.objects.filter(source__community=self.member.community)
            convo_filter = Q(conversation__speaker=self.member, conversation__timestamp__gte=datetime.datetime.now() - datetime.timedelta(days=180))
            if self.tag:
                convo_filter = convo_filter & Q(conversation__tags=self.tag)
            if self.role:
                convo_filter = convo_filter & Q(conversation__speaker__role=self.role)

            channels = channels.annotate(conversation_count=Count('conversation', filter=convo_filter))
            channels = channels.annotate(source_icon=F('source__icon_name'), source_connector=F('source__connector'), color=F('tag__color'))
            for c in channels:
                if c.conversation_count == 0:
                    continue
                counts[c] = c.conversation_count 

            self._channelsChart = PieChart("channelsChart", title="Conversations by Channel", limit=8)
            for channel, count in sorted(counts.items(), key=operator.itemgetter(1), reverse=True):
                self._channelsChart.add("%s (%s)" % (channel.name, ConnectionManager.display_name(channel.source_connector)), count, channel.color)
        self.charts.add(self._channelsChart)
        return self._channelsChart

    @property
    def channel_names(self):
        chart = self.getChannelsChart()
        return str([channel[0] for channel in chart])

    @property
    def channel_counts(self):
        chart = self.getChannelsChart()
        return [channel[1] for channel in chart]

    @property
    def channel_colors(self):
        chart = self.getChannelsChart()
        return ['#'+channel[2] for channel in chart]

    @login_required
    def as_view(request, member_id):
        view = MemberProfile(request, member_id)
        if request.method == 'POST':
            if 'delete_note' in request.POST:
                note = get_object_or_404(Note, id=request.POST.get('delete_note'))
                context = view.context
                context.update({
                    'object_type':"Note", 
                    'object_name': str(note), 
                    'object_id': note.id,
                })
                return render(request, "savannahv2/delete_confirm.html", context)
            elif 'delete_confirm' in request.POST:
                note = get_object_or_404(Note, id=request.POST.get('object_id'))
                note.delete()
                messages.success(request, "Note deleted")

                return redirect('member_profile', member_id=member_id)
        return render(request, 'savannahv2/member_profile.html', view.context)
Example #3
0
class Connections(SavannahFilterView):
    def __init__(self, request, community_id, json=False):
        self._is_json = json
        super().__init__(request, community_id)
        self.active_tab = "connections"
        self._connectionsChart = None
        self._sourcesChart = None

    def _add_sources_message(self):
        if self._is_json:
            pass
        else:
            super()._add_sources_message()

    def getConnectionsChart(self):
        if not self._connectionsChart:
            months = list()
            counts = dict()

            connections = MemberConnection.objects.filter(
                via__community=self.community)
            if self.member_tag:
                connections = connections.filter(
                    Q(from_member__tags=self.member_tag)
                    | Q(to_member__tags=self.member_tag))
            if self.role:
                connections = connections.filter(
                    Q(from_member__role=self.role)
                    & Q(from_member__role=self.role))

            counts['prev'] = connections.filter(
                first_connected__lt=self.rangestart).count()

            connections = connections.filter(
                first_connected__gte=self.rangestart,
                first_connected__lte=self.rangeend)
            for c in connections:
                month = self.trunc_date(c.first_connected)
                if month not in months:
                    months.append(month)
                if month not in counts:
                    counts[month] = 1
                else:
                    counts[month] += 1
            self._connectionsChart = (months, counts)
        return self._connectionsChart

    @property
    def connections_chart_months(self):
        (months, counts) = self.getConnectionsChart()
        return self.timespan_chart_keys(months)

    @property
    def connections_chart_counts(self):
        (months, counts) = self.getConnectionsChart()
        cumulative_counts = []
        previous = counts['prev']
        for month in self.timespan_chart_keys(months):
            cumulative_counts.append(counts.get(month, 0) + previous)
            previous = cumulative_counts[-1]
        return cumulative_counts

    def sources_chart(self):
        channel_names = dict()
        if not self._sourcesChart:
            counts = dict()
            connections = MemberConnection.objects.filter(
                via__community=self.community,
                first_connected__gte=self.rangestart,
                first_connected__lte=self.rangeend)
            if self.member_tag:
                connections = connections.filter(
                    Q(from_member__tags=self.member_tag)
                    | Q(to_member__tags=self.member_tag))
            if self.role:
                connections = connections.filter(
                    Q(from_member__role=self.role)
                    & Q(from_member__role=self.role))

            connections = connections.annotate(
                source_name=F('via__name'),
                source_connector=F('via__connector'),
                source_icon=F('via__icon_name'))
            for c in connections:
                source_name = "%s (%s)" % (c.source_name,
                                           ConnectionManager.display_name(
                                               c.source_connector))
                if source_name not in counts:
                    counts[source_name] = 1
                else:
                    counts[source_name] += 1

            self._sourcesChart = PieChart("sourcesChart",
                                          title="Connections by Source",
                                          limit=8)
            for source_name, count in sorted(counts.items(),
                                             key=operator.itemgetter(1),
                                             reverse=True):
                self._sourcesChart.add(source_name, count)
        self.charts.add(self._sourcesChart)
        return self._sourcesChart

    @login_required
    def as_view(request, community_id):
        view = Connections(request, community_id)
        return render(request, 'savannahv2/connections.html', view.context)

    @login_required
    def as_json(request, community_id):
        view = Connections(request, community_id, json=True)
        nodes = list()
        links = list()
        member_map = dict()
        connection_counts = dict()
        connected = set()
        if view.timespan <= 31:
            timespan = view.timespan
        else:
            timespan = 30

        connections = MemberConnection.objects.filter(
            from_member__community=view.community,
            last_connected__gte=view.rangeend -
            datetime.timedelta(days=timespan),
            last_connected__lte=view.rangeend)
        if view.member_tag:
            connections = connections.filter(
                Q(to_member__tags=view.member_tag)
                | Q(from_member__tags=view.member_tag))
        if view.role:
            connections = connections.filter(
                Q(to_member__role=view.role) & Q(from_member__role=view.role))
        connections = connections.select_related(
            'from_member').prefetch_related('from_member__tags').order_by(
                '-last_connected')

        for connection in connections:
            if connection.from_member_id != connection.to_member_id:
                if connection.to_member_id > connection.from_member_id:
                    connection_id = str(connection.to_member_id) + ":" + str(
                        connection.from_member_id)
                else:
                    connection_id = str(connection.from_member_id) + ":" + str(
                        connection.to_member_id)

                links.append({
                    "source": connection.from_member_id,
                    "target": connection.to_member_id
                })
                member_map[connection.from_member_id] = connection.from_member

                if not connection_id in connected:
                    connected.add(connection_id)

                    if connection.from_member_id not in connection_counts:
                        connection_counts[connection.from_member_id] = 1
                    else:
                        connection_counts[connection.from_member_id] += 1

                    if len(connected) >= 100000:
                        break

        for member_id, member in member_map.items():
            tag_color = None
            tags = member.tags.all()
            if len(tags) > 0:
                tag_color = tags[0].color
            if tag_color is None and member.role == Member.BOT:
                tag_color = "aeaeae"
            elif tag_color is None and member.role == Member.STAFF:
                tag_color = "36b9cc"
            if tag_color is None:
                tag_color = "1f77b4"

            nodes.append({
                "id": member_id,
                "name": member.name,
                "color": tag_color,
                "connections": connection_counts.get(member_id, 0)
            })

        return JsonResponse({"nodes": nodes, "links": links})
Example #4
0
class Members(SavannahFilterView):
    def __init__(self, request, community_id):
        super().__init__(request, community_id)
        self.active_tab = "members"
        self._membersChart = None
        self._tagsChart = None
        self._sourcesChart = None
    
    @property
    def all_members(self):
        members = Member.objects.filter(community=self.community)
        if self.member_tag:
            members =members.filter(tags=self.member_tag)
        if self.role:
            members =members.filter(role=self.role)
        members = members.annotate(note_count=Count('note'), tag_count=Count('tags'))
        return members

    @property
    def new_members(self):
        members = Member.objects.filter(community=self.community)
        if self.member_tag:
            members = members.filter(tags=self.member_tag)
        if self.role:
            members = members.filter(role=self.role)
        members = members.filter(first_seen__gte=self.rangestart, first_seen__lte=self.rangeend)
        return members.order_by("-first_seen")[:10]

    @property
    def recently_active(self):
        members = Member.objects.filter(community=self.community)
        if self.member_tag:
            members = members.filter(tags=self.member_tag)
        if self.role:
            members = members.filter(role=self.role)
            
        members = members.annotate(last_active=Max('speaker_in__timestamp', filter=Q(speaker_in__timestamp__isnull=False)))
        members = members.filter(last_active__gte=self.rangestart, last_active__lte=self.rangeend)
        actives = dict()
        for m in members:
            if m.last_active is not None:
                actives[m] = m.last_active
        recently_active = [(member, tstamp) for member, tstamp in sorted(actives.items(), key=operator.itemgetter(1), reverse=True)]
        
        return recently_active[:10]

    def getMembersChart(self):
        if not self._membersChart:
            months = list()
            counts = dict()
            monthly_active = dict()
            total = 0
            members = Member.objects.filter(community=self.community)
            if self.member_tag:
                members = members.filter(tags=self.member_tag)
            if self.role:
                members = members.filter(role=self.role)

            seen = members.annotate(month=Trunc('first_seen', self.trunc_span)).values('month').annotate(member_count=Count('id', distinct=True)).order_by('month')
            for m in seen:
                total += 1
                month = self.trunc_date(m['month'])

                if month not in months:
                    months.append(month)
                counts[month] = m['member_count']

            active = members.annotate(month=Trunc('speaker_in__timestamp', self.trunc_span)).values('month').annotate(member_count=Count('id', distinct=True)).order_by('month')
            for a in active:
                if a['month'] is not None:
                    month = self.trunc_date(a['month'])

                    if month not in months:
                        months.append(month)
                    monthly_active[month] = a['member_count']
            self._membersChart = (sorted(months), counts, monthly_active)
        return self._membersChart

    @property
    def members_chart_months(self):
        (months, counts, monthly_active) = self.getMembersChart()
        return self.timespan_chart_keys(months)

    @property
    def members_chart_counts(self):
        (months, counts, monthly_active) = self.getMembersChart()
        return [counts.get(month, 0) for month in self.timespan_chart_keys(months)]

    @property
    def members_chart_monthly_active(self):
        (months, counts, monthly_active) = self.getMembersChart()
        return [monthly_active.get(month, 0) for month in self.timespan_chart_keys(months)]

    def sources_chart(self):
        if not self._sourcesChart:
            counts = dict()
            other_count = 0
            identity_filter = Q(contact__member__first_seen__gte=self.rangestart, contact__member__last_seen__lte=self.rangeend)
            if self.member_tag:
                identity_filter = identity_filter & Q(contact__member__tags=self.member_tag)
            if self.role:
                identity_filter = identity_filter & Q(contact__member__role=self.role)
            sources = Source.objects.filter(community=self.community).annotate(identity_count=Count('contact', filter=identity_filter))
            for source in sources:
                if source.identity_count == 0:
                    continue
                counts[source] = source.identity_count

            self._sourcesChart = PieChart("sourcesChart", title="Member Sources", limit=8)
            for source, count in sorted(counts.items(), key=operator.itemgetter(1), reverse=True):
                self._sourcesChart.add("%s (%s)" % (source.name, ConnectionManager.display_name(source.connector)), count)
        self.charts.add(self._sourcesChart)
        return self._sourcesChart

    @login_required
    def as_view(request, community_id):
        members = Members(request, community_id)
        return render(request, 'savannahv2/members.html', members.context)
Example #5
0
class Contributions(SavannahFilterView):
    def __init__(self, request, community_id):
        super().__init__(request, community_id)
        self.active_tab = "contributions"
        self._membersChart = None
        self._channelsChart = None

        self.RESULTS_PER_PAGE = 25

        try:
            self.page = int(request.GET.get('page', 1))
        except:
            self.page = 1

        if 'search' in request.GET:
            self.search = request.GET.get('search', "").lower()
        else:
            self.search = None
        self.result_count = 0

    @property
    def all_contributions(self):
        contributions = Contribution.objects.filter(community=self.community)
        contributions = contributions.filter(timestamp__gte=self.rangestart,
                                             timestamp__lte=self.rangeend)
        if self.tag:
            contributions = contributions.filter(tags=self.tag)

        if self.member_tag:
            contributions = contributions.filter(author__tags=self.member_tag)

        if self.role:
            contributions = contributions.filter(author__role=self.role)

        contributions = contributions.annotate(
            author_name=F('author__name'),
            channel_name=F('channel__name'),
            source_name=F('contribution_type__source__name'),
            source_icon=F('contribution_type__source__icon_name'
                          )).prefetch_related('tags').order_by('-timestamp')
        self.result_count = contributions.count()
        start = (self.page - 1) * self.RESULTS_PER_PAGE
        return contributions[start:start + self.RESULTS_PER_PAGE]

    @property
    def has_pages(self):
        return self.result_count > self.RESULTS_PER_PAGE

    @property
    def last_page(self):
        pages = int(self.result_count / self.RESULTS_PER_PAGE) + 1
        return pages

    @property
    def page_links(self):
        pages = int(self.result_count / self.RESULTS_PER_PAGE) + 1
        offset = 1
        if self.page > 5:
            offset = self.page - 5
        if offset + 9 > pages:
            offset = pages - 9
        if offset < 1:
            offset = 1
        return [page + offset for page in range(min(10, pages))]

    @property
    def new_contributors(self):
        members = Member.objects.filter(community=self.community)
        contrib_filter = None
        if self.tag:
            contrib_filter = Q(contribution__tags=self.tag)
        if self.member_tag:
            members = members.filter(tags=self.member_tag)
        if self.role:
            members = members.filter(role=self.role)

        members = members.annotate(first_contrib=Min('contribution__timestamp',
                                                     filter=contrib_filter))
        members = members.filter(first_contrib__gte=self.rangestart,
                                 first_contrib__lte=self.rangeend)
        members = members.prefetch_related('tags')
        actives = dict()
        for m in members:
            if m.first_contrib is not None:
                actives[m] = m.first_contrib
        recently_active = [(member, tstamp) for member, tstamp in sorted(
            actives.items(), key=operator.itemgetter(1), reverse=True)]

        return recently_active[:10]

    @property
    def recent_contributors(self):
        members = Member.objects.filter(community=self.community)
        contrib_filter = Q(contribution__timestamp__gte=self.rangestart,
                           contribution__timestamp__lte=self.rangeend)
        if self.tag:
            contrib_filter = contrib_filter & Q(contribution__tags=self.tag)
        if self.member_tag:
            members = members.filter(tags=self.member_tag)
        if self.role:
            members = members.filter(role=self.role)

        members = members.annotate(last_active=Max(
            'contribution__timestamp', filter=contrib_filter)).filter(
                last_active__isnull=False).prefetch_related('tags')
        actives = dict()
        for m in members:
            if m.last_active is not None:
                actives[m] = m.last_active
        recently_active = [(member, tstamp) for member, tstamp in sorted(
            actives.items(), key=operator.itemgetter(1), reverse=True)]

        return recently_active[:10]

    @property
    def top_contributors(self):
        activity_counts = dict()
        members = Member.objects.filter(community=self.community)
        contrib_filter = Q(contribution__timestamp__gte=self.rangestart,
                           contribution__timestamp__lte=self.rangeend)
        if self.tag:
            contrib_filter = contrib_filter & Q(contribution__tags=self.tag)
        if self.member_tag:
            members = members.filter(tags=self.member_tag)
        if self.role:
            members = members.filter(role=self.role)

        members = members.annotate(contribution_count=Count(
            'contribution', filter=contrib_filter)).filter(
                contribution_count__gt=0).prefetch_related('tags')
        for m in members:
            if m.contribution_count > 0:
                activity_counts[m] = m.contribution_count
        most_active = [(member, count)
                       for member, count in sorted(activity_counts.items(),
                                                   key=operator.itemgetter(1))]
        most_active.reverse()
        return most_active[:10]

    @property
    def top_supporters(self):
        activity_counts = dict()
        contributor_ids = set()
        contributors = Member.objects.filter(community=self.community)
        contrib_filter = Q(contribution__timestamp__gte=self.rangestart,
                           contribution__timestamp__lte=self.rangeend)
        if self.tag:
            contrib_filter = contrib_filter & Q(contribution__tags=self.tag)
        if self.member_tag:
            contributors = contributors.filter(tags=self.member_tag)
        if self.role:
            contributors = contributors.filter(role=self.role)

        contributors = contributors.annotate(
            contribution_count=Count('contribution', filter=contrib_filter))
        contributors = contributors.filter(
            contribution_count__gt=0).order_by('-contribution_count')
        for c in contributors:
            if c.contribution_count > 0:
                contributor_ids.add(c.id)

        members = Member.objects.filter(community=self.community)
        members = members.annotate(conversation_count=Count(
            'speaker_in',
            filter=Q(speaker_in__participants__in=contributor_ids,
                     speaker_in__timestamp__gte=datetime.datetime.now() -
                     datetime.timedelta(days=30))))
        members = members.order_by('-conversation_count').filter(
            conversation_count__gt=0).prefetch_related('tags')
        for m in members[:10]:
            if m.conversation_count > 0:
                activity_counts[m] = m.conversation_count
        most_active = [(member, count)
                       for member, count in sorted(activity_counts.items(),
                                                   key=operator.itemgetter(1))]
        most_active.reverse()
        return most_active[:10]

    @property
    def top_enablers(self):
        activity_counts = dict()
        contributor_ids = set()
        contributors = Member.objects.filter(community=self.community)
        contrib_filter = Q(contribution__timestamp__gte=self.rangestart,
                           contribution__timestamp__lte=self.rangeend)
        if self.tag:
            contrib_filter = contrib_filter & Q(contribution__tags=self.tag)
        if self.member_tag:
            contributors = contributors.filter(tags=self.member_tag)
        if self.role:
            contributors = contributors.filter(role=self.role)

        contributors = contributors.annotate(contribution_count=Count(
            'contribution', filter=contrib_filter)).filter(
                contribution_count__gt=0)

        for c in contributors:
            if c.contribution_count > 0:
                contributor_ids.add(c.id)

        members = Member.objects.filter(community=self.community)
        members = members.annotate(connection_count=Count(
            'memberconnection__id',
            filter=Q(memberconnection__to_member__in=contributor_ids,
                     memberconnection__first_connected__lte=self.rangeend,
                     memberconnection__last_connected__gte=self.rangestart)))
        members = members.order_by('-connection_count').filter(
            connection_count__gt=0).prefetch_related('tags')
        for m in members:
            if m.connection_count > 0:
                activity_counts[m] = m.connection_count
        most_active = [(member, count)
                       for member, count in sorted(activity_counts.items(),
                                                   key=operator.itemgetter(1))]
        most_active.reverse()
        return most_active[:10]

    def getContributionsChart(self):
        if not self._membersChart:
            months = list()
            counts = dict()

            contributions = Contribution.objects.filter(
                community=self.community,
                timestamp__gte=self.rangestart,
                timestamp__lte=self.rangeend)
            if self.tag:
                contributions = contributions.filter(tags=self.tag)

            if self.member_tag:
                contributions = contributions.filter(
                    author__tags=self.member_tag)
            if self.role:
                contributions = contributions.filter(author__role=self.role)
            contributions = contributions.order_by("timestamp")

            for m in contributions:
                month = self.trunc_date(m.timestamp)
                if month not in months:
                    months.append(month)
                if month not in counts:
                    counts[month] = 1
                else:
                    counts[month] += 1
            self._membersChart = (months, counts)
        return self._membersChart

    @property
    def contributions_chart_months(self):
        (months, counts) = self.getContributionsChart()
        return self.timespan_chart_keys(months)

    @property
    def contributions_chart_counts(self):
        (months, counts) = self.getContributionsChart()
        return [
            counts.get(month, 0) for month in self.timespan_chart_keys(months)
        ]

    def channels_chart(self):
        if not self._channelsChart:
            channels = list()
            counts = dict()

            channels = Channel.objects.filter(source__community=self.community)
            contrib_filter = Q(contribution__timestamp__gte=self.rangestart,
                               contribution__timestamp__lte=self.rangeend)
            if self.tag:
                contrib_filter = contrib_filter & Q(
                    contribution__tags=self.tag)
            if self.member_tag:
                contrib_filter = contrib_filter & Q(
                    contribution__author__tags=self.member_tag)
            if self.role:
                contrib_filter = contrib_filter & Q(
                    contribution__author__role=self.role)

            channels = channels.annotate(contribution_count=Count(
                'contribution', filter=contrib_filter))
            channels = channels.annotate(
                source_icon=F('source__icon_name'),
                source_connector=F('source__connector'),
                color=F('tag__color'))
            for c in channels:
                if c.contribution_count == 0:
                    continue
                counts[c] = c.contribution_count
            self._channelsChart = PieChart("channelsChart",
                                           title="Contributions by Channel",
                                           limit=8)
            for channel, count in sorted(counts.items(),
                                         key=operator.itemgetter(1),
                                         reverse=True):
                self._channelsChart.add(
                    "%s (%s)" %
                    (channel.name,
                     ConnectionManager.display_name(channel.source_connector)),
                    count, channel.color)
        self.charts.add(self._channelsChart)
        return self._channelsChart

    @login_required
    def as_view(request, community_id):
        view = Contributions(request, community_id)
        return render(request, 'savannahv2/contributions.html', view.context)