<commit_before>from django.core.urlresolvers import reverse
from django.http import Http404
from django.shortcuts import redirect, render
from django.views.generic import View

from django.contrib.auth.models import User

from account.decorators import login_required

from .hooks import hookset
from .proxies import ActivityState
from .models import ActivitySessionState
from .signals import (
    activity_start as activity_start_signal,
    activity_play as activity_play_signal
)
from .utils import load_path_attr

class ActivityMixin(object):

    activity_key = None

    def get_activity_class(self):
        return load_path_attr(self.activity_class_path)

    def get_activity_state(self):
        return ActivityState(
            self.request,
            activity_key=self.activity_key,
            activity_class_path=self.activity_class_path,
        )

    def get_cancel_url(self):
        return reverse("dashboard")

    def get_completed_url(self):
        return reverse("dashboard")

    def get_activity_url(self):
        return reverse("activity", kwargs=dict(activity_key=self.activity_key))

    def get_activity(self):
        return self.activity_class(
            self.activity_state.latest if self.activity_state else None,
            self.activity_state,
            **self.get_activity_kwargs()
        )

    def get_activity_kwargs(self, **kwargs):
        kwargs.update({
            "activity_url": self.get_activity_url(),
            "completed_url": self.get_completed_url(),
            "cancel_url": self.get_cancel_url(),
        })
        return kwargs

    def dispatch(self, request, *args, **kwargs):
        self.request = request
        self.args = args
        self.kwargs = kwargs
        if self.activity_key is None:
            self.activity_key = kwargs.get("key")
        self.activity_class_path = hookset.activity_class_path(self.activity_key)
        if self.activity_class_path is None:
            raise Http404
        self.activity_class = self.get_activity_class()
        self.activity_state = self.get_activity_state()
        return super(ActivityMixin, self).dispatch(request, *args, **kwargs)

class ActivityView(ActivityMixin, View):

    def get(self, request, *args, **kwargs):
        activity = self.get_activity()
        if self.activity_state.progression == "start":  # Must mean you are just starting out
            return render(request, "pinax/lms/activities/start_activity.html", {"activity": activity})
        activity_play_signal.send(sender=ActivityView, activity_key=self.activity_key, activity_session_state=self.activity_state.latest, request=self.request)
        return activity.handle_get_request(self.request)

    def post(self, request, *args, **kwargs):
        return getattr(self, "_{}".format(self.activity_state.progression))()

    def _continue(self):
        activity = self.get_activity()
        return activity.handle_post_request(self.request)

    def _start(self):
        activity_start_signal.send(sender=ActivityView, activity_key=self.activity_key, activity_state=self.activity_state, request=self.request)
        return redirect(self.get_activity_url())

    def _repeat(self):
        activity_start_signal.send(sender=ActivityView, activity_key=self.activity_key, activity_state=self.activity_state, request=self.request)
        return redirect(self.get_activity_url())

    def _completed(self):
        return redirect(self.get_completed_url())

@login_required
def staff_dashboard(request):

    if not request.user.is_staff:
        raise Http404

    activities = []

    for key, activity_class_path in hookset.all_activities():
        activity = load_path_attr(activity_class_path)
        activity_states = ActivityState.objects.filter(activity_key=key)
        completed_activity_states = activity_states.exclude(completed_count=0)
        activity_session_states = ActivitySessionState.objects.filter(activity_key=key)
        completed_activity_session_states = activity_session_states.filter(completed__isnull=False)

        activities.append({
            "activity_key": key,
            "title": activity.title,
            "activity_states": activity_states,
            "completed_activity_states": completed_activity_states,
            "activity_session_states": activity_session_states,
            "completed_activity_session_states": completed_activity_session_states,
        })
    return render(request, "staff_dashboard.html", {
        "users": User.objects.all(),
        "activity_states": ActivityState.objects.all(),
        "activity_session_states": ActivitySessionState.objects.all(),
        "activities": activities,
    })

@login_required
def staff_activity_detail(request, activity_key):

    if not request.user.is_staff:
        raise Http404

    activity_states = ActivityState.objects.filter(activity_key=activity_key)
    activity_session_states = ActivitySessionState.objects.filter(activity_key=activity_key)

    return render(request, "staff_activity_detail.html", {
        "activity_states": activity_states,
        "activity_session_states": activity_session_states,
    })
<commit_msg>Fixed missing parameters<commit_after>from django.core.urlresolvers import reverse
from django.http import Http404
from django.shortcuts import redirect, render
from django.views.generic import View

from django.contrib.auth.models import User

from account.decorators import login_required

from .hooks import hookset
from .proxies import ActivityState
from .models import ActivitySessionState
from .signals import (
    activity_start as activity_start_signal,
    activity_play as activity_play_signal
)
from .utils import load_path_attr

class ActivityMixin(object):

    activity_key = None

    def get_activity_class(self):
        return load_path_attr(self.activity_class_path)

    def get_activity_state(self):
        return ActivityState(
            self.request,
            activity_key=self.activity_key,
            activity_class_path=self.activity_class_path,
        )

    def get_cancel_url(self):
        return reverse("dashboard")

    def get_completed_url(self):
        return reverse("dashboard")

    def get_activity_url(self):
        return reverse("activity", kwargs=dict(activity_key=self.activity_key))

    def get_activity(self):
        return self.activity_class(
            self.activity_state.latest if self.activity_state else None,
            self.activity_state,
            **self.get_activity_kwargs()
        )

    def get_activity_kwargs(self, **kwargs):
        kwargs.setdefault("parameters", {})
        kwargs.update({
            "activity_url": self.get_activity_url(),
            "completed_url": self.get_completed_url(),
            "cancel_url": self.get_cancel_url(),
        })
        return kwargs

    def dispatch(self, request, *args, **kwargs):
        self.request = request
        self.args = args
        self.kwargs = kwargs
        if self.activity_key is None:
            self.activity_key = kwargs.get("key")
        self.activity_class_path = hookset.activity_class_path(self.activity_key)
        if self.activity_class_path is None:
            raise Http404
        self.activity_class = self.get_activity_class()
        self.activity_state = self.get_activity_state()
        return super(ActivityMixin, self).dispatch(request, *args, **kwargs)

class ActivityView(ActivityMixin, View):

    def get(self, request, *args, **kwargs):
        activity = self.get_activity()
        if self.activity_state.progression == "start":  # Must mean you are just starting out
            return render(request, "pinax/lms/activities/start_activity.html", {"activity": activity})
        activity_play_signal.send(sender=ActivityView, activity_key=self.activity_key, activity_session_state=self.activity_state.latest, request=self.request)
        return activity.handle_get_request(self.request)

    def post(self, request, *args, **kwargs):
        return getattr(self, "_{}".format(self.activity_state.progression))()

    def _continue(self):
        activity = self.get_activity()
        return activity.handle_post_request(self.request)

    def _start(self):
        activity_start_signal.send(sender=ActivityView, activity_key=self.activity_key, activity_state=self.activity_state, request=self.request)
        return redirect(self.get_activity_url())

    def _repeat(self):
        activity_start_signal.send(sender=ActivityView, activity_key=self.activity_key, activity_state=self.activity_state, request=self.request)
        return redirect(self.get_activity_url())

    def _completed(self):
        return redirect(self.get_completed_url())

@login_required
def staff_dashboard(request):

    if not request.user.is_staff:
        raise Http404

    activities = []

    for key, activity_class_path in hookset.all_activities():
        activity = load_path_attr(activity_class_path)
        activity_states = ActivityState.objects.filter(activity_key=key)
        completed_activity_states = activity_states.exclude(completed_count=0)
        activity_session_states = ActivitySessionState.objects.filter(activity_key=key)
        completed_activity_session_states = activity_session_states.filter(completed__isnull=False)

        activities.append({
            "activity_key": key,
            "title": activity.title,
            "activity_states": activity_states,
            "completed_activity_states": completed_activity_states,
            "activity_session_states": activity_session_states,
            "completed_activity_session_states": completed_activity_session_states,
        })
    return render(request, "staff_dashboard.html", {
        "users": User.objects.all(),
        "activity_states": ActivityState.objects.all(),
        "activity_session_states": ActivitySessionState.objects.all(),
        "activities": activities,
    })

@login_required
def staff_activity_detail(request, activity_key):

    if not request.user.is_staff:
        raise Http404

    activity_states = ActivityState.objects.filter(activity_key=activity_key)
    activity_session_states = ActivitySessionState.objects.filter(activity_key=activity_key)

    return render(request, "staff_activity_detail.html", {
        "activity_states": activity_states,
        "activity_session_states": activity_session_states,
    })
<|endoftext|>


<commit_before>from django.contrib.gis.db.models.functions import Distance
from django.db import transaction
from django.db.models import F
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import status
from rest_framework.exceptions import NotFound
from rest_framework.generics import (
    ListAPIView,
    RetrieveAPIView,
    ListCreateAPIView,
    RetrieveUpdateDestroyAPIView,
    UpdateAPIView,
    DestroyAPIView,
)
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response

from agir.events.models import Event
from agir.events.serializers import EventSerializer
from agir.groups.actions.notifications import (
    new_message_notifications,
    new_comment_notifications,
)
from agir.groups.filters import GroupAPIFilterSet
from agir.groups.models import SupportGroup, SupportGroupSubtype
from agir.groups.serializers import (
    SupportGroupLegacySerializer,
    SupportGroupSubtypeSerializer,
    SupportGroupSerializer,
    SupportGroupDetailSerializer,
)
from agir.lib.pagination import APIPaginator
from agir.groups.tasks import send_message_notification_email

__all__ = [
    "GroupSearchAPIView",
    "GroupSubtypesView",
    "UserGroupsView",
    "GroupDetailAPIView",
    "NearGroupsAPIView",
    "GroupEventsAPIView",
    "GroupPastEventsAPIView",
    "GroupUpcomingEventsAPIView",
    "GroupPastEventReportsAPIView",
    "GroupMessagesAPIView",
    "GroupSingleMessageAPIView",
    "GroupMessageCommentsAPIView",
    "GroupSingleCommentAPIView",
]

from agir.lib.rest_framework_permissions import GlobalOrObjectPermissions
from agir.msgs.models import SupportGroupMessage, SupportGroupMessageComment

from agir.msgs.serializers import (
    SupportGroupMessageSerializer,
    MessageCommentSerializer,
)

class GroupSearchAPIView(ListAPIView):
    "Vieille API encore utilisée par le composant js groupSelector du formulaire de dons"

    queryset = SupportGroup.objects.active()
    filter_backends = (DjangoFilterBackend,)
    filterset_class = GroupAPIFilterSet
    serializer_class = SupportGroupLegacySerializer
    pagination_class = APIPaginator
    permission_classes = (IsAuthenticated,)

class GroupSubtypesView(ListAPIView):
    serializer_class = SupportGroupSubtypeSerializer
    queryset = SupportGroupSubtype.objects.filter(
        visibility=SupportGroupSubtype.VISIBILITY_ALL
    )

class UserGroupsView(ListAPIView):
    serializer_class = SupportGroupSerializer
    permission_classes = (IsAuthenticated,)

    def get_queryset(self):
        person = self.request.user.person
        person_groups = (
            SupportGroup.objects.filter(memberships__person=self.request.user.person)
            .active()
            .annotate(membership_type=F("memberships__membership_type"))
            .order_by("-membership_type", "name")
        )
        if person_groups.count() == 0 and person.coordinates is not None:
            person_groups = SupportGroup.objects.active()
            if person.is_2022_only:
                person_groups = person_groups.is_2022()
            person_groups = person_groups.annotate(
                distance=Distance("coordinates", person.coordinates)
            ).order_by("distance")[:3]
            for group in person_groups:
                group.membership = None

        return person_groups

class GroupDetailAPIView(RetrieveAPIView):
    permission_ = ("groups.view_supportgroup",)
    serializer_class = SupportGroupDetailSerializer
    queryset = SupportGroup.objects.active()

class NearGroupsAPIView(ListAPIView):
    serializer_class = SupportGroupDetailSerializer
    queryset = SupportGroup.objects.active()

    def get_serializer(self, *args, **kwargs):
        return super().get_serializer(
            *args, fields=["id", "name", "iconConfiguration", "location",], **kwargs
        )

    def get_queryset(self):
        if self.supportgroup.coordinates is None:
            return SupportGroup.objects.none()

        groups = (
            SupportGroup.objects.active()
            .exclude(pk=self.supportgroup.pk)
            .exclude(coordinates__isnull=True)
        )

        if self.supportgroup.is_2022:
            groups = groups.is_2022()

        groups = groups.annotate(
            distance=Distance("coordinates", self.supportgroup.coordinates)
        ).order_by("distance")

        return groups[:3]

    def dispatch(self, request, pk, *args, **kwargs):
        try:
            self.supportgroup = SupportGroup.objects.active().get(pk=pk)
        except SupportGroup.DoesNotExist:
            return Response(status=status.HTTP_404_NOT_FOUND)

        return super().dispatch(request, *args, **kwargs)

class GroupEventsAPIView(ListAPIView):
    permission_ = ("groups.view_supportgroup",)
    serializer_class = EventSerializer
    queryset = Event.objects.listed()

    def get_queryset(self):
        events = (
            self.supportgroup.organized_events.listed()
            .distinct()
            .order_by("-start_time")
        )
        return events

    def dispatch(self, request, pk, *args, **kwargs):
        try:
            self.supportgroup = SupportGroup.objects.get(pk=pk)
        except SupportGroup.DoesNotExist:
            return Response(status=status.HTTP_404_NOT_FOUND)

        return super().dispatch(request, *args, **kwargs)

class GroupUpcomingEventsAPIView(ListAPIView):
    permission_ = ("groups.view_supportgroup",)
    serializer_class = EventSerializer
    queryset = Event.objects.listed().upcoming()

    def get_queryset(self):
        events = (
            self.supportgroup.organized_events.listed()
            .upcoming()
            .distinct()
            .order_by("start_time")
        )
        return events

    def dispatch(self, request, pk, *args, **kwargs):
        try:
            self.supportgroup = SupportGroup.objects.get(pk=pk)
        except SupportGroup.DoesNotExist:
            return Response(status=status.HTTP_404_NOT_FOUND)

        return super().dispatch(request, *args, **kwargs)

class GroupPastEventsAPIView(ListAPIView):
    permission_ = ("groups.view_supportgroup",)
    serializer_class = EventSerializer
    queryset = Event.objects.listed().past()
    pagination_class = APIPaginator

    def get_queryset(self):
        events = (
            self.supportgroup.organized_events.listed()
            .past()
            .distinct()
            .order_by("-start_time")
        )
        return events

    def dispatch(self, request, pk, *args, **kwargs):
        try:
            self.supportgroup = SupportGroup.objects.get(pk=pk)
        except SupportGroup.DoesNotExist:
            return Response(status=status.HTTP_404_NOT_FOUND)

        return super().dispatch(request, *args, **kwargs)

class GroupPastEventReportsAPIView(ListAPIView):
    permission_ = ("groups.view_supportgroup",)
    serializer_class = EventSerializer
    queryset = Event.objects.listed().past()

    def get_queryset(self):
        events = (
            self.supportgroup.organized_events.listed()
            .past()
            .exclude(report_content="")
            .distinct()
            .order_by("-start_time")
        )
        return events

    def dispatch(self, request, pk, *args, **kwargs):
        try:
            self.supportgroup = SupportGroup.objects.get(pk=pk)
        except SupportGroup.DoesNotExist:
            return Response(status=status.HTTP_404_NOT_FOUND)

        return super().dispatch(request, *args, **kwargs)

class GroupMessagesPermissions(GlobalOrObjectPermissions):
    perms_map = {"GET": [], "POST": []}
    object_perms_map = {
        "GET": ["msgs.view_supportgroupmessage"],
        "POST": ["msgs.add_supportgroupmessage"],
    }

class GroupMessagesAPIView(ListCreateAPIView):
    serializer_class = SupportGroupMessageSerializer
    permission_classes = (IsAuthenticated, GroupMessagesPermissions)
    pagination_class = APIPaginator

    def initial(self, request, *args, **kwargs):
        try:
            self.supportgroup = SupportGroup.objects.get(pk=kwargs["pk"])
        except SupportGroup.DoesNotExist:
            raise NotFound()

        self.check_object_permissions(request, self.supportgroup)

        super().initial(request, *args, **kwargs)

    def get_queryset(self):
        return self.supportgroup.messages.filter(deleted=False).order_by("-created")

    def get_serializer(self, *args, **kwargs):
        return super().get_serializer(
            *args, fields=self.serializer_class.LIST_FIELDS, **kwargs
        )

    def perform_create(self, serializer):
        with transaction.atomic():
            message = serializer.save(
                author=self.request.user.person, supportgroup=self.supportgroup
            )
            new_message_notifications(message)

class GroupSingleMessageAPIView(RetrieveUpdateDestroyAPIView):
    queryset = SupportGroupMessage.objects.filter(deleted=False)
    serializer_class = SupportGroupMessageSerializer
    permission_classes = (IsAuthenticated, GlobalOrObjectPermissions)

    def get_serializer(self, *args, **kwargs):
        return super().get_serializer(
            *args, fields=self.serializer_class.DETAIL_FIELDS, **kwargs
        )

    def perform_destroy(self, instance):
        instance.deleted = True
        instance.save()

class GroupMessageCommentsPermissions(GlobalOrObjectPermissions):
    perms_map = {"GET": [], "POST": []}
    object_perms_map = {
        "GET": ["msgs.view_supportgroupmessage"],
        "POST": ["msgs.add_supportgroupmessagecomment"],
    }

class GroupMessageCommentsAPIView(ListCreateAPIView):
    serializer_class = MessageCommentSerializer
    permission_classes = (IsAuthenticated, GroupMessageCommentsPermissions)

    def initial(self, request, *args, **kwargs):
        try:
            self.message = SupportGroupMessage.objects.get(pk=kwargs["pk"])
        except SupportGroupMessage.DoesNotExist:
            raise NotFound()

        self.check_object_permissions(request, self.message)

        super().initial(request, *args, **kwargs)

    def get_queryset(self):
        return self.message.comments.filter(deleted=False)

    def perform_create(self, serializer):
        with transaction.atomic():
            comment = serializer.save(
                author=self.request.user.person, message=self.message
            )
            new_comment_notifications(comment)

class GroupSingleCommentAPIView(UpdateAPIView, DestroyAPIView):
    queryset = SupportGroupMessageComment.objects.filter(deleted=False)
    serializer_class = MessageCommentSerializer
    permission_classes = (IsAuthenticated, GlobalOrObjectPermissions)

    def perform_destroy(self, instance):
        instance.deleted = True
        instance.save()
<commit_msg>fix: record message versions<commit_after>import reversion
from django.contrib.gis.db.models.functions import Distance
from django.db import transaction
from django.db.models import F
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import status
from rest_framework.exceptions import NotFound
from rest_framework.generics import (
    ListAPIView,
    RetrieveAPIView,
    ListCreateAPIView,
    RetrieveUpdateDestroyAPIView,
    UpdateAPIView,
    DestroyAPIView,
)
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response

from agir.events.models import Event
from agir.events.serializers import EventSerializer
from agir.groups.actions.notifications import (
    new_message_notifications,
    new_comment_notifications,
)
from agir.groups.filters import GroupAPIFilterSet
from agir.groups.models import SupportGroup, SupportGroupSubtype
from agir.groups.serializers import (
    SupportGroupLegacySerializer,
    SupportGroupSubtypeSerializer,
    SupportGroupSerializer,
    SupportGroupDetailSerializer,
)
from agir.lib.pagination import APIPaginator
from agir.groups.tasks import send_message_notification_email

__all__ = [
    "GroupSearchAPIView",
    "GroupSubtypesView",
    "UserGroupsView",
    "GroupDetailAPIView",
    "NearGroupsAPIView",
    "GroupEventsAPIView",
    "GroupPastEventsAPIView",
    "GroupUpcomingEventsAPIView",
    "GroupPastEventReportsAPIView",
    "GroupMessagesAPIView",
    "GroupSingleMessageAPIView",
    "GroupMessageCommentsAPIView",
    "GroupSingleCommentAPIView",
]

from agir.lib.rest_framework_permissions import GlobalOrObjectPermissions
from agir.msgs.models import SupportGroupMessage, SupportGroupMessageComment

from agir.msgs.serializers import (
    SupportGroupMessageSerializer,
    MessageCommentSerializer,
)

class GroupSearchAPIView(ListAPIView):
    "Vieille API encore utilisée par le composant js groupSelector du formulaire de dons"

    queryset = SupportGroup.objects.active()
    filter_backends = (DjangoFilterBackend,)
    filterset_class = GroupAPIFilterSet
    serializer_class = SupportGroupLegacySerializer
    pagination_class = APIPaginator
    permission_classes = (IsAuthenticated,)

class GroupSubtypesView(ListAPIView):
    serializer_class = SupportGroupSubtypeSerializer
    queryset = SupportGroupSubtype.objects.filter(
        visibility=SupportGroupSubtype.VISIBILITY_ALL
    )

class UserGroupsView(ListAPIView):
    serializer_class = SupportGroupSerializer
    permission_classes = (IsAuthenticated,)

    def get_queryset(self):
        person = self.request.user.person
        person_groups = (
            SupportGroup.objects.filter(memberships__person=self.request.user.person)
            .active()
            .annotate(membership_type=F("memberships__membership_type"))
            .order_by("-membership_type", "name")
        )
        if person_groups.count() == 0 and person.coordinates is not None:
            person_groups = SupportGroup.objects.active()
            if person.is_2022_only:
                person_groups = person_groups.is_2022()
            person_groups = person_groups.annotate(
                distance=Distance("coordinates", person.coordinates)
            ).order_by("distance")[:3]
            for group in person_groups:
                group.membership = None

        return person_groups

class GroupDetailAPIView(RetrieveAPIView):
    permission_ = ("groups.view_supportgroup",)
    serializer_class = SupportGroupDetailSerializer
    queryset = SupportGroup.objects.active()

class NearGroupsAPIView(ListAPIView):
    serializer_class = SupportGroupDetailSerializer
    queryset = SupportGroup.objects.active()

    def get_serializer(self, *args, **kwargs):
        return super().get_serializer(
            *args, fields=["id", "name", "iconConfiguration", "location",], **kwargs
        )

    def get_queryset(self):
        if self.supportgroup.coordinates is None:
            return SupportGroup.objects.none()

        groups = (
            SupportGroup.objects.active()
            .exclude(pk=self.supportgroup.pk)
            .exclude(coordinates__isnull=True)
        )

        if self.supportgroup.is_2022:
            groups = groups.is_2022()

        groups = groups.annotate(
            distance=Distance("coordinates", self.supportgroup.coordinates)
        ).order_by("distance")

        return groups[:3]

    def dispatch(self, request, pk, *args, **kwargs):
        try:
            self.supportgroup = SupportGroup.objects.active().get(pk=pk)
        except SupportGroup.DoesNotExist:
            return Response(status=status.HTTP_404_NOT_FOUND)

        return super().dispatch(request, *args, **kwargs)

class GroupEventsAPIView(ListAPIView):
    permission_ = ("groups.view_supportgroup",)
    serializer_class = EventSerializer
    queryset = Event.objects.listed()

    def get_queryset(self):
        events = (
            self.supportgroup.organized_events.listed()
            .distinct()
            .order_by("-start_time")
        )
        return events

    def dispatch(self, request, pk, *args, **kwargs):
        try:
            self.supportgroup = SupportGroup.objects.get(pk=pk)
        except SupportGroup.DoesNotExist:
            return Response(status=status.HTTP_404_NOT_FOUND)

        return super().dispatch(request, *args, **kwargs)

class GroupUpcomingEventsAPIView(ListAPIView):
    permission_ = ("groups.view_supportgroup",)
    serializer_class = EventSerializer
    queryset = Event.objects.listed().upcoming()

    def get_queryset(self):
        events = (
            self.supportgroup.organized_events.listed()
            .upcoming()
            .distinct()
            .order_by("start_time")
        )
        return events

    def dispatch(self, request, pk, *args, **kwargs):
        try:
            self.supportgroup = SupportGroup.objects.get(pk=pk)
        except SupportGroup.DoesNotExist:
            return Response(status=status.HTTP_404_NOT_FOUND)

        return super().dispatch(request, *args, **kwargs)

class GroupPastEventsAPIView(ListAPIView):
    permission_ = ("groups.view_supportgroup",)
    serializer_class = EventSerializer
    queryset = Event.objects.listed().past()
    pagination_class = APIPaginator

    def get_queryset(self):
        events = (
            self.supportgroup.organized_events.listed()
            .past()
            .distinct()
            .order_by("-start_time")
        )
        return events

    def dispatch(self, request, pk, *args, **kwargs):
        try:
            self.supportgroup = SupportGroup.objects.get(pk=pk)
        except SupportGroup.DoesNotExist:
            return Response(status=status.HTTP_404_NOT_FOUND)

        return super().dispatch(request, *args, **kwargs)

class GroupPastEventReportsAPIView(ListAPIView):
    permission_ = ("groups.view_supportgroup",)
    serializer_class = EventSerializer
    queryset = Event.objects.listed().past()

    def get_queryset(self):
        events = (
            self.supportgroup.organized_events.listed()
            .past()
            .exclude(report_content="")
            .distinct()
            .order_by("-start_time")
        )
        return events

    def dispatch(self, request, pk, *args, **kwargs):
        try:
            self.supportgroup = SupportGroup.objects.get(pk=pk)
        except SupportGroup.DoesNotExist:
            return Response(status=status.HTTP_404_NOT_FOUND)

        return super().dispatch(request, *args, **kwargs)

class GroupMessagesPermissions(GlobalOrObjectPermissions):
    perms_map = {"GET": [], "POST": []}
    object_perms_map = {
        "GET": ["msgs.view_supportgroupmessage"],
        "POST": ["msgs.add_supportgroupmessage"],
    }

class GroupMessagesAPIView(ListCreateAPIView):
    serializer_class = SupportGroupMessageSerializer
    permission_classes = (IsAuthenticated, GroupMessagesPermissions)
    pagination_class = APIPaginator

    def initial(self, request, *args, **kwargs):
        try:
            self.supportgroup = SupportGroup.objects.get(pk=kwargs["pk"])
        except SupportGroup.DoesNotExist:
            raise NotFound()

        self.check_object_permissions(request, self.supportgroup)

        super().initial(request, *args, **kwargs)

    def get_queryset(self):
        return self.supportgroup.messages.filter(deleted=False).order_by("-created")

    def get_serializer(self, *args, **kwargs):
        return super().get_serializer(
            *args, fields=self.serializer_class.LIST_FIELDS, **kwargs
        )

    def perform_create(self, serializer):
        with transaction.atomic():
            message = serializer.save(
                author=self.request.user.person, supportgroup=self.supportgroup
            )
            new_message_notifications(message)

class GroupSingleMessageAPIView(RetrieveUpdateDestroyAPIView):
    queryset = SupportGroupMessage.objects.filter(deleted=False)
    serializer_class = SupportGroupMessageSerializer
    permission_classes = (IsAuthenticated, GlobalOrObjectPermissions)

    def get_serializer(self, *args, **kwargs):
        return super().get_serializer(
            *args, fields=self.serializer_class.DETAIL_FIELDS, **kwargs
        )

    def perform_update(self, serializer):
        with reversion.create_revision():
            super().perform_update(serializer)

    def perform_destroy(self, instance):
        instance.deleted = True
        instance.save()

class GroupMessageCommentsPermissions(GlobalOrObjectPermissions):
    perms_map = {"GET": [], "POST": []}
    object_perms_map = {
        "GET": ["msgs.view_supportgroupmessage"],
        "POST": ["msgs.add_supportgroupmessagecomment"],
    }

class GroupMessageCommentsAPIView(ListCreateAPIView):
    serializer_class = MessageCommentSerializer
    permission_classes = (IsAuthenticated, GroupMessageCommentsPermissions)

    def initial(self, request, *args, **kwargs):
        try:
            self.message = SupportGroupMessage.objects.get(pk=kwargs["pk"])
        except SupportGroupMessage.DoesNotExist:
            raise NotFound()

        self.check_object_permissions(request, self.message)

        super().initial(request, *args, **kwargs)

    def get_queryset(self):
        return self.message.comments.filter(deleted=False)

    def perform_create(self, serializer):
        with transaction.atomic():
            comment = serializer.save(
                author=self.request.user.person, message=self.message
            )
            new_comment_notifications(comment)

class GroupSingleCommentAPIView(UpdateAPIView, DestroyAPIView):
    queryset = SupportGroupMessageComment.objects.filter(deleted=False)
    serializer_class = MessageCommentSerializer
    permission_classes = (IsAuthenticated, GlobalOrObjectPermissions)

    def perform_update(self, serializer):
        with reversion.create_revision():
            super().perform_update(serializer)

    def perform_destroy(self, instance):
        instance.deleted = True
        instance.save()
<|endoftext|>


<commit_before>import os.path
from functools import total_ordering
import numpy as np

import dwi.asciifile
import dwi.files
import dwi.util

@total_ordering
class GleasonScore(object):
    def __init__(self, score):
        """Intialize with a sequence or a string like '3+4+5' (third digit is
        optional)."""
        if isinstance(score, str) or isinstance(score, unicode):
            s = score.split('+')
        else:
            s = score
        s = tuple(map(int, s))
        if len(s) == 2:
            s += (0,) # Internal representation always has three digits.
        if not len(s) == 3:
            raise Exception('Invalid gleason score: %s', score)
        self.score = s

    def __repr__(self):
        s = self.score
        if not s[-1]:
            s = s[0:-1] # Drop trailing zero.
        return '+'.join(map(str, s))

    def __hash__(self):
        return hash(self.score)

    def __iter__(self):
        return iter(self.score)

    def __eq__(self, other):
        return self.score == other.score

    def __lt__(self, other):
        return self.score < other.score

class Lesion(object):
    def __init__(self, index, score, location):
        self.index = index
        self.score = score
        self.location = location

    def __repr__(self):
        return repr((self.score, self.location))

@total_ordering
class Patient(object):
    def __init__(self, num, name, scans, lesions):
        self.num = num
        self.name = name
        self.scans = scans
        self.lesions = lesions
        self.score = lesions[0].score # For backwards compatibility.

    def __repr__(self):
        return repr(self.tuple())

    def __hash__(self):
        return hash(self.tuple())

    def __eq__(self, other):
        return self.tuple() == other.tuple()

    def __lt__(self, other):
        return self.tuple() < other.tuple()

    def tuple(self):
        return self.num, self.name, self.scans, self.lesions

def scan_in_patients(patients, num, scan):
    """Is this scan listed in the patients sequence?"""
    for p in patients:
        if p.num == num and scan in p.scans:
            return True
    return False

def get_patient(patients, num):
    """Search a patient from sequence by patient number."""
    for p in patients:
        if p.num == num:
            return p
    raise Exception('Patient not found: {}'.format(num))

def get_gleason_scores(patients):
    """Get all separate Gleason scores, sorted."""
    #return sorted({p.score for p in patients})
    scores = set()
    for p in patients:
        scores.update(l.score for l in p.lesions)
    return sorted(scores)

def score_ord(scores, score):
    """Get Gleason score's ordinal number."""
    return sorted(scores).index(score)

def load_files(patients, filenames, pairs=False):
    """Load pmap files."""
    pmapfiles = []
    for f in filenames:
        num, scan = dwi.util.parse_num_scan(os.path.basename(f))
        if scan_in_patients(patients, num, scan):
            pmapfiles.append(f)
    afs = map(dwi.asciifile.AsciiFile, pmapfiles)
    if pairs:
        dwi.util.scan_pairs(afs)
    ids = [dwi.util.parse_num_scan(af.basename) for af in afs]
    pmaps = [af.a for af in afs]
    pmaps = np.array(pmaps)
    params = afs[0].params()
    assert pmaps.shape[-1] == len(params), 'Parameter name mismatch.'
    #print 'Filenames: %i, loaded: %i, lines: %i, columns: %i'\
    #        % (len(filenames), pmaps.shape[0], pmaps.shape[1], pmaps.shape[2])
    return pmaps, ids, params

def load_labels(patients, nums, labeltype='score'):
    """Load labels according to patient numbers."""
    gs = get_gleason_scores(patients)
    scores = [get_patient(patients, n).score for n in nums]
    if labeltype == 'score':
        # Use Gleason score.
        labels = scores
    elif labeltype == 'ord':
        # Use ordinal.
        labels = [score_ord(gs, s) for s in scores]
    elif labeltype == 'bin':
        # Is aggressive? (ROI1.)
        labels = [s > GleasonScore('3+4') for s in scores]
    elif labeltype == 'cancer':
        # Is cancer? (ROI1 vs 2, all true for ROI1.)
        labels = [1] * len(scores)
    else:
        raise Exception('Invalid parameter: %s' % labeltype)
    return labels

def lesions(patients):
    """Generate all case, scan, lesion combinations."""
    for p in patients:
        for s in p.scans:
            for i, l in enumerate(p.lesions):
                yield p, s, i, l

# Low group: 3 only; intermediate: 4 secondary or tertiary w/o 5; high: rest.
THRESHOLDS_STANDARD = ['3+3', '3+4']

def read_pmaps(patients_file, pmapdir, thresholds=['3+3'], voxel='all',
        multiroi=False):
    """Read pmaps labeled by their Gleason score.

    Label thresholds are maximum scores of each label group. Labels are ordinal
    of score if no thresholds provided."""
    # TODO Support for selecting measurements over scan pairs
    thresholds = map(GleasonScore, thresholds)
    patients = dwi.files.read_patients_file(patients_file)
    gs = get_gleason_scores(patients)
    data = []
    for patient, scan, i, lesion in lesions(patients):
        if not multiroi and i != 0:
            continue
        case = patient.num
        score = lesion.score
        if thresholds:
            label = sum(score > t for t in thresholds)
        else:
            label = score_ord(gs, score)
        roi = i if multiroi else None
        pmap, params, pathname = read_pmap(pmapdir, case, scan, roi=roi,
                voxel=voxel)
        d = dict(case=case, scan=scan, roi=i, score=score, label=label,
                pmap=pmap, params=params, pathname=pathname)
        data.append(d)
        if pmap.shape != data[0]['pmap'].shape:
            raise Exception('Irregular shape: %s' % pathname)
        if params != data[0]['params']:
            raise Exception('Irregular params: %s' % pathname)
    return data

def grouping(data):
    """Return different scores, grouped scores, and their sample sizes.

    See read_pmaps()."""
    scores = [d['score'] for d in data]
    labels = [d['label'] for d in data]
    n_labels = max(labels) + 1
    groups = [[] for _ in range(n_labels)]
    for s, l in zip(scores, labels):
        groups[l].append(s)
    different_scores = sorted(set(scores))
    group_scores = [sorted(set(g)) for g in groups]
    group_sizes = [len(g) for g in groups]
    return different_scores, group_scores, group_sizes

def read_pmap(dirname, case, scan, roi=None, voxel='all'):
    """Read single pmap."""
    d = dict(d=dirname, c=case, s=scan, r=roi)
    if roi is None:
        s = '{d}/{c}_*{s}*.txt'
    else:
        d['r'] += 1
        s = '{d}/{c}_*{s}_{r}*.txt'
    path = dwi.util.sglob(s.format(**d))
    af = dwi.asciifile.AsciiFile(path)
    pmap = af.a
    params = af.params()
    if pmap.shape[-1] != len(params):
        # TODO Move to Asciifile initializer?
        raise Exception('Number of parameters mismatch: %s' % af.filename)
    # Select voxel to use.
    if voxel == 'all':
        pass # Use all voxels.
    elif voxel == 'mean':
        pmap = np.mean(pmap, axis=0, keepdims=True) # Use mean voxel.
    elif voxel == 'median':
        pmap = dwi.util.median(pmap, axis=0, keepdims=True) # Use median voxel.
    else:
        pmap = pmap[[int(voxel)]] # Use single voxel only.
    return pmap, params, af.filename
<commit_msg>lesions() iterator doesn't directly yield lesion index<commit_after>import os.path
from functools import total_ordering
import numpy as np

import dwi.asciifile
import dwi.files
import dwi.util

@total_ordering
class GleasonScore(object):
    def __init__(self, score):
        """Intialize with a sequence or a string like '3+4+5' (third digit is
        optional)."""
        if isinstance(score, str) or isinstance(score, unicode):
            s = score.split('+')
        else:
            s = score
        s = tuple(map(int, s))
        if len(s) == 2:
            s += (0,) # Internal representation always has three digits.
        if not len(s) == 3:
            raise Exception('Invalid gleason score: %s', score)
        self.score = s

    def __repr__(self):
        s = self.score
        if not s[-1]:
            s = s[0:-1] # Drop trailing zero.
        return '+'.join(map(str, s))

    def __hash__(self):
        return hash(self.score)

    def __iter__(self):
        return iter(self.score)

    def __eq__(self, other):
        return self.score == other.score

    def __lt__(self, other):
        return self.score < other.score

class Lesion(object):
    def __init__(self, index, score, location):
        self.index = index
        self.score = score
        self.location = location

    def __repr__(self):
        return repr((self.score, self.location))

@total_ordering
class Patient(object):
    def __init__(self, num, name, scans, lesions):
        self.num = num
        self.name = name
        self.scans = scans
        self.lesions = lesions
        self.score = lesions[0].score # For backwards compatibility.

    def __repr__(self):
        return repr(self.tuple())

    def __hash__(self):
        return hash(self.tuple())

    def __eq__(self, other):
        return self.tuple() == other.tuple()

    def __lt__(self, other):
        return self.tuple() < other.tuple()

    def tuple(self):
        return self.num, self.name, self.scans, self.lesions

def scan_in_patients(patients, num, scan):
    """Is this scan listed in the patients sequence?"""
    for p in patients:
        if p.num == num and scan in p.scans:
            return True
    return False

def get_patient(patients, num):
    """Search a patient from sequence by patient number."""
    for p in patients:
        if p.num == num:
            return p
    raise Exception('Patient not found: {}'.format(num))

def get_gleason_scores(patients):
    """Get all separate Gleason scores, sorted."""
    #return sorted({p.score for p in patients})
    scores = set()
    for p in patients:
        scores.update(l.score for l in p.lesions)
    return sorted(scores)

def score_ord(scores, score):
    """Get Gleason score's ordinal number."""
    return sorted(scores).index(score)

def load_files(patients, filenames, pairs=False):
    """Load pmap files."""
    pmapfiles = []
    for f in filenames:
        num, scan = dwi.util.parse_num_scan(os.path.basename(f))
        if scan_in_patients(patients, num, scan):
            pmapfiles.append(f)
    afs = map(dwi.asciifile.AsciiFile, pmapfiles)
    if pairs:
        dwi.util.scan_pairs(afs)
    ids = [dwi.util.parse_num_scan(af.basename) for af in afs]
    pmaps = [af.a for af in afs]
    pmaps = np.array(pmaps)
    params = afs[0].params()
    assert pmaps.shape[-1] == len(params), 'Parameter name mismatch.'
    #print 'Filenames: %i, loaded: %i, lines: %i, columns: %i'\
    #        % (len(filenames), pmaps.shape[0], pmaps.shape[1], pmaps.shape[2])
    return pmaps, ids, params

def load_labels(patients, nums, labeltype='score'):
    """Load labels according to patient numbers."""
    gs = get_gleason_scores(patients)
    scores = [get_patient(patients, n).score for n in nums]
    if labeltype == 'score':
        # Use Gleason score.
        labels = scores
    elif labeltype == 'ord':
        # Use ordinal.
        labels = [score_ord(gs, s) for s in scores]
    elif labeltype == 'bin':
        # Is aggressive? (ROI1.)
        labels = [s > GleasonScore('3+4') for s in scores]
    elif labeltype == 'cancer':
        # Is cancer? (ROI1 vs 2, all true for ROI1.)
        labels = [1] * len(scores)
    else:
        raise Exception('Invalid parameter: %s' % labeltype)
    return labels

def lesions(patients):
    """Generate all case, scan, lesion combinations."""
    for p in patients:
        for s in p.scans:
            for l in p.lesions:
                yield p, s, l

# Low group: 3 only; intermediate: 4 secondary or tertiary w/o 5; high: rest.
THRESHOLDS_STANDARD = ['3+3', '3+4']

def read_pmaps(patients_file, pmapdir, thresholds=['3+3'], voxel='all',
        multiroi=False):
    """Read pmaps labeled by their Gleason score.

    Label thresholds are maximum scores of each label group. Labels are ordinal
    of score if no thresholds provided."""
    # TODO Support for selecting measurements over scan pairs
    thresholds = map(GleasonScore, thresholds)
    patients = dwi.files.read_patients_file(patients_file)
    gs = get_gleason_scores(patients)
    data = []
    for patient, scan, lesion in lesions(patients):
        if not multiroi and lesion.index != 0:
            continue
        case = patient.num
        score = lesion.score
        if thresholds:
            label = sum(score > t for t in thresholds)
        else:
            label = score_ord(gs, score)
        roi = lesion.index if multiroi else None
        pmap, params, pathname = read_pmap(pmapdir, case, scan, roi=roi,
                voxel=voxel)
        d = dict(case=case, scan=scan, roi=lesion.index, score=score,
                label=label, pmap=pmap, params=params, pathname=pathname)
        data.append(d)
        if pmap.shape != data[0]['pmap'].shape:
            raise Exception('Irregular shape: %s' % pathname)
        if params != data[0]['params']:
            raise Exception('Irregular params: %s' % pathname)
    return data

def grouping(data):
    """Return different scores, grouped scores, and their sample sizes.

    See read_pmaps()."""
    scores = [d['score'] for d in data]
    labels = [d['label'] for d in data]
    n_labels = max(labels) + 1
    groups = [[] for _ in range(n_labels)]
    for s, l in zip(scores, labels):
        groups[l].append(s)
    different_scores = sorted(set(scores))
    group_scores = [sorted(set(g)) for g in groups]
    group_sizes = [len(g) for g in groups]
    return different_scores, group_scores, group_sizes

def read_pmap(dirname, case, scan, roi=None, voxel='all'):
    """Read single pmap."""
    d = dict(d=dirname, c=case, s=scan, r=roi)
    if roi is None:
        s = '{d}/{c}_*{s}*.txt'
    else:
        d['r'] += 1
        s = '{d}/{c}_*{s}_{r}*.txt'
    path = dwi.util.sglob(s.format(**d))
    af = dwi.asciifile.AsciiFile(path)
    pmap = af.a
    params = af.params()
    if pmap.shape[-1] != len(params):
        # TODO Move to Asciifile initializer?
        raise Exception('Number of parameters mismatch: %s' % af.filename)
    # Select voxel to use.
    if voxel == 'all':
        pass # Use all voxels.
    elif voxel == 'mean':
        pmap = np.mean(pmap, axis=0, keepdims=True) # Use mean voxel.
    elif voxel == 'median':
        pmap = dwi.util.median(pmap, axis=0, keepdims=True) # Use median voxel.
    else:
        pmap = pmap[[int(voxel)]] # Use single voxel only.
    return pmap, params, af.filename
<|endoftext|>


<commit_before># Licensed under a 3-clause BSD style license - see LICENSE.rst
import os
import tempfile
import shutil
from astropy.tests.helper import pytest, remote_data
try:
    import keyring
    HAS_KEYRING = True
except ImportError:
    HAS_KEYRING = False
try:
    from ...eso import Eso
    ESO_IMPORTED = True
except ImportError:
    ESO_IMPORTED = False
from ...exceptions import LoginError

SKIP_TESTS = not(HAS_KEYRING and ESO_IMPORTED)

@pytest.mark.skipif('SKIP_TESTS')
@remote_data
class TestEso:
    @pytest.fixture()
    def temp_dir(self, request):
        my_temp_dir = tempfile.mkdtemp()
        def fin():
            shutil.rmtree(my_temp_dir)
        request.addfinalizer(fin)
        return my_temp_dir

    def test_SgrAstar(self, temp_dir):
        eso = Eso()
        eso.cache_location = temp_dir

        instruments = eso.list_instruments()
        # in principle, we should run both of these tests
        # result_i = eso.query_instrument('midi', target='Sgr A*')
        # Equivalent, does not depend on SESAME:
        result_i = eso.query_instrument('midi', coord1=266.41681662, coord2=-29.00782497)

        surveys = eso.list_surveys()
        # result_s = eso.query_survey('VVV', target='Sgr A*')
        # Equivalent, does not depend on SESAME:
        result_s = eso.query_survey('VVV', coord1=266.41681662, coord2=-29.00782497)

        assert 'midi' in instruments
        assert result_i is not None
        assert 'VVV' in surveys
        assert result_s is not None
        assert 'Object' in result_s.colnames
        assert 'b333' in result_s['Object']

    def test_nologin(self):
        # WARNING: this test will fail if you haven't cleared your cache and
        # you have downloaded this file!
        eso = Eso()

        with pytest.raises(LoginError) as exc:
            eso.retrieve_data('AMBER.2006-03-14T07:40:19.830')

        assert exc.value.args[0] == "If you do not pass a username to login(), you should configure a default one!"

    def test_empty_return(self):
        # test for empty return with an object from the North
        eso = Eso()
        surveys = eso.list_surveys()
        # result_s = eso.query_survey(surveys[0], target='M51')
        # Avoid SESAME
        result_s = eso.query_survey(surveys[0], coord1=202.469575, coord2=47.195258)

        assert result_s is None

    def test_SgrAstar_remotevslocal(self, temp_dir):
        eso = Eso()
        # Remote version
        instruments = eso.list_instruments()
        # result1 = eso.query_instrument(instruments[0], target='Sgr A*')
        result1 = eso.query_instrument(instruments[0], coord1=266.41681662, coord2=-29.00782497)

        # Local version
        eso.cache_location = temp_dir
        instruments = eso.list_instruments()
        # result2 = eso.query_instrument(instruments[0], target='Sgr A*')
        result2 = eso.query_instrument(instruments[0], coord1=266.41681662, coord2=-29.00782497)

        assert result1 == result2

    def test_list_instruments(self):
        # If this test fails, we may simply need to update it

        inst = Eso.list_instruments()

        assert inst == ['fors1', 'fors2', 'vimos', 'omegacam', 'hawki', 'isaac',
                        'naco', 'visir', 'vircam', 'apex', 'uves', 'giraffe',
                        'xshooter', 'crires', 'kmos', 'sinfoni', 'amber',
                        'midi', 'harps']

    # REQUIRES LOGIN!
    # Can we get a special login specifically for astroquery testing?
    # def test_data_retrieval():
    #
    #    data_product_id = 'AMBER.2006-03-14T07:40:03.741'
    #    data_files = eso.retrieve_data([data_product_id])
    #    # How do we know if we're going to get .fits or .fits.Z?
    #    assert 'AMBER.2006-03-14T07:40:03.741.fits' in data_files[0]

    @pytest.mark.xfail
    def test_retrieve_data(self):
        eso = Eso()
        eso.login()
        result = eso.retrieve_data("MIDI.2014-07-25T02:03:11.561")
        assert len(result)>0
        assert "MIDI.2014-07-25T02:03:11.561" in result[0]
<commit_msg>list_instruments() returns unicode<commit_after># Licensed under a 3-clause BSD style license - see LICENSE.rst
import os
import tempfile
import shutil
from astropy.tests.helper import pytest, remote_data
try:
    import keyring
    HAS_KEYRING = True
except ImportError:
    HAS_KEYRING = False
try:
    from ...eso import Eso
    ESO_IMPORTED = True
except ImportError:
    ESO_IMPORTED = False
from ...exceptions import LoginError

SKIP_TESTS = not(HAS_KEYRING and ESO_IMPORTED)

@pytest.mark.skipif('SKIP_TESTS')
@remote_data
class TestEso:
    @pytest.fixture()
    def temp_dir(self, request):
        my_temp_dir = tempfile.mkdtemp()
        def fin():
            shutil.rmtree(my_temp_dir)
        request.addfinalizer(fin)
        return my_temp_dir

    def test_SgrAstar(self, temp_dir):
        eso = Eso()
        eso.cache_location = temp_dir

        instruments = eso.list_instruments()
        # in principle, we should run both of these tests
        # result_i = eso.query_instrument('midi', target='Sgr A*')
        # Equivalent, does not depend on SESAME:
        result_i = eso.query_instrument('midi', coord1=266.41681662, coord2=-29.00782497)

        surveys = eso.list_surveys()
        # result_s = eso.query_survey('VVV', target='Sgr A*')
        # Equivalent, does not depend on SESAME:
        result_s = eso.query_survey('VVV', coord1=266.41681662, coord2=-29.00782497)

        assert 'midi' in instruments
        assert result_i is not None
        assert 'VVV' in surveys
        assert result_s is not None
        assert 'Object' in result_s.colnames
        assert 'b333' in result_s['Object']

    def test_nologin(self):
        # WARNING: this test will fail if you haven't cleared your cache and
        # you have downloaded this file!
        eso = Eso()

        with pytest.raises(LoginError) as exc:
            eso.retrieve_data('AMBER.2006-03-14T07:40:19.830')

        assert exc.value.args[0] == "If you do not pass a username to login(), you should configure a default one!"

    def test_empty_return(self):
        # test for empty return with an object from the North
        eso = Eso()
        surveys = eso.list_surveys()
        # result_s = eso.query_survey(surveys[0], target='M51')
        # Avoid SESAME
        result_s = eso.query_survey(surveys[0], coord1=202.469575, coord2=47.195258)

        assert result_s is None

    def test_SgrAstar_remotevslocal(self, temp_dir):
        eso = Eso()
        # Remote version
        instruments = eso.list_instruments()
        # result1 = eso.query_instrument(instruments[0], target='Sgr A*')
        result1 = eso.query_instrument(instruments[0], coord1=266.41681662, coord2=-29.00782497)

        # Local version
        eso.cache_location = temp_dir
        instruments = eso.list_instruments()
        # result2 = eso.query_instrument(instruments[0], target='Sgr A*')
        result2 = eso.query_instrument(instruments[0], coord1=266.41681662, coord2=-29.00782497)

        assert result1 == result2

    def test_list_instruments(self):
        # If this test fails, we may simply need to update it

        inst = Eso.list_instruments()

        assert inst == [u'fors1', u'fors2', u'vimos', u'omegacam', u'hawki', u'isaac',
                        u'naco', u'visir', u'vircam', u'apex', u'uves', u'giraffe',
                        u'xshooter', u'crires', u'kmos', u'sinfoni', u'amber',
                        u'midi', u'harps']

    # REQUIRES LOGIN!
    # Can we get a special login specifically for astroquery testing?
    # def test_data_retrieval():
    #
    #    data_product_id = 'AMBER.2006-03-14T07:40:03.741'
    #    data_files = eso.retrieve_data([data_product_id])
    #    # How do we know if we're going to get .fits or .fits.Z?
    #    assert 'AMBER.2006-03-14T07:40:03.741.fits' in data_files[0]

    @pytest.mark.xfail
    def test_retrieve_data(self):
        eso = Eso()
        eso.login()
        result = eso.retrieve_data("MIDI.2014-07-25T02:03:11.561")
        assert len(result)>0
        assert "MIDI.2014-07-25T02:03:11.561" in result[0]
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
'''
Various user validation utilities
'''

# Import python libs
import re
import logging

log = logging.getLogger(__name__)

VALID_USERNAME= re.compile(r'[a-z_][a-z0-9_-]*[$]?', re.IGNORECASE)

def valid_username(user):
    '''
    Validates a username based on the guidelines in `useradd(8)`
    '''
    if type(user) not str:
        return False

    if len(user) > 32:
        return False

    return VALID_USERNAME.match(user) is not None
<commit_msg>Oops, typo<commit_after># -*- coding: utf-8 -*-
'''
Various user validation utilities
'''

# Import python libs
import re
import logging

log = logging.getLogger(__name__)

VALID_USERNAME = re.compile(r'[a-z_][a-z0-9_-]*[$]?', re.IGNORECASE)

def valid_username(user):
    '''
    Validates a username based on the guidelines in `useradd(8)`
    '''
    if type(user) not str:
        return False

    if len(user) > 32:
        return False

    return VALID_USERNAME.match(user) is not None
<|endoftext|>


<commit_before>from django.core.urlresolvers import reverse
from django.http import Http404
from django.shortcuts import redirect, render, get_object_or_404
from django.views.generic import View

from django.contrib.auth.models import User

from account.decorators import login_required

from .hooks import hookset
from .proxies import ActivityState
from .models import ActivitySessionState
# from .signals import (
#     activity_start as activity_start_signal,
#     activity_play as activity_play_signal
# )
from .utils import load_path_attr

class ActivityView(View):
    activity_key = None
    template_name = "pinax/lms/activities/activity.html"
    base_template_name = "pinax/lms/activities/base.html"

    def get_activity_class(self):
        return load_path_attr(self.activity_class_path)

    def get_activity_state(self):
        return ActivityState(
            self.request,
            activity_key=self.activity_key,
            activity_class_path=self.activity_class_path,
        )

    # @@@ this is overridden in module case but we'll need it for non-module case
    # @@@ when we get back to working on it
    def get_activity_url(self):
        return reverse("activity", kwargs=dict(activity_key=self.activity_key))

    # @@@ this is overridden in module case but we'll need it for non-module case
    # @@@ when we get back to working on it
    def get_session_url(self):
        pass  # to implement

    def get_activity(self):
        return self.activity_class(
            self.activity_state,
            **self.get_activity_kwargs()
        )

    def get_extra_context(self, **kwargs):
        kwargs.update({
            "base_template": self.base_template_name,
            "activity_url": self.get_activity_url(),
        })
        return kwargs

    def get_activity_kwargs(self, **kwargs):
        kwargs.setdefault("parameters", {})
        kwargs.update({
            "extra_context": self.get_extra_context()
        })
        return kwargs

    def dispatch(self, request, *args, **kwargs):
        self.request = request
        self.args = args
        self.kwargs = kwargs
        if self.activity_key is None:
            self.activity_key = kwargs.get("key")
        self.activity_class_path = hookset.activity_class_path(*args, **kwargs)
        if self.activity_class_path is None:
            raise Http404
        self.activity_class = self.get_activity_class()
        self.activity_state = self.get_activity_state()
        self.activity = self.get_activity()
        return super(ActivityView, self).dispatch(request, *args, **kwargs)

    def get(self, request, *args, **kwargs):
        if self.activity.singleton:
            return self.post(request, *args, **kwargs)
        else:
            return render(request, self.template_name, {
                "activity": self.activity,
                **self.get_extra_context()
            })

    def post(self, request, *args, **kwargs):
        if self.activity_state.progression == "completed":
            return redirect(self.get_activity_url())
        else:
            self.activity_state.ensure_exists()
            session = self.activity_state.latest
            return redirect(self.get_session_url(session))

class ActivitySessionView(View):

    base_template_name = "pinax/lms/activities/base.html"

    def get_activity_state(self):
        return ActivityState(
            self.request,
            activity_key=self.activity_key,
            activity_class_path=self.activity_class_path,
        )

    def get_activity(self):
        return self.activity_class(
            self.activity_state,
            **self.get_activity_kwargs()
        )

    def get_extra_context(self, **kwargs):
        kwargs.update({
            "base_template": self.base_template_name,
            "activity_url": self.get_activity_url(),
        })
        return kwargs

    def get_activity_kwargs(self, **kwargs):
        kwargs.setdefault("parameters", {})
        kwargs.update({
            "extra_context": self.get_extra_context()
        })
        return kwargs

    def dispatch(self, request, *args, **kwargs):
        self.request = request
        self.args = args
        self.kwargs = kwargs
        self.session = get_object_or_404(ActivitySessionState, pk=kwargs["session_pk"])
        if not hasattr(self, "activity_key"):
            self.activity_key = kwargs.get("key")
        self.activity_class_path = hookset.activity_class_path(*args, **kwargs)
        if self.activity_class_path is None:
            raise Http404
        self.activity_class = self.get_activity_class()
        self.activity_state = self.get_activity_state()
        self.activity = self.get_activity()
        return super(ActivitySessionView, self).dispatch(request, *args, **kwargs)

    def get(self, request, *args, **kwargs):
        return self.activity.handle_get_request(request, self.session, self.get_session_url(self.session))

    def post(self, request, *args, **kwargs):
        return self.activity.handle_post_request(request, self.session, self.get_session_url(self.session))

@login_required
def staff_dashboard(request):

    if not request.user.is_staff:
        raise Http404

    activities = []

    for key, activity_class_path in hookset.all_activities():
        activity = load_path_attr(activity_class_path)
        activity_states = ActivityState.objects.filter(activity_key=key)
        completed_activity_states = activity_states.exclude(completed_count=0)
        activity_session_states = ActivitySessionState.objects.filter(activity_key=key)
        completed_activity_session_states = activity_session_states.filter(completed__isnull=False)

        activities.append({
            "activity_key": key,
            "title": activity.title,
            "activity_states": activity_states,
            "completed_activity_states": completed_activity_states,
            "activity_session_states": activity_session_states,
            "completed_activity_session_states": completed_activity_session_states,
        })
    return render(request, "staff_dashboard.html", {
        "users": User.objects.all(),
        "activity_states": ActivityState.objects.all(),
        "activity_session_states": ActivitySessionState.objects.all(),
        "activities": activities,
    })

@login_required
def staff_activity_detail(request, activity_key):

    if not request.user.is_staff:
        raise Http404

    activity_states = ActivityState.objects.filter(activity_key=activity_key)
    activity_session_states = ActivitySessionState.objects.filter(activity_key=activity_key)

    return render(request, "staff_activity_detail.html", {
        "activity_states": activity_states,
        "activity_session_states": activity_session_states,
    })
<commit_msg>don't use dictionary syntax not supported in 2.7, etc<commit_after>from django.core.urlresolvers import reverse
from django.http import Http404
from django.shortcuts import redirect, render, get_object_or_404
from django.views.generic import View

from django.contrib.auth.models import User

from account.decorators import login_required

from .hooks import hookset
from .proxies import ActivityState
from .models import ActivitySessionState
# from .signals import (
#     activity_start as activity_start_signal,
#     activity_play as activity_play_signal
# )
from .utils import load_path_attr

class ActivityView(View):
    activity_key = None
    template_name = "pinax/lms/activities/activity.html"
    base_template_name = "pinax/lms/activities/base.html"

    def get_activity_class(self):
        return load_path_attr(self.activity_class_path)

    def get_activity_state(self):
        return ActivityState(
            self.request,
            activity_key=self.activity_key,
            activity_class_path=self.activity_class_path,
        )

    # @@@ this is overridden in module case but we'll need it for non-module case
    # @@@ when we get back to working on it
    def get_activity_url(self):
        return reverse("activity", kwargs=dict(activity_key=self.activity_key))

    # @@@ this is overridden in module case but we'll need it for non-module case
    # @@@ when we get back to working on it
    def get_session_url(self):
        pass  # to implement

    def get_activity(self):
        return self.activity_class(
            self.activity_state,
            **self.get_activity_kwargs()
        )

    def get_extra_context(self, **kwargs):
        kwargs.update({
            "base_template": self.base_template_name,
            "activity_url": self.get_activity_url(),
        })
        return kwargs

    def get_activity_kwargs(self, **kwargs):
        kwargs.setdefault("parameters", {})
        kwargs.update({
            "extra_context": self.get_extra_context()
        })
        return kwargs

    def dispatch(self, request, *args, **kwargs):
        self.request = request
        self.args = args
        self.kwargs = kwargs
        if self.activity_key is None:
            self.activity_key = kwargs.get("key")
        self.activity_class_path = hookset.activity_class_path(*args, **kwargs)
        if self.activity_class_path is None:
            raise Http404
        self.activity_class = self.get_activity_class()
        self.activity_state = self.get_activity_state()
        self.activity = self.get_activity()
        return super(ActivityView, self).dispatch(request, *args, **kwargs)

    def get(self, request, *args, **kwargs):
        if self.activity.singleton:
            return self.post(request, *args, **kwargs)
        else:
            ctx = {"activity": self.activity}
            ctx.update(self.get_extra_context())
            return render(request, self.template_name, ctx)

    def post(self, request, *args, **kwargs):
        if self.activity_state.progression == "completed":
            return redirect(self.get_activity_url())
        else:
            self.activity_state.ensure_exists()
            session = self.activity_state.latest
            return redirect(self.get_session_url(session))

class ActivitySessionView(View):

    base_template_name = "pinax/lms/activities/base.html"

    def get_activity_state(self):
        return ActivityState(
            self.request,
            activity_key=self.activity_key,
            activity_class_path=self.activity_class_path,
        )

    def get_activity(self):
        return self.activity_class(
            self.activity_state,
            **self.get_activity_kwargs()
        )

    def get_extra_context(self, **kwargs):
        kwargs.update({
            "base_template": self.base_template_name,
            "activity_url": self.get_activity_url(),
        })
        return kwargs

    def get_activity_kwargs(self, **kwargs):
        kwargs.setdefault("parameters", {})
        kwargs.update({
            "extra_context": self.get_extra_context()
        })
        return kwargs

    def dispatch(self, request, *args, **kwargs):
        self.request = request
        self.args = args
        self.kwargs = kwargs
        self.session = get_object_or_404(ActivitySessionState, pk=kwargs["session_pk"])
        if not hasattr(self, "activity_key"):
            self.activity_key = kwargs.get("key")
        self.activity_class_path = hookset.activity_class_path(*args, **kwargs)
        if self.activity_class_path is None:
            raise Http404
        self.activity_class = self.get_activity_class()
        self.activity_state = self.get_activity_state()
        self.activity = self.get_activity()
        return super(ActivitySessionView, self).dispatch(request, *args, **kwargs)

    def get(self, request, *args, **kwargs):
        return self.activity.handle_get_request(request, self.session, self.get_session_url(self.session))

    def post(self, request, *args, **kwargs):
        return self.activity.handle_post_request(request, self.session, self.get_session_url(self.session))

@login_required
def staff_dashboard(request):

    if not request.user.is_staff:
        raise Http404

    activities = []

    for key, activity_class_path in hookset.all_activities():
        activity = load_path_attr(activity_class_path)
        activity_states = ActivityState.objects.filter(activity_key=key)
        completed_activity_states = activity_states.exclude(completed_count=0)
        activity_session_states = ActivitySessionState.objects.filter(activity_key=key)
        completed_activity_session_states = activity_session_states.filter(completed__isnull=False)

        activities.append({
            "activity_key": key,
            "title": activity.title,
            "activity_states": activity_states,
            "completed_activity_states": completed_activity_states,
            "activity_session_states": activity_session_states,
            "completed_activity_session_states": completed_activity_session_states,
        })
    return render(request, "staff_dashboard.html", {
        "users": User.objects.all(),
        "activity_states": ActivityState.objects.all(),
        "activity_session_states": ActivitySessionState.objects.all(),
        "activities": activities,
    })

@login_required
def staff_activity_detail(request, activity_key):

    if not request.user.is_staff:
        raise Http404

    activity_states = ActivityState.objects.filter(activity_key=activity_key)
    activity_session_states = ActivitySessionState.objects.filter(activity_key=activity_key)

    return render(request, "staff_activity_detail.html", {
        "activity_states": activity_states,
        "activity_session_states": activity_session_states,
    })
<|endoftext|>


<commit_before>import os.path
import re
from functools import total_ordering
import numpy as np

import asciifile
import util

@total_ordering
class GleasonScore(object):
    def __init__(self, score):
        """Intialize with a sequence or a string like '3+4+5' (third digit is
        optional)."""
        s = score.split('+') if isinstance(score, str) else list(score)
        if not 2 <= len(s) <= 3:
            raise Exception('Invalid gleason score: %s', score)
        if len(s) == 2:
            s.append(0)
        self.score = tuple(map(int, s))

    def __repr__(self):
        s = self.score
        if not s[-1]:
            s = s[0:-1] # Drop trailing zero.
        return '+'.join(map(str, s))

    def __hash__(self):
        return hash(self.score)

    def __iter__(self):
        return iter(self.score)

    def __eq__(self, other):
        return self.score == other.score

    def __lt__(self, other):
        return self.score < other.score

@total_ordering
class Patient(object):
    def __init__(self, num, name, scans, score):
        self.num = num
        self.name = name
        self.scans = scans
        self.score = score

    def __repr__(self):
        return repr(self.tuple())

    def __hash__(self):
        return hash(self.tuple())

    def __eq__(self, other):
        return self.tuple() == other.tuple()

    def __lt__(self, other):
        return self.tuple() < other.tuple()

    def tuple(self):
        return self.num, self.name, self.scans, self.score

def read_patients_file(filename):
    """Load a list of patients.

    Row format: num name scan,... score1+score2
    """
    patients = []
    #p = re.compile(r'(\d+)\s+(\w+)\s+([\w,]+)\s+(\d\+\d)')
    p = re.compile(r'(\d+)\s+(\w+)\s+([\w,]+)\s+(\d\+\d(\+\d)?)')
    with open(filename, 'rU') as f:
        for line in f:
            line = line.strip()
            if not line:
                continue
            if line[0] == '#':
                continue
            m = p.match(line)
            if m:
                num, name, scans, score = m.groups()[0:4]
                num = int(num)
                name = name.lower()
                scans = sorted(scans.lower().split(','))
                score = GleasonScore(score)
                patient = Patient(num, name, scans, score)
                patients.append(patient)
            else:
                raise Exception('Invalid line in patients file: %s', line)
    return sorted(patients)

def scan_in_patients(patients, num, scan):
    """Is this scan listed in the patients sequence?"""
    for p in patients:
        if p.num == num and scan in p.scans:
            return True
    return False

def get_patient(patients, num):
    """Search a patient from sequence by patient number."""
    for p in patients:
        if p.num == num:
            return p
    return None

def get_gleason_scores(patients):
    """Get all separate Gleason scores, sorted."""
    scores = {p.score for p in patients}
    return sorted(scores)

def score_ord(scores, score):
    """Get Gleason score's ordinal number."""
    return sorted(scores).index(score)

def read_exclude_file(filename):
    """Load a list scans to exclude."""
    exclusions = []
    p = re.compile(r'(\d+)\s+([*\w]+)')
    with open(filename, 'rU') as f:
        for line in f:
            m = p.match(line.strip())
            if m:
                num, scan = m.groups()
                num = int(num)
                scan = scan.lower()
                exclusions.append((num, scan))
    return sorted(list(set(exclusions)))

def scan_excluded(exclusions, num, scan):
    """Tell whether given scan should be excluded."""
    for n, s in exclusions:
        if n == num:
            if s == scan or s == '*':
                return True
    return False

def exclude_files(pmapfiles, patients, exclusions=[]):
    """Return filenames without those that are to be excluded."""
    r = []
    for f in pmapfiles:
        num, name, scan = util.parse_filename(os.path.basename(f))
        p = get_patient(patients, num)
        if not p:
            continue # Patient not mentioned in patients file: exclude.
        if not scan_in_patients(patients, num, scan):
            continue # Scan not mentioned in patients file: exclude.
        if scan_excluded(exclusions, num, scan):
            continue # Scan mentioned in exclude file: exclude.
        r.append(f)
    return r

def load_files(patients, filenames, pairs=False):
    """Load pmap files."""
    pmapfiles = exclude_files(filenames, patients)
    afs = map(asciifile.AsciiFile, pmapfiles)
    if pairs:
        util.scan_pairs(afs)
    ids = [util.parse_num_scan(af.basename) for af in afs]
    pmaps = [af.a for af in afs]
    pmaps = np.array(pmaps)
    params = afs[0].params()
    assert pmaps.shape[-1] == len(params), 'Parameter name mismatch.'
    #print 'Filenames: %i, loaded: %i, lines: %i, columns: %i'\
    #        % (len(filenames), pmaps.shape[0], pmaps.shape[1], pmaps.shape[2])
    return pmaps, ids, params

def load_labels(patients, nums, labeltype='score'):
    """Load labels according to patient numbers."""
    gs = get_gleason_scores(patients)
    scores = [get_patient(patients, n).score for n in nums]
    if labeltype == 'score':
        # Use Gleason score.
        labels = scores
    elif labeltype == 'ord':
        # Use ordinal.
        labels = [score_ord(gs, s) for s in scores]
    elif labeltype == 'bin':
        # Is aggressive? (ROI1.)
        labels = [s > GleasonScore('3+4') for s in scores]
    elif labeltype == 'cancer':
        # Is cancer? (ROI1 vs 2, all true for ROI1.)
        labels = [1] * len(scores)
    else:
        raise Exception('Invalid parameter: %s' % labeltype)
    return labels

def read_pmaps(samplelist_file, patients_file, pmapdir, thresholds=['3+3'],
        average=False):
    """Read pmaps labeled by their Gleason score.

    Label thresholds are maximum scores of each label group. Labels are ordinal
    of score if no thresholds provided."""
    # TODO Support for selecting measurements over scan pairs
    thresholds = map(GleasonScore, thresholds)
    samples = util.read_sample_list(samplelist_file)
    patientsinfo = read_patients_file(patients_file)
    data = []
    for sample in samples:
        case = sample['case']
        score = get_patient(patientsinfo, case).score
        if thresholds:
            label = sum(score > t for t in thresholds)
        else:
            label = score_ord(get_gleason_scores(patientsinfo), score)
        for scan in sample['scans']:
            pmap, params, pathname = read_pmap(pmapdir, case, scan, average)
            d = dict(case=case, scan=scan, score=score, label=label, pmap=pmap,
                    params=params, pathname=pathname)
            data.append(d)
            if pmap.shape != data[0]['pmap'].shape:
                raise Exception('Irregular shape: %s' % pathname)
            if params != data[0]['params']:
                raise Exception('Irregular params: %s' % pathname)
    return data

def read_pmap(dirname, case, scan, average):
    """Read single pmap."""
    import glob
    d = dict(d=dirname, c=case, s=scan)
    s = '{d}/{c}_*_{s}_*.txt'.format(**d)
    paths = glob.glob(s)
    if len(paths) != 1:
        raise Exception('Ambiguous pmap: %s' % s)
    af = asciifile.AsciiFile(paths[0])
    pmap = af.a
    params = af.params()
    if pmap.shape[-1] != len(params):
        # TODO Move to Asciifile initializer?
        raise Exception('Number of parameters mismatch: %s' % af.filename)
    if average:
        pmap = np.mean(pmap, axis=0, keepdims=True)
    return pmap, params, af.filename
<commit_msg>Use sglob<commit_after>import os.path
import re
from functools import total_ordering
import numpy as np

import asciifile
import util

@total_ordering
class GleasonScore(object):
    def __init__(self, score):
        """Intialize with a sequence or a string like '3+4+5' (third digit is
        optional)."""
        s = score.split('+') if isinstance(score, str) else list(score)
        if not 2 <= len(s) <= 3:
            raise Exception('Invalid gleason score: %s', score)
        if len(s) == 2:
            s.append(0)
        self.score = tuple(map(int, s))

    def __repr__(self):
        s = self.score
        if not s[-1]:
            s = s[0:-1] # Drop trailing zero.
        return '+'.join(map(str, s))

    def __hash__(self):
        return hash(self.score)

    def __iter__(self):
        return iter(self.score)

    def __eq__(self, other):
        return self.score == other.score

    def __lt__(self, other):
        return self.score < other.score

@total_ordering
class Patient(object):
    def __init__(self, num, name, scans, score):
        self.num = num
        self.name = name
        self.scans = scans
        self.score = score

    def __repr__(self):
        return repr(self.tuple())

    def __hash__(self):
        return hash(self.tuple())

    def __eq__(self, other):
        return self.tuple() == other.tuple()

    def __lt__(self, other):
        return self.tuple() < other.tuple()

    def tuple(self):
        return self.num, self.name, self.scans, self.score

def read_patients_file(filename):
    """Load a list of patients.

    Row format: num name scan,... score1+score2
    """
    patients = []
    #p = re.compile(r'(\d+)\s+(\w+)\s+([\w,]+)\s+(\d\+\d)')
    p = re.compile(r'(\d+)\s+(\w+)\s+([\w,]+)\s+(\d\+\d(\+\d)?)')
    with open(filename, 'rU') as f:
        for line in f:
            line = line.strip()
            if not line:
                continue
            if line[0] == '#':
                continue
            m = p.match(line)
            if m:
                num, name, scans, score = m.groups()[0:4]
                num = int(num)
                name = name.lower()
                scans = sorted(scans.lower().split(','))
                score = GleasonScore(score)
                patient = Patient(num, name, scans, score)
                patients.append(patient)
            else:
                raise Exception('Invalid line in patients file: %s', line)
    return sorted(patients)

def scan_in_patients(patients, num, scan):
    """Is this scan listed in the patients sequence?"""
    for p in patients:
        if p.num == num and scan in p.scans:
            return True
    return False

def get_patient(patients, num):
    """Search a patient from sequence by patient number."""
    for p in patients:
        if p.num == num:
            return p
    return None

def get_gleason_scores(patients):
    """Get all separate Gleason scores, sorted."""
    scores = {p.score for p in patients}
    return sorted(scores)

def score_ord(scores, score):
    """Get Gleason score's ordinal number."""
    return sorted(scores).index(score)

def read_exclude_file(filename):
    """Load a list scans to exclude."""
    exclusions = []
    p = re.compile(r'(\d+)\s+([*\w]+)')
    with open(filename, 'rU') as f:
        for line in f:
            m = p.match(line.strip())
            if m:
                num, scan = m.groups()
                num = int(num)
                scan = scan.lower()
                exclusions.append((num, scan))
    return sorted(list(set(exclusions)))

def scan_excluded(exclusions, num, scan):
    """Tell whether given scan should be excluded."""
    for n, s in exclusions:
        if n == num:
            if s == scan or s == '*':
                return True
    return False

def exclude_files(pmapfiles, patients, exclusions=[]):
    """Return filenames without those that are to be excluded."""
    r = []
    for f in pmapfiles:
        num, name, scan = util.parse_filename(os.path.basename(f))
        p = get_patient(patients, num)
        if not p:
            continue # Patient not mentioned in patients file: exclude.
        if not scan_in_patients(patients, num, scan):
            continue # Scan not mentioned in patients file: exclude.
        if scan_excluded(exclusions, num, scan):
            continue # Scan mentioned in exclude file: exclude.
        r.append(f)
    return r

def load_files(patients, filenames, pairs=False):
    """Load pmap files."""
    pmapfiles = exclude_files(filenames, patients)
    afs = map(asciifile.AsciiFile, pmapfiles)
    if pairs:
        util.scan_pairs(afs)
    ids = [util.parse_num_scan(af.basename) for af in afs]
    pmaps = [af.a for af in afs]
    pmaps = np.array(pmaps)
    params = afs[0].params()
    assert pmaps.shape[-1] == len(params), 'Parameter name mismatch.'
    #print 'Filenames: %i, loaded: %i, lines: %i, columns: %i'\
    #        % (len(filenames), pmaps.shape[0], pmaps.shape[1], pmaps.shape[2])
    return pmaps, ids, params

def load_labels(patients, nums, labeltype='score'):
    """Load labels according to patient numbers."""
    gs = get_gleason_scores(patients)
    scores = [get_patient(patients, n).score for n in nums]
    if labeltype == 'score':
        # Use Gleason score.
        labels = scores
    elif labeltype == 'ord':
        # Use ordinal.
        labels = [score_ord(gs, s) for s in scores]
    elif labeltype == 'bin':
        # Is aggressive? (ROI1.)
        labels = [s > GleasonScore('3+4') for s in scores]
    elif labeltype == 'cancer':
        # Is cancer? (ROI1 vs 2, all true for ROI1.)
        labels = [1] * len(scores)
    else:
        raise Exception('Invalid parameter: %s' % labeltype)
    return labels

def read_pmaps(samplelist_file, patients_file, pmapdir, thresholds=['3+3'],
        average=False):
    """Read pmaps labeled by their Gleason score.

    Label thresholds are maximum scores of each label group. Labels are ordinal
    of score if no thresholds provided."""
    # TODO Support for selecting measurements over scan pairs
    thresholds = map(GleasonScore, thresholds)
    samples = util.read_sample_list(samplelist_file)
    patientsinfo = read_patients_file(patients_file)
    data = []
    for sample in samples:
        case = sample['case']
        score = get_patient(patientsinfo, case).score
        if thresholds:
            label = sum(score > t for t in thresholds)
        else:
            label = score_ord(get_gleason_scores(patientsinfo), score)
        for scan in sample['scans']:
            pmap, params, pathname = read_pmap(pmapdir, case, scan, average)
            d = dict(case=case, scan=scan, score=score, label=label, pmap=pmap,
                    params=params, pathname=pathname)
            data.append(d)
            if pmap.shape != data[0]['pmap'].shape:
                raise Exception('Irregular shape: %s' % pathname)
            if params != data[0]['params']:
                raise Exception('Irregular params: %s' % pathname)
    return data

def read_pmap(dirname, case, scan, average):
    """Read single pmap."""
    d = dict(d=dirname, c=case, s=scan)
    path = util.sglob('{d}/{c}_*_{s}_*.txt'.format(**d))
    af = asciifile.AsciiFile(path)
    pmap = af.a
    params = af.params()
    if pmap.shape[-1] != len(params):
        # TODO Move to Asciifile initializer?
        raise Exception('Number of parameters mismatch: %s' % af.filename)
    if average:
        pmap = np.mean(pmap, axis=0, keepdims=True)
    return pmap, params, af.filename
<|endoftext|>


<commit_before>################################################################################
# Copyright (c) 2021 ContinualAI.                                              #
# Copyrights licensed under the MIT License.                                   #
# See the accompanying LICENSE file for terms.                                 #
#                                                                              #
# Date: 25-11-2020                                                             #
# Author(s): Vincenzo Lomonaco, Lorenzo Pellegrini                             #
# E-mail: contact@continualai.org                                              #
# Website: www.continualai.org                                                 #
################################################################################

""" This module handles all the functionalities related to the logging of
Avalanche experiments. """

from pathlib import Path
from typing import Union

from PIL.Image import Image
from torch import Tensor
from torch.utils.tensorboard import SummaryWriter
from matplotlib.pyplot import Figure
from torchvision.transforms.functional import to_tensor
from avalanche.evaluation.metric_results import AlternativeValues, MetricValue
from avalanche.logging import StrategyLogger

class TensorboardLogger(StrategyLogger):
    """
    The `TensorboardLogger` provides an easy integration with
    Tensorboard logging. Each monitored metric is automatically
    logged to Tensorboard.
    The user can inspect results in real time by appropriately launching
    tensorboard with `tensorboard --logdir=/path/to/tb_log_exp_name`.

    AWS's S3 buckets and (if tensorflow is installed) GCloud storage url are
    supported.

    If no parameters are provided, the default folder in which tensorboard
    log files are placed is "./runs/".
    .. note::
        We rely on PyTorch implementation of Tensorboard. If you
        don't have Tensorflow installed in your environment,
        tensorboard will tell you that it is running with reduced
        feature set. This should not impact on the logger performance.
    """

    def __init__(self, tb_log_dir: Union[str, Path] = "./tb_data",
                 filename_suffix: str = ''):
        """
        Creates an instance of the `TensorboardLogger`.

        :param tb_log_dir: path to the directory where tensorboard log file
            will be stored. Default to "./tb_data".
        :param filename_suffix: string suffix to append at the end of
            tensorboard log file. Default ''.
        """

        super().__init__()
        tb_log_dir = _make_path_if_local(tb_log_dir)
        self.writer = SummaryWriter(tb_log_dir,
                                    filename_suffix=filename_suffix)

    def log_metric(self, metric_value: MetricValue, callback: str):
        super().log_metric(metric_value, callback)
        name = metric_value.name
        value = metric_value.value

        if isinstance(value, AlternativeValues):
            value = value.best_supported_value(Image, Tensor,
                                               Figure, float, int)

        if not isinstance(value, (Image, Tensor, Figure, float, int)):
            # Unsupported type
            return

        if isinstance(value, Figure):
            self.writer.add_figure(name, value,
                                   global_step=metric_value.x_plot)

        elif isinstance(value, Image):
            self.writer.add_image(name, to_tensor(value),
                                  global_step=metric_value.x_plot)

        elif isinstance(value, Tensor):
            self.writer.add_histogram(name, value,
                                      global_step=metric_value.x_plot)

        elif isinstance(value, (float, int)):
            self.writer.add_scalar(name, value,
                                   global_step=metric_value.x_plot)

def _make_path_if_local(tb_log_dir: Union[str, Path]) -> Union[str, Path]:
    if isinstance(tb_log_dir, str) and _is_aws_or_gcloud_path(tb_log_dir):
        return tb_log_dir

    tb_log_dir = Path(tb_log_dir)
    tb_log_dir.mkdir(parents=True, exist_ok=True)
    return tb_log_dir

def _is_aws_or_gcloud_path(tb_log_dir: str) -> bool:
    return tb_log_dir.startswith("gs://") or tb_log_dir.startswith("s3://")

__all__ = [
    'TensorboardLogger'
]
<commit_msg>[Tensorboard] Close writer on destructor<commit_after>################################################################################
# Copyright (c) 2021 ContinualAI.                                              #
# Copyrights licensed under the MIT License.                                   #
# See the accompanying LICENSE file for terms.                                 #
#                                                                              #
# Date: 25-11-2020                                                             #
# Author(s): Vincenzo Lomonaco, Lorenzo Pellegrini                             #
# E-mail: contact@continualai.org                                              #
# Website: www.continualai.org                                                 #
################################################################################

""" This module handles all the functionalities related to the logging of
Avalanche experiments. """

from pathlib import Path
from typing import Union

from PIL.Image import Image
from torch import Tensor
from torch.utils.tensorboard import SummaryWriter
from matplotlib.pyplot import Figure
from torchvision.transforms.functional import to_tensor
from avalanche.evaluation.metric_results import AlternativeValues, MetricValue
from avalanche.logging import StrategyLogger

class TensorboardLogger(StrategyLogger):
    """
    The `TensorboardLogger` provides an easy integration with
    Tensorboard logging. Each monitored metric is automatically
    logged to Tensorboard.
    The user can inspect results in real time by appropriately launching
    tensorboard with `tensorboard --logdir=/path/to/tb_log_exp_name`.

    AWS's S3 buckets and (if tensorflow is installed) GCloud storage url are
    supported.

    If no parameters are provided, the default folder in which tensorboard
    log files are placed is "./runs/".
    .. note::
        We rely on PyTorch implementation of Tensorboard. If you
        don't have Tensorflow installed in your environment,
        tensorboard will tell you that it is running with reduced
        feature set. This should not impact on the logger performance.
    """

    def __init__(self, tb_log_dir: Union[str, Path] = "./tb_data",
                 filename_suffix: str = ''):
        """
        Creates an instance of the `TensorboardLogger`.

        :param tb_log_dir: path to the directory where tensorboard log file
            will be stored. Default to "./tb_data".
        :param filename_suffix: string suffix to append at the end of
            tensorboard log file. Default ''.
        """

        super().__init__()
        tb_log_dir = _make_path_if_local(tb_log_dir)
        self.writer = SummaryWriter(tb_log_dir,
                                    filename_suffix=filename_suffix)

    def __del__(self):
        self.writer.close()

    def log_metric(self, metric_value: MetricValue, callback: str):
        super().log_metric(metric_value, callback)
        name = metric_value.name
        value = metric_value.value

        if isinstance(value, AlternativeValues):
            value = value.best_supported_value(Image, Tensor,
                                               Figure, float, int)

        if not isinstance(value, (Image, Tensor, Figure, float, int)):
            # Unsupported type
            return

        if isinstance(value, Figure):
            self.writer.add_figure(name, value,
                                   global_step=metric_value.x_plot)

        elif isinstance(value, Image):
            self.writer.add_image(name, to_tensor(value),
                                  global_step=metric_value.x_plot)

        elif isinstance(value, Tensor):
            self.writer.add_histogram(name, value,
                                      global_step=metric_value.x_plot)

        elif isinstance(value, (float, int)):
            self.writer.add_scalar(name, value,
                                   global_step=metric_value.x_plot)

def _make_path_if_local(tb_log_dir: Union[str, Path]) -> Union[str, Path]:
    if isinstance(tb_log_dir, str) and _is_aws_or_gcloud_path(tb_log_dir):
        return tb_log_dir

    tb_log_dir = Path(tb_log_dir)
    tb_log_dir.mkdir(parents=True, exist_ok=True)
    return tb_log_dir

def _is_aws_or_gcloud_path(tb_log_dir: str) -> bool:
    return tb_log_dir.startswith("gs://") or tb_log_dir.startswith("s3://")

__all__ = [
    'TensorboardLogger'
]
<|endoftext|>