BASE_DIR = os.path.dirname(os.path.abspath(__file__))
DEBUG = True
DOMAIN_NAME = "https://neurovault.org"
TEMPLATE_DEBUG = DEBUG
ADMINS = (
    (('Chris', 'krzysztof.gorgolewski@gmail.com'))
)
MANAGERS = ADMINS
SITE_ID = 1
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2', # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'.
        'NAME': 'postgres',
        # The following settings are not used with sqlite3:
        'USER': 'postgres',
        'HOST': 'db',                      # Empty for localhost through domain sockets or '127.0.0.1' for localhost through TCP.
        'PORT': '5432',        # Set to empty string for default.
    }
}
ALLOWED_HOSTS = ["*"]
TIME_ZONE = 'Europe/Berlin'
LANGUAGE_CODE = 'en-us'
SITE_ID = 1
USE_I18N = True
USE_L10N = True
USE_TZ = True
MEDIA_ROOT = os.path.join(BASE_DIR,'media')
MEDIA_URL = '/public/media/'
PRIVATE_MEDIA_ROOT = '/var/www/image_data'
PRIVATE_MEDIA_URL = '/media/images'
STATIC_ROOT = '/var/www/static'
STATIC_URL = '/static/'
STATICFILES_FINDERS = (
    'django.contrib.staticfiles.finders.FileSystemFinder',
    'django.contrib.staticfiles.finders.AppDirectoriesFinder',
#    'django.contrib.staticfiles.finders.DefaultStorageFinder',
)
MIDDLEWARE_CLASSES = (
    'django.middleware.common.CommonMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'neurovault.apps.statmaps.middleware.CollectionRedirectMiddleware',
    # Uncomment the next line for simple clickjacking protection:
    # 'django.middleware.clickjacking.XFrameOptionsMiddleware',
)
ROOT_URLCONF = 'neurovault.urls'
TEST_RUNNER = 'django.test.runner.DiscoverRunner'
WSGI_APPLICATION = 'neurovault.wsgi.application'
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': (),
        'OPTIONS': {'context_processors': ("django.contrib.auth.context_processors.auth",
                                            "django.core.context_processors.debug",
                                            "django.core.context_processors.i18n",
                                            "django.core.context_processors.media",
                                            "django.core.context_processors.static",
                                            "django.core.context_processors.tz",
                                            "django.contrib.messages.context_processors.messages",
                                            'django.core.context_processors.request'),
                    'loaders': ('django.template.loaders.filesystem.Loader',
                                'django.template.loaders.app_directories.Loader',
                                )}
    }
]
INSTALLED_APPS = (
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'neurovault.apps.main',
    'neurovault.apps.statmaps',
    'neurovault.apps.users',
    'django.contrib.sitemaps',
    # Uncomment the next line to enable the admin:
    'django.contrib.admin',
    # Uncomment the next line to enable admin documentation:
     #'django.contrib.admindocs',
    'social.apps.django_app.default',
    'rest_framework',
    'taggit',
    'crispy_forms',
    'taggit_templatetags',
    #'south',
    'dbbackup',
    'polymorphic',
    'djcelery',
    'django_cleanup',
    'file_resubmit',
    'django_mailgun',
    'django_hstore',
    'guardian',
    'oauth2_provider',
    'fixture_media',
    'raven.contrib.django.raven_compat',
)
AUTHENTICATION_BACKENDS = (
    'django.contrib.auth.backends.ModelBackend',
    'social.backends.facebook.FacebookOAuth2',
    'social.backends.google.GoogleOAuth2',
    'guardian.backends.ObjectPermissionBackend',
)
SOCIAL_AUTH_PIPELINE = (
    'social.pipeline.social_auth.social_details',
    'social.pipeline.social_auth.social_uid',
    'social.pipeline.social_auth.auth_allowed',
    'social.pipeline.social_auth.social_user',
    'social.pipeline.user.get_username',
    'social.pipeline.social_auth.associate_by_email',  # <--- enable this one
    'social.pipeline.user.create_user',
    'social.pipeline.social_auth.associate_user',
    'social.pipeline.social_auth.load_extra_data',
    'social.pipeline.user.user_details'
)
SOCIAL_AUTH_FACEBOOK_SCOPE = ['email']
SESSION_SERIALIZER = 'django.contrib.sessions.serializers.PickleSerializer'
REST_FRAMEWORK = {
    # Use hyperlinked styles by default.
    # Only used if the `serializer_class` attribute is not set on a view.
    'DEFAULT_MODEL_SERIALIZER_CLASS':
        'rest_framework.serializers.HyperlinkedModelSerializer',

    # LimitOffsetPagination will allow to set a ?limit= and ?offset=
    # variable in the URL.
    'DEFAULT_PAGINATION_CLASS':
        'neurovault.api.pagination.StandardResultPagination',

    # Use Django's standard `django.contrib.auth` permissions,
    # or allow read-only access for unauthenticated users.
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly'
    ],

    'DEFAULT_AUTHENTICATION_CLASSES': (
        'oauth2_provider.ext.rest_framework.OAuth2Authentication',
    ),
    'DEFAULT_RENDERER_CLASSES': (
        'neurovault.api.utils.ExplicitUnicodeJSONRenderer',
        'rest_framework.renderers.BrowsableAPIRenderer',
    ),
    'UNICODE_JSON': True,
}
OAUTH2_PROVIDER = {
    'REQUEST_APPROVAL_PROMPT': 'auto'
}
LOGIN_REDIRECT_URL = '/my_collections/'
CRISPY_TEMPLATE_PACK = 'bootstrap4'
DBBACKUP_STORAGE = 'dbbackup.storage.dropbox_storage'
DBBACKUP_TOKENS_FILEPATH = '/home/filo/dbtokens'
DBBACKUP_POSTGRES_BACKUP_COMMAND = 'export PGPASSWORD=neurovault\n pg_dump --username={adminuser} --host={host} --port={port} {databasename} >'
SENDFILE_BACKEND = 'sendfile.backends.development'
PRIVATE_MEDIA_REDIRECT_HEADER = 'X-Accel-Redirect'
PYCORTEX_DATASTORE = os.path.join(BASE_DIR,'pycortex_data')
CACHES = {
            'default': {
                'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
            },
            "file_resubmit": {
                'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
                "LOCATION": '/tmp/file_resubmit/'
            }
          }
EMAIL_BACKEND = 'django_mailgun.MailgunBackend'
MAILGUN_ACCESS_KEY = 'key-3ax6xnjp29jd6fds4gc373sgvjxteol0' # replace with a real key in production
MAILGUN_SERVER_NAME = 'samples.mailgun.org'# replace with 'neurovault.org' in production
DEFAULT_FROM_EMAIL = "noreply@neurovault.org"
    STATICFILES_DIRS = (
                        ('pycortex-resources', '/usr/local/lib/python2.7/site-packages/cortex/webgl/resources'),
                        ('pycortex-ctmcache', '/usr/local/share/pycortex/db/fsaverage/cache')
                        )
BROKER_URL = 'redis://redis:6379/0'
CELERY_RESULT_BACKEND = 'djcelery.backends.database:DatabaseBackend'
CELERY_ACCEPT_CONTENT = ['json']
CELERY_TASK_SERIALIZER = 'json'
CELERY_RESULT_SERIALIZER = 'json'
CELERY_DEFAULT_QUEUE = 'default'
CELERY_QUEUES = (
    Queue('default', Exchange('default'), routing_key='default'),
)
CELERY_IMPORTS = ('neurovault.apps.statmaps.tasks', )
CELERYBEAT_SCHEDULE = {
    'anima_crawl_every day': {
        'task': 'crawl_anima',
        'schedule': timedelta(days=1)
    },
}
CELERY_TIMEZONE = 'Europe/Berlin'
ANONYMOUS_USER_ID = -1
DEFAULT_OAUTH_APPLICATION_ID = -1
DEFAULT_OAUTH_APP_NAME = 'DefaultOAuthApp'
DEFAULT_OAUTH_APP_OWNER_ID = -2
DEFAULT_OAUTH_APP_OWNER_USERNAME = 'DefaultAppOwner'
OAUTH_PERSONAL_TOKEN_LENGTH = 40
    PRIVATE_MEDIA_ROOT = test_media_root
    CELERY_ALWAYS_EAGER = True
    CELERY_EAGER_PROPAGATES_EXCEPTIONS = True
TAGGIT_CASE_INSENSITIVE=True
FIXTURE_DIRS = (
    'apps/statmaps/fixtures/',
)
MEDIA_ROOT = PRIVATE_MEDIA_ROOT class KeyValueTag(TagBase):
    value = models.CharField(max_length=200, blank=True) class ImagesInCollectionJson(BaseDatatableView):
    columns = ['file.url', 'pk', 'name', 'polymorphic_ctype.name', 'is_valid']
    order_columns = ['','pk', 'name', 'polymorphic_ctype.name', '']

    def get_initial_queryset(self):
        # return queryset used as base for futher sorting/filtering
        # these are simply objects displayed in datatable
        # You should not filter data returned here by any filter values entered by user. This is because
        # we need some base queryset to count total number of records.
        collection = get_collection(self.kwargs['cid'], self.request)
        return collection.basecollectionitem_set.all()

    def render_column(self, row, column):
        if row.polymorphic_ctype.name == "statistic map":
            type = row.get_map_type_display()
        else:
            type = row.polymorphic_ctype.name

        # We want to render user as a custom column
        if column == 'file.url':
            if isinstance(row, Image):
                return get_button_html(filepath_to_uri(row.file.url), type,
                                       row.target_template_image)
            elif isinstance(row, NIDMResults):
                try:
                    excursion_sets = Graph(
                        row.zip_file.path).get_excursion_set_maps().values()
                    map_url = row.get_absolute_url() + "/" + str(excursion_sets[0].file.path)
                except KeyError:
                    maps = Graph(
                        row.zip_file.path).get_statistic_maps()
                    map_url = row.get_absolute_url() + "/" + str(
                        maps[0].file.path)
                return get_button_html(map_url, type, DEFAULT_TEMPLATE)
        elif column == 'polymorphic_ctype.name':
            return type
        elif column == 'is_valid':
            if row.polymorphic_ctype.name == "nidm results":
                return True
            else:
                return row.is_valid
        else:
            return super(ImagesInCollectionJson, self).render_column(row, column)

    def filter_queryset(self, qs):
        # use parameters passed in GET request to filter queryset

        # simple example:
        search = self.request.GET.get(u'search[value]', None)
        if search:
            qs = qs.filter(Q(name__icontains=search)| Q(description__icontains=search))
        return qs class PublicCollectionsJson(BaseDatatableView):
    columns = ['name', 'n_images', 'description', 'has_doi']
    order_columns = ['name', '', 'description', '']

    def get_initial_queryset(self):
        # return queryset used as base for futher sorting/filtering
        # these are simply objects displayed in datatable
        # You should not filter data returned here by any filter values entered by user. This is because
        # we need some base queryset to count total number of records.
        return Collection.objects.filter(~Q(name__endswith = "temporary collection"), private=False)

    def render_column(self, row, column):
        # We want to render user as a custom column
        if column == 'has_doi':
            if row.DOI:
                return "Yes"
            else:
                return ""
        elif column == 'n_images':
            return row.basecollectionitem_set.count()
        else:
            return super(PublicCollectionsJson, self).render_column(row, column)

    def filter_queryset(self, qs):
        # use parameters passed in GET request to filter queryset

        # simple example:
        search = self.request.GET.get(u'search[value]', None)
        if search:
            qs = qs.filter(Q(name__icontains=search)| Q(description__icontains=search))
        return qs class MyCollectionsJson(PublicCollectionsJson):

    def get_initial_queryset(self):
        return get_objects_for_user(self.request.user, 'statmaps.change_collection') class AtlasesAndParcellationsJson(BaseDatatableView):
    columns = ['file.url', 'name', 'collection.name', 'collection.authors', 'polymorphic_ctype.name']
    order_columns = ['','name', 'polymorphic_ctype.name']

    def get_initial_queryset(self):
        # return queryset used as base for futher sorting/filtering
        # these are simply objects displayed in datatable
        # You should not filter data returned here by any filter values entered by user. This is because
        # we need some base queryset to count total number of records.
        qs = Image.objects.instance_of(Atlas) | Image.objects.instance_of(StatisticMap).filter(statisticmap__map_type=BaseStatisticMap.Pa)
        qs = qs.filter(collection__private=False).exclude(collection__DOI__isnull=True)
        return qs


    def render_column(self, row, column):
        if row.polymorphic_ctype.name == "statistic map":
            type = row.get_map_type_display()
        else:
            type = row.polymorphic_ctype.name

        # We want to render user as a custom column
        if column == 'file.url':
            return get_button_html(filepath_to_uri(row.file.url), type,
                                   row.target_template_image)
        elif column == 'polymorphic_ctype.name':
            return type
        if column == 'collection.authors' and row.collection.authors:
            return row.collection.authors.split(',')[0].split(' ')[-1] + " et al."
        if column == 'collection.name':
            return "<a href='" + row.collection.get_absolute_url() + "'>" + row.collection.name + "</a>"
        else:
            return super(AtlasesAndParcellationsJson, self).render_column(row, column)

    def filter_queryset(self, qs):
        # use parameters passed in GET request to filter queryset

        # simple example:
        search = self.request.GET.get(u'search[value]', None)
        if search:
            qs = qs.filter(Q(name__icontains=search)| Q(description__icontains=search))
        return qs class ImagesByTaskJson(BaseDatatableView):
    columns = ['file.url', 'name', 'cognitive_contrast_cogatlas', 'collection.name']
    order_columns = ['', 'name', 'cognitive_contrast_cogatlas', 'collection.name']

    def get_initial_queryset(self):
        # Do not filter by task here, we may want other parameters
        cog_atlas_id = self.kwargs['cog_atlas_id']
        return StatisticMap.objects.filter(collection__private=False).filter(cognitive_paradigm_cogatlas=cog_atlas_id)

    def render_column(self, row, column):
        # We want to render user as a custom column
        if column == 'file.url':
            if isinstance(row, Image):
                return get_button_html(filepath_to_uri(row.file.url), type,
                                       row.target_template_image)
            elif isinstance(row, NIDMResults):
                return ""
        else:
            return super(ImagesByTaskJson, self).render_column(row, column)

    def filter_queryset(self, qs):
        # use parameters passed in GET request to filter queryset
        # simple example:
        search = self.request.GET.get(u'search[value]', None)
        if search:
            qs = qs.filter(Q(name__icontains=search)| Q(id__icontains=search))
        return qs class GroupImagesInCollectionJson(ImagesInCollectionJson):

    def get_initial_queryset(self):
        collection = get_collection(self.kwargs['cid'], self.request)
        return StatisticMap.objects.filter(collection=collection).filter(analysis_level='G') class SingleSubjectImagesInCollectionJson(ImagesInCollectionJson):

    def get_initial_queryset(self):
        collection = get_collection(self.kwargs['cid'], self.request)
        return StatisticMap.objects.filter(collection=collection).filter(analysis_level='S') class OtherImagesInCollectionJson(ImagesInCollectionJson):

    def get_initial_queryset(self):
        collection = get_collection(self.kwargs['cid'], self.request)
        return StatisticMap.objects.filter(collection=collection).filter(~Q(analysis_level__in=['G', 'S'])) class AllDOIPublicGroupImages(BaseDatatableView):
    columns = ['collection.name', 'collection.description', 'name', 'description', 'modality']
    order_columns = ['collection.name', 'collection.description', 'name', 'description',
                     'modality']

    def get_initial_queryset(self):
        if 'metaanalysis_id' in self.kwargs.keys():
            metaanalysis = Metaanalysis.objects.get(pk=self.kwargs['metaanalysis_id'])
            return metaanalysis.maps.all()
        else:
            public_collections_ids = Collection.objects.exclude(DOI__isnull=True).exclude(
                private=True).values_list('id', flat=True)
            return StatisticMap.objects.filter(analysis_level='G').filter(map_type__in=['T', 'Z']).filter(collection_id__in=public_collections_ids)

    def filter_queryset(self, qs):
        # use parameters passed in GET request to filter queryset

        # simple example:
        search = self.request.GET.get(u'search[value]', None)
        if search:
            qs = qs.filter(Q(name__icontains=search) | Q(description__icontains=search) | Q(
                collection__name__icontains=search) | Q(collection__description__icontains=search))
        return qs class MyMetaanalysesJson(PublicCollectionsJson):
    columns = ['name', 'description', 'n_images', 'status']
    order_columns = ['name', 'description', '', '']

    def get_initial_queryset(self):
        return Metaanalysis.objects.filter(owner=self.request.user)

    def render_column(self, row, column):
        if column == 'n_images':
            return row.maps.count()
        elif column == 'name':
            return "<a href='" + reverse('view_metaanalysis', kwargs={'metaanalysis_id': row.pk})\
                   + "'>" + \
                   row.name + "</a>"
        else:
            return super(PublicCollectionsJson, self).render_column(row, column)

    def filter_queryset(self, qs):
        # use parameters passed in GET request to filter queryset

        # simple example:
        search = self.request.GET.get(u'search[value]', None)
        if search:
            qs = qs.filter(Q(name__icontains=search)| Q(description__icontains=search))
        return qs @login_required
def toggle_active_metaanalysis(request, pk, collection_cid=None):
    image = get_image(pk,collection_cid,request)
    metaanalysis = Metaanalysis.objects.filter(owner=request.user).filter(status='active')[0]
    if request.user != metaanalysis.owner:
        return HttpResponseForbidden()
    if metaanalysis in image.metaanalysis_set.all():
        metaanalysis.maps.remove(image)
        new_status = 'out'
    else:
        metaanalysis.maps.add(image)
        new_status = 'in'
    return JSONResponse({'new_status': new_status}) @login_required
def edit_collection(request, cid=None):
    '''edit_collection is a view to edit a collection, meaning showing the edit form for an existing collection, creating a new collection, or passing POST data to update an existing collection
    :param cid: statmaps.models.Collection.pk the primary key of the collection. If none, will generate form to make a new collection.
    '''
    page_header = "Add new collection"
    if cid:
        collection = get_collection(cid, request)
        edit_permission = request.user.has_perm('statmaps.change_collection', collection)
        is_owner = request.user.has_perm('statmaps.delete_collection', collection)
        page_header = 'Edit collection'
        if not edit_permission:
            return HttpResponseForbidden()
    else:
        is_owner = True
        collection = Collection(owner=request.user)
    if request.method == "POST":
        if is_owner:
            form = OwnerCollectionForm(request.POST, request.FILES, instance=collection)
        else:
            form = CollectionForm(request.POST, request.FILES, instance=collection)
        if form.is_valid():
            previous_contribs = set()
            if form.instance.pk is not None:
                previous_contribs = set(form.instance.contributors.all())
            collection = form.save(commit=False)
            if collection.private and collection.private_token is None:
                collection.private_token = generate_url_token()
            collection.save()

            if is_owner:
                form.save_m2m()  # save contributors
                current_contribs = set(collection.contributors.all())
                new_contribs = list(current_contribs.difference(previous_contribs))
                context = {
                    'owner': collection.owner.username,
                    'collection': collection.name,
                    'url': get_server_url(request) + collection.get_absolute_url(),
                }
                subj = '%s has added you to a NeuroVault collection' % context['owner']
                send_email_notification('new_contributor', subj, new_contribs, context)

            return HttpResponseRedirect(collection.get_absolute_url())
    else:
        if is_owner:
            form = OwnerCollectionForm(instance=collection)
        else:
            form = CollectionForm(instance=collection)

    context = {"form": form, "page_header": page_header, "is_owner": is_owner}
    return render(request, "statmaps/edit_collection.html", context) def view_image(request, pk, collection_cid=None):
    '''view_image returns main view to see an image and associated meta data. If the image is in a collection with a DOI and has a generated thumbnail, it is a contender for image comparison, and a find similar button is exposed.
    :param pk: statmaps.models.Image.pk the primary key of the image
    :param collection_cid: statmaps.models.Collection.pk the primary key of the collection. Default None
    '''
    image = get_image(pk,collection_cid,request)
    user_owns_image = owner_or_contrib(request,image.collection)
    api_cid = pk

    # if no template is specified use MNI152
    if not image.target_template_image:
        image.target_template_image = DEFAULT_TEMPLATE

    comparison_is_possible = is_search_compatible(pk) and image.thumbnail
    pycortex_compatible = is_target_template_image_pycortex_compatible( image.target_template_image )
    neurosynth_compatible = is_target_template_image_neurosynth_compatible( image.target_template_image )

    is_there_an_active_metaanalysis = request.user.is_authenticated() and (Metaanalysis.objects.filter(
        owner=request.user).filter(status='active').count() != 0)
    is_metaanalysis_compatible = isinstance(image, StatisticMap) and image.analysis_level=='G' \
                                                                                          and \
                                                                                          image.map_type in ['T', 'Z']
    show_metaanalysis_button = is_there_an_active_metaanalysis and is_metaanalysis_compatible

    if not show_metaanalysis_button:
        meta_status = 'invalid'
    else:
        metaanalysis = Metaanalysis.objects.filter(owner=request.user).filter(status='active')[0]
        if metaanalysis in image.metaanalysis_set.all():
            meta_status = 'in'
        else:
            meta_status = 'out'

    if image.collection.private:
        api_cid = '%s-%s' % (image.collection.private_token,pk)
    context = {
        'image': image,
        'user': image.collection.owner,
        'user_owns_image': user_owns_image,
        'api_cid': api_cid,
        'neurosynth_compatible': neurosynth_compatible,
        'pycortex_compatible': pycortex_compatible,
        'comparison_is_possible': comparison_is_possible,
        'show_metaanalysis_button': show_metaanalysis_button,
        'meta_status': meta_status
    }

    communities = image.collection.communities.all()
    add_communities_context(communities, context)

    if isinstance(image, NIDMResultStatisticMap):
        context['img_basename'] = os.path.basename(image.file.url)
        context['ttl_basename'] = os.path.basename(image.nidm_results.ttl_file.url)

    if isinstance(image, Atlas):
        template = 'statmaps/atlas_details.html'
    else:
        if np.isnan(image.perc_bad_voxels) or np.isnan(image.perc_voxels_outside):
            context['warning'] = "Warning: This map seems to be empty!"
        elif not image.is_valid:
            context['warning'] = "Warning: This map is missing some mandatory metadata!"
            if user_owns_image:
                context['warning'] += " Please <a href='edit'>edit image details</a> to provide the missing information."
        elif image.not_mni:
            context['warning'] = "Warning: This map seems not to be in the MNI space (%.4g%% of meaningful voxels are outside of the brain). "%image.perc_voxels_outside
            context['warning'] += "Please transform the map to MNI space. "
        elif image.is_thresholded:
            context['warning'] = "Warning: This map seems to be thresholded, sparse or acquired with limited field of view (%.4g%% of voxels are zeros). "%image.perc_bad_voxels
            context['warning'] += "Some of the NeuroVault functions such as decoding might not work properly. "
            context['warning'] += "Please use unthresholded maps whenever possible."

        template = 'statmaps/statisticmap_details.html'
    return render(request, template, context) @login_required
def delete_image(request, pk):
    image = get_object_or_404(Image,pk=pk)
    cid = image.collection.pk
    if not request.user.has_perm("statmaps.delete_basecollectionitem", image):
        return HttpResponseForbidden()
    image.delete()
    return redirect('collection_details', cid=cid) @login_required
def edit_image(request, pk):
    image = get_object_or_404(Image,pk=pk)
    if isinstance(image, StatisticMap):
        form = EditStatisticMapForm
    elif isinstance(image, Atlas):
        form = EditAtlasForm
    elif isinstance(image, NIDMResultStatisticMap):
        form = EditNIDMResultStatisticMapForm
    else:
        raise Exception("unsupported image type")
    if not owner_or_contrib(request,image.collection):
        return HttpResponseForbidden()
    if request.method == "POST":
        form = form(request.POST, request.FILES, instance=image, user=request.user)
        if form.is_valid():
            form.save()
            return HttpResponseRedirect(image.get_absolute_url())
    else:
        form = form(instance=image, user=request.user)

    contrasts = get_contrast_lookup()
    context = {"form": form, "contrasts": json.dumps(contrasts)}
    return render(request, "statmaps/edit_image.html", context) def view_collection(request, cid):
    '''view_collection returns main view to see an entire collection of images, meaning a viewer and list of images to load into it.
    :param cid: statmaps.models.Collection.pk the primary key of the collection
    '''
    collection = get_collection(cid,request)
    edit_permission = request.user.has_perm('statmaps.change_collection', collection)
    delete_permission = request.user.has_perm('statmaps.delete_collection', collection)
    is_empty = not collection.basecollectionitem_set.exists()
    pycortex_compatible = all([is_target_template_image_pycortex_compatible(image.target_template_image) for image in collection.basecollectionitem_set.instance_of(Image)])

    context = {'collection': collection,
               'is_empty': is_empty,
               'user': request.user,
               'delete_permission': delete_permission,
               'edit_permission': edit_permission,
               'pycortex_compatible': pycortex_compatible,
               'cid':cid}

    context['gimages_visible'] = False
    context['simages_visible'] = False
    context['oimages_visible'] = False
    context['images_visible'] = False
    context['gimages_active'] = False
    context['simages_active'] = False
    context['oimages_active'] = False
    context['oimages_title'] = 'Other'
    if collection.basecollectionitem_set.not_instance_of(StatisticMap).count() > 0:
        context['images_visible'] = True
    else:
        context['images_visible'] = False
        if StatisticMap.objects.filter(collection=collection).filter(analysis_level='G').count()\
                > 0:
            context['gimages_visible'] = True
            context['gimages_active'] = True

        if StatisticMap.objects.filter(collection=collection).filter(analysis_level='S').count()\
                > 0:
            context['simages_visible'] = True
            if not context['gimages_visible']:
                context['simages_active'] = True

        if StatisticMap.objects.filter(collection=collection).filter(~Q(analysis_level__in=['G', 'S'])).count()\
                > 0:
            context['oimages_visible'] = True
            if not (context['gimages_visible'] or context['simages_visible']):
                context['oimages_active'] = True
                context['oimages_title'] = 'Images'

    communities = collection.communities.all()
    add_communities_context(communities, context)

    if not all(collection.basecollectionitem_set.instance_of(StatisticMap).values_list('is_valid', flat=True)):
        msg = "Some of the images in this collection are missing crucial metadata."
        if owner_or_contrib(request,collection):
            msg += " Please add the missing information by <a href='editmetadata'>editing images metadata</a>."
        context["messages"] = [msg]

    if not is_empty:
        context["first_image"] = collection.basecollectionitem_set.order_by("pk")[0]

    if owner_or_contrib(request,collection):
        form = UploadFileForm()
        c = RequestContext(request)
        c.update(context)
        return render_to_response('statmaps/collection_details.html', {'form': form}, c)
    else:
        return render(request, 'statmaps/collection_details.html', context) @login_required
def delete_collection(request, cid):
    collection = get_collection(cid,request)
    if not request.user.has_perm('statmaps.delete_collection', collection):
        return HttpResponseForbidden()
    collection.delete()
    return redirect('my_collections') def download_collection(request, cid):
    # Files (local path) to put in the .zip
    collection = get_collection(cid, request)

    filenames = [img.zip_file.path for img in collection.basecollectionitem_set.instance_of(NIDMResults)] + \
                [img.file.path for img in
                 (collection.basecollectionitem_set.instance_of(Image).not_instance_of(NIDMResultStatisticMap))]

    # Folder name in ZIP archive which contains the above files
    # E.g [collection.name.zip]/collection.name/img.id.nii.gz
    zip_subdir = collection.name
    zip_filename = '%s.zip' % collection.name

    zf = zipstream.ZipFile(mode='w', compression=zipstream.ZIP_DEFLATED)

    for fpath in filenames:
        # Calculate path for file in zip
        fdir, fname = os.path.split(fpath)
        zip_path = os.path.join(zip_subdir, fname)

        # Add file, at correct path
        zf.write(fpath, zip_path)

    response = StreamingHttpResponse(zf, content_type='application/zip')
    response['Content-Disposition'] = 'attachment; filename=%s' % urllib.quote_plus(zip_filename.encode('utf-8'))
    return response @login_required
def upload_folder(request, collection_cid):
    collection = get_collection(collection_cid,request)
    allowed_extensions = ['.nii', '.img', '.nii.gz']
    niftiFiles = []
    if request.method == 'POST':
        print request.POST
        print request.FILES
        form = UploadFileForm(request.POST, request.FILES)
        if form.is_valid():
            tmp_directory = tempfile.mkdtemp()
            print tmp_directory
            try:
                # Save archive (.zip or .tar.gz) to disk
                if "file" in request.FILES:
                    archive_name = request.FILES['file'].name
                    if fnmatch(archive_name,'*.nidm.zip'):
                        form = populate_nidm_results(request,collection)
                        if not form:
                            messages.warning(request, "Invalid NIDM-Results file.")
                        return HttpResponseRedirect(collection.get_absolute_url())

                    _, archive_ext = os.path.splitext(archive_name)
                    if archive_ext == '.zip':
                        compressed = zipfile.ZipFile(request.FILES['file'])
                    elif archive_ext == '.gz':
                        django_file = request.FILES['file']
                        django_file.open()
                        compressed = tarfile.TarFile(fileobj=gzip.GzipFile(fileobj=django_file.file, mode='r'), mode='r')
                    else:
                        raise Exception("Unsupported archive type %s."%archive_name)
                    compressed.extractall(path=tmp_directory)

                elif "file_input[]" in request.FILES:

                    for f, path in zip(request.FILES.getlist(
                                       "file_input[]"), request.POST.getlist("paths[]")):
                        if fnmatch(f.name,'*.nidm.zip'):
                            request.FILES['file'] = f
                            populate_nidm_results(request,collection)
                            continue

                        new_path, _ = os.path.split(os.path.join(tmp_directory, path))
                        mkdir_p(new_path)
                        filename = os.path.join(new_path,f.name)
                        tmp_file = open(filename, 'w')
                        tmp_file.write(f.read())
                        tmp_file.close()
                else:
                    raise Exception("Unable to find uploaded files.")

                atlases = {}
                for root, subdirs, filenames in os.walk(tmp_directory):
                    if detect_feat_directory(root):
                        populate_feat_directory(request,collection,root)
                        del(subdirs)
                        filenames = []

                    # .gfeat parent dir under cope*.feat should not be added as statmaps
                    # this may be affected by future nidm-results_fsl parsing changes
                    if root.endswith('.gfeat'):
                        filenames = []

                    filenames = [f for f in filenames if not f[0] == '.']
                    for fname in sorted(filenames):
                        name, ext = splitext_nii_gz(fname)
                        nii_path = os.path.join(root, fname)

                        if ext == '.xml':
                            dom = minidom.parse(os.path.join(root, fname))
                            for atlas in dom.getElementsByTagName("imagefile"):
                                path, base = os.path.split(atlas.lastChild.nodeValue)
                                nifti_name = os.path.join(path, base)
                                if nifti_name.startswith("/"):
                                    nifti_name = nifti_name[1:]
                                atlases[str(os.path.join(root,
                                            nifti_name))] = os.path.join(root, fname)
                        if ext in allowed_extensions:
                            nii = nib.load(nii_path)
                            if detect_4D(nii):
                                niftiFiles.extend(split_4D_to_3D(nii))
                            else:
                                niftiFiles.append((fname, nii_path))

                for label,fpath in niftiFiles:
                    # Read nifti file information
                    nii = nib.load(fpath)
                    if len(nii.get_shape()) > 3 and nii.get_shape()[3] > 1:
                        messages.warning(request, "Skipping %s - not a 3D file."%label)
                        continue
                    hdr = nii.get_header()
                    raw_hdr = hdr.structarr

                    # SPM only !!!
                    # Check if filename corresponds to a T-map
                    Tregexp = re.compile('spmT.*')
                    # Fregexp = re.compile('spmF.*')

                    if Tregexp.search(fpath) is not None:
                        map_type = StatisticMap.T
                    else:
                        # Check if filename corresponds to a F-map
                        if Tregexp.search(fpath) is not None:
                            map_type = StatisticMap.F
                        else:
                            map_type = StatisticMap.OTHER

                    path, name, ext = split_filename(fpath)
                    dname = name + ".nii.gz"
                    spaced_name = name.replace('_',' ').replace('-',' ')

                    squeezable_dimensions = len([a for a in nii.shape if a not in [0, 1]])

                    if (ext.lower() != ".nii.gz" or squeezable_dimensions < len(nii.shape)):

                        if squeezable_dimensions < len(nii.shape):
                            new_data = np.squeeze(nii.get_data())
                            nii = nib.Nifti1Image(new_data, nii.get_affine(),
                                                  nii.get_header())

                        new_file_tmp_dir = tempfile.mkdtemp()
                        new_file_tmp = os.path.join(new_file_tmp_dir, name) + '.nii.gz'
                        nib.save(nii, new_file_tmp)
                        f = ContentFile(open(new_file_tmp).read(), name=dname)
                        shutil.rmtree(new_file_tmp_dir)
                        label += " (old ext: %s)" % ext
                    else:
                        f = ContentFile(open(fpath).read(), name=dname)

                    collection = get_collection(collection_cid,request)

                    if os.path.join(path, name) in atlases:

                        new_image = Atlas(name=spaced_name,
                                          description=raw_hdr['descrip'], collection=collection)

                        new_image.label_description_file = ContentFile(
                                    open(atlases[os.path.join(path,name)]).read(),
                                                                    name=name + ".xml")
                    else:
                        new_image = StatisticMap(name=spaced_name, is_valid=False,
                                description=raw_hdr['descrip'] or label, collection=collection)
                        new_image.map_type = map_type

                    new_image.file = f
                    new_image.save()

            except:
                error = traceback.format_exc().splitlines()[-1]
                msg = "An error occurred with this upload: {}".format(error)
                messages.warning(request, msg)
                return HttpResponseRedirect(collection.get_absolute_url())
            finally:
                shutil.rmtree(tmp_directory)
            if not niftiFiles:
                messages.warning(request, "No NIFTI files (.nii, .nii.gz, .img/.hdr) found in the upload.")
            return HttpResponseRedirect(collection.get_absolute_url())
    else:
        form = UploadFileForm()
    return render_to_response("statmaps/upload_folder.html",
                              {'form': form},  RequestContext(request)) @login_required
def add_image_for_neurosynth(request):
    redirect_url = "http://neurosynth.org/decode/?neurovault=%(private_token)s-%(image_id)s"
    preamble = """Welcome to NeuroVault - a  public web repository for statistical 
maps of the brain. After filling in the minimum
information outlined below (only the fields with an 
asterisk [*] are compulsory), your map 
will be sent back to <a href="http://neurosynth.org">Neurosynth.org</a> for
decoding. <b>Remember that your map needs to be unthresholded and in MNI152
space for decoding to work correctly!</b>.
<b>By uploading your map you agree to make it publicly available under <a href="http://creativecommons.org/about/cc0">CC0 license</a>.</b>
You can always come back to <a href="/">NeuroVault.org</a> to 
inspect, modify, and/or delete your maps. If you wish to use Neurosynth decoding, but keep 
your maps private, consider creating a private collection."""

    return add_image_redirect(request, SimplifiedStatisticMapForm,
                              redirect_url, False, preamble) def serve_image(request, collection_cid, img_name):
    collection = get_collection(collection_cid,request,mode='file')
    path = os.path.join(settings.PRIVATE_MEDIA_ROOT,'images',str(collection.id),img_name)
    return sendfile(request, path, encoding="utf-8") def serve_pycortex(request, collection_cid, path, pycortex_dir='pycortex_all'):
    collection = get_collection(collection_cid,request,mode='file')
    int_path = os.path.join(settings.PRIVATE_MEDIA_ROOT,
                            'images',str(collection.id),pycortex_dir,path)
    return sendfile(request, int_path) def view_collection_with_pycortex(request, cid):
    volumes = {}
    collection = get_collection(cid,request,mode='file')
    images = collection.basecollectionitem_set.instance_of(Image)

    if not images:
        return redirect(collection)

    else:
        basedir = os.path.join(settings.PRIVATE_MEDIA_ROOT,'images',str(collection.id))
        baseurl = os.path.join(settings.PRIVATE_MEDIA_URL,cid)

        output_dir = os.path.join(basedir, "pycortex_all")
        html_path = os.path.join(basedir, "pycortex_all/index.html")
        pycortex_url = os.path.join(baseurl, "pycortex_all/index.html")

        if os.path.exists(output_dir):
            # check if collection contents have changed
            if (not os.path.exists(html_path)) or collection.modify_date > get_file_ctime(html_path):
                shutil.rmtree(output_dir)
                return view_collection_with_pycortex(request, cid)
        else:
            for image in images:
                vol = generate_pycortex_volume(image)
                volumes[image.name] = vol
            generate_pycortex_static(volumes, output_dir,
                                     title=collection.name)

        return redirect(pycortex_url) @login_required
def add_image(request, collection_cid):
    collection = get_collection(collection_cid,request)
    if not owner_or_contrib(request,collection):
        return HttpResponseForbidden()
    image = StatisticMap(collection=collection)
    if request.method == "POST":
        form = AddStatisticMapForm(request.POST, request.FILES, instance=image)
        if form.is_valid():
            image = form.save()
            return HttpResponseRedirect(image.get_absolute_url())
    else:
        form = AddStatisticMapForm(instance=image)

    contrasts = get_contrast_lookup()
    context = {"form": form,
               "contrasts": json.dumps(contrasts),
               "page_header": "Upload a statistical map"}
    return render(request, "statmaps/edit_image.html", context) def papaya_js_embed(request, pk, iframe=None):
    tpl = 'papaya_embed.tpl.js'
    mimetype = "text/javascript"
    if iframe is not None:
        tpl = 'papaya_frame.html'
        mimetype = "text/html"
    image = get_image(pk,None,request)
    if image.collection.private:
        return HttpResponseForbidden()
    context = {'image': image, 'request':request}
    return render_to_response('statmaps/%s' % tpl,
                              context, content_type=mimetype) def view_images_by_tag(request, tag):
    if request.user.is_authenticated():
        images = Image.objects.filter(tags__name__in=[tag]).filter(
                                        Q(collection__private=False) |
                                        Q(collection__owner=request.user))
    else:
        images = Image.objects.filter(tags__name__in=[tag]).filter(
                                        collection__private=False)
    context = {'images': images, 'tag': tag}
    return render(request, 'statmaps/images_by_tag.html', context) @login_required
def add_image_for_neuropower(request):
    redirect_url = "http://neuropowertools.org/neuropower/neuropowerinput/?neurovault=%(private_token)s-%(image_id)s"
    preamble = """Welcome to NeuroVault - a  public web repository for statistical
maps of the brain. After filling in the minimum
information outlined below (only the fields with an
asterisk [*] are compulsory), your map
will be sent back to <a href="http://neuropowertools.org">Neuropowertools.org</a> for
a power analysis. Your (pilot or preliminary) data will not be public, but we strongly encourage you to upload your full dataset after collection to Neurovault and make it public.
You can always come back to <a href="/">NeuroVault.org</a> to
inspect, modify, and/or delete your maps."""
    return add_image_redirect(request, NeuropowerStatisticMapForm,
                              redirect_url, True, preamble) def view_image_with_pycortex(request, pk, collection_cid=None):
    image = get_image(pk,collection_cid,request)
    base, fname, _ = split_filename(image.file.path)
    pycortex_dir = os.path.join(base, fname + "_pycortex")

    if not os.path.exists(pycortex_dir):
        volume = generate_pycortex_volume(image)
        generate_pycortex_static({image.name: volume}, pycortex_dir)

    _, _, ext = split_filename(image.file.url)
    pycortex_url = image.file.url[:-len(ext)] + "_pycortex/index.html"
    return redirect(pycortex_url) def stats_view(request):
    collections_by_journals = {}
    for collection in Collection.objects.filter(
                                private=False).exclude(Q(DOI__isnull=True) | Q(DOI__exact='')):
        if not collection.journal_name:
            _,_,_,_, collection.journal_name = get_paper_properties(collection.DOI)
            collection.save()
        if collection.journal_name not in collections_by_journals.keys():
            collections_by_journals[collection.journal_name] = 1
        else:
            collections_by_journals[collection.journal_name] += 1
    collections_by_journals = OrderedDict(sorted(collections_by_journals.items(
                                                ), key=lambda t: t[1], reverse=True))

    non_empty_collections_count = Collection.objects.annotate(num_submissions=Count('basecollectionitem')).filter(num_submissions__gt = 0).count()
    public_collections_count = Collection.objects.filter(private=False).annotate(num_submissions=Count('basecollectionitem')).filter(num_submissions__gt = 0).count()
    public_collections_with_DOIs_count = Collection.objects.filter(private=False).exclude(Q(DOI__isnull=True) | Q(DOI__exact='')).annotate(num_submissions=Count('basecollectionitem')).filter(num_submissions__gt = 0).count()
    context = {'collections_by_journals': collections_by_journals,
               'non_empty_collections_count': non_empty_collections_count,
               'public_collections_count': public_collections_count,
               'public_collections_with_DOIs_count': public_collections_with_DOIs_count}
    return render(request, 'statmaps/stats.html', context) def serve_nidm(request, collection_cid, nidmdir, sep, path):
    collection = get_collection(collection_cid, request, mode='file')
    basepath = os.path.join(settings.PRIVATE_MEDIA_ROOT, 'images')
    fpath = path if sep is '/' else ''.join([nidmdir, sep, path])
    try:
        nidmr = collection.basecollectionitem_set.instance_of(NIDMResults).get(name=nidmdir)
    except ObjectDoesNotExist:
        return HttpResponseForbidden()

    if path in ['zip', 'ttl']:
        fieldf = getattr(nidmr, '{0}_file'.format(path))
        fpath = fieldf.path
    else:
        zipfile = nidmr.zip_file.path
        fpathbase = os.path.dirname(zipfile)
        fpath = ''.join([fpathbase,sep,path])

    return sendfile(request, os.path.join(basepath, fpath), encoding="utf-8") def serve_nidm_image(request, collection_cid, nidmdir, sep, path):
    collection = get_collection(collection_cid,request,mode='file')
    path = os.path.join(settings.PRIVATE_MEDIA_ROOT,'images',str(collection.id),nidmdir,path)
    return sendfile(request, path, encoding="utf-8") def view_nidm_results(request, collection_cid, nidm_name):
    collection = get_collection(collection_cid,request)
    nidmr = get_object_or_404(NIDMResults, collection=collection,name=nidm_name)
    if request.method == "POST":
        if not request.user.has_perm("statmaps.change_basecollectionitem", nidmr):
            return HttpResponseForbidden()
        form = NIDMResultsForm(request.POST, request.FILES, instance=nidmr)
        if form.is_valid():
            instance = form.save(commit=False)
            instance.save()
            form.save_nidm()
            form.save_m2m()
            return HttpResponseRedirect(collection.get_absolute_url())
    else:
        if owner_or_contrib(request,collection):
            form = NIDMResultsForm(instance=nidmr)
        else:
            form = NIDMViewForm(instance=nidmr)

    # Generate viewer for nidm result
    nidm_files = [nidmr.ttl_file.path.encode("utf-8")]
    standard_brain = "/static/images/MNI152.nii.gz"

    # We will remove these scripts and a button
    remove_resources = ["BOOTSTRAPCSS","ROBOTOFONT","NIDMSELECTBUTTON",
                       "PAPAYAJS","PAPAYACSS"]

    # We will remove these columns
    columns_to_remove = ["statmap_location","statmap",
                         "statmap_type","coordinate_id",
                         "excsetmap_location"]

    # Text for the "Select image" button
    button_text = "Statistical Map"

    html_snippet = generate(nidm_files,
                            base_image=standard_brain,
                            remove_scripts=remove_resources,
                            columns_to_remove=columns_to_remove,
                            template_choice="embed",
                            button_text=button_text)

    context = {"form": form,"nidm_viewer":html_snippet}
    return render(request, "statmaps/edit_nidm_results.html", context) def find_similar(request, pk):
    image1 = get_image(pk, None, request)
    pk = int(pk)
    max_results = 10

    # Search only enabled if the image is not thresholded
    if image1.is_thresholded:
        error_message = "Image comparison is not enabled for thresholded images."
        context = {'error_message': error_message}
        return render(request, 'statmaps/error_message.html', context)

    image_title = format_image_collection_names(image_name=image1.name,
                                                    collection_name=image1.collection.name,
                                                    map_type=image1.map_type, total_length=50)
    # Here is the query image
    query_png = image1.thumbnail.url

    context = {
        'image': image1,
        'image_title': image_title,
        'query_png': query_png
    }
    template = 'statmaps/compare_search.html'
    return render(request, template, context) def find_similar_json(request, pk, collection_cid=None):

    limit = 500 # maximum number of Comparisons allowed to retrieve
    max_results = int(request.GET.get('q', '100'))
    if max_results > limit:
        max_results = limit

    image1 = get_image(pk, None, request)
    pk = int(pk)

    # Search only enabled if the image is not thresholded
    if image1.is_thresholded:
        return JSONResponse('error: Image comparison is not enabled for thresholded images.', status=400)
    else:
        similar_images = get_similar_images(pk, max_results)

    dict = similar_images.to_dict("split")
    del dict["index"]
    return JSONResponse(dict) def compare_images(request,pk1,pk2):
    import numpy as np
    image1 = get_image(pk1,None,request)
    image2 = get_image(pk2,None,request)
    images = [image1,image2]

    # Get image: collection: [map_type] names no longer than ~125 characters
    image1_custom_name = format_image_collection_names(image_name=image1.name,
                                                       collection_name=image1.collection.name,
                                                       map_type=image1.map_type,total_length=125)
    image2_custom_name = format_image_collection_names(image_name=image2.name,
                                                       collection_name=image2.collection.name,
                                                       map_type=image2.map_type,total_length=125)

    image_names = [image1_custom_name,image2_custom_name]

    # Create custom links for the visualization
    custom = {
            "IMAGE_1_LINK":"/images/%s" % (image1.pk),
            "IMAGE_2_LINK":"/images/%s" % (image2.pk)
    }

    # create reduced representation in case it's not there
    if not image1.reduced_representation or not os.path.exists(image1.reduced_representation.path):
        image1 = save_resampled_transformation_single(image1.id) # cannot run this async
    if not image2.reduced_representation or not os.path.exists(image2.reduced_representation.path):
        image2 = save_resampled_transformation_single(image2.id) # cannot run this async

    # Load image vectors from npy files
    image_vector1 = np.load(image1.reduced_representation.file)
    image_vector2 = np.load(image2.reduced_representation.file)

    # Load atlas pickle, containing vectors of atlas labels, colors, and values for same voxel dimension (4mm)
    this_path = os.path.abspath(os.path.dirname(__file__))
    atlas_pkl_path = os.path.join(this_path, 'static/atlas/atlas_mni_4mm.pkl')
    atlas = joblib.load(atlas_pkl_path)

    # Load the atlas svg, so we don't need to dynamically generate it
    atlas_svg = os.path.join(this_path, 'static/atlas/atlas_mni_2mm_svg.pkl')
    atlas_svg = joblib.load(atlas_svg)

    # Generate html for similarity search, do not specify atlas
    html_snippet, _ = scatterplot_compare_vector(image_vector1=image_vector1,
                                                                 image_vector2=image_vector2,
                                                                 image_names=image_names,
                                                                 atlas_vector=atlas["atlas_vector"],
                                                                 atlas_labels=atlas["atlas_labels"],
                                                                 atlas_colors=atlas["atlas_colors"],
                                                                 corr_type="pearson",
                                                                 subsample_every=10, # subsample every 10th voxel
                                                                 custom=custom,
                                                                 remove_scripts="D3_MIN_JS",
                                                                 width=1000)

    # Add atlas svg to the image, and prepare html for rendering
    html = [h.replace("[coronal]",atlas_svg) for h in html_snippet]
    html = [h.strip("\n").replace("[axial]","").replace("[sagittal]","") for h in html]
    context = {'html': html}

    # Determine if either image is thresholded
    threshold_status = np.array([image_names[i] for i in range(0,2) if images[i].is_thresholded])
    if len(threshold_status) > 0:
        warnings = list()
        for i in range(0,len(image_names)):
            warnings.append('Warning: Thresholded image: %s (%.4g%% of voxels are zeros),' %(image_names[i],images[i].perc_bad_voxels))
        context["warnings"] = warnings

    return render(request, 'statmaps/compare_images.html', context) @csrf_exempt
@login_required
def edit_metadata(request, collection_cid):
    collection = get_collection(collection_cid, request)

    if not owner_or_contrib(request, collection):
        return HttpResponseForbidden()

    if not collection.is_statisticmap_set:
        return HttpResponseForbidden('Editing image metadata of collections '
                                     'that include not only statistical '
                                     'maps is forbidden.')

    if request.method == "POST":
        return JSONResponse(
            **image_metadata.handle_post_metadata(
                request, collection, 'Images metadata have been saved.'))

    collection_images = collection.basecollectionitem_set.instance_of(Image).order_by('pk')
    data_headers = image_metadata.get_data_headers(collection_images)
    metadata = image_metadata.get_images_metadata(collection_images)
    datasources = get_field_datasources()

    return render(request, "statmaps/edit_metadata.html", {
        'collection': collection,
        'datasources': json.dumps(datasources),
        'data_headers': json.dumps(data_headers),
        'metadata': json.dumps(metadata)}) @login_required
def export_images_filenames(request, collection_cid):
    collection = get_collection(collection_cid, request)
    images_filenames = image_metadata.get_images_filenames(collection)

    response = HttpResponse(content_type='text/csv')
    response['Content-Disposition'] = 'attachment; ' \
                                      'filename="collection_%s.csv"' % (
                                          collection.id)

    writer = csv.writer(response)
    writer.writerows([['Filename']] + [[name] for name in images_filenames])

    return response @login_required
def delete_nidm_results(request, collection_cid, nidm_name):
    collection = get_collection(collection_cid,request)
    nidmr = get_object_or_404(NIDMResults, collection=collection, name=nidm_name)

    if request.user.has_perm("statmaps.delete_basecollectionitem", nidmr):
        nidmr.delete()
        return redirect('collection_details', cid=collection_cid)
    else:
        return HttpResponseForbidden() def view_task(request, cog_atlas_id=None):
    '''view_task returns a view to see a group of images associated with a particular cognitive atlas task.
    :param cog_atlas_id: statmaps.models.CognitiveAtlasTask the id for the task defined in the Cognitive Atlas
    '''
    from cogat_functions import get_task_graph

    # Get the cognitive atlas id
    if not cog_atlas_id:
        return search(request, error_message="Please search for a Cognitive Atlas task to see the task view.")

    try:
        task = CognitiveAtlasTask.objects.get(cog_atlas_id=cog_atlas_id)
    except ObjectDoesNotExist:
        return search(request, error_message="Invalid search for Cognitive Atlas.")

    if task:
        images = StatisticMap.objects.filter(cognitive_paradigm_cogatlas=cog_atlas_id,
                                             collection__private=False).order_by("pk")

        if len(images) > 0:
            first_image = images[0]
            graph = get_task_graph(cog_atlas_id, images=images)

            # Which images aren't tagged with contrasts?
            not_tagged = images.filter(cognitive_contrast_cogatlas__isnull=True)

            context = {'task': task,
                       'first_image': first_image,
                       'cognitive_atlas_tree': graph,
                       'tree_divid': "tree",  # div id in template to append tree svg to
                       'images_without_contrasts': not_tagged}

            return render(request, 'cogatlas/cognitive_atlas_task.html', context)

    # If task does not have images
    context = {"no_task_images": True, # robots won't index page if defined
               "task": task }
    return render(request, 'cogatlas/cognitive_atlas_task.html', context) def search(request,error_message=None):
    cogatlas_task = CognitiveAtlasTask.objects.all()
    context = {'message': error_message,
               'cogatlas_task': cogatlas_task}
    return render(request, 'statmaps/search.html', context) def gene_expression_json(request, pk, collection_cid=None):
    image = get_image(pk, collection_cid, request)
    if image.is_thresholded:
        raise Http404

    if not image.reduced_representation or not os.path.exists(image.reduced_representation.path):
        image = save_resampled_transformation_single(image.id)

    map_data = np.load(image.reduced_representation.file)

    mask = request.GET.get('mask', None)
    expression_results = calculate_gene_expression_similarity(map_data, mask)
    dict = expression_results.to_dict("split")
    del dict["index"]
    return JSONResponse(dict) def gene_expression(request, pk, collection_cid=None):
    '''view_image returns main view to see an image and associated meta data. If the image is in a collection with a DOI and has a generated thumbnail, it is a contender for image comparison, and a find similar button is exposed.
    :param pk: statmaps.models.Image.pk the primary key of the image
    :param collection_cid: statmaps.models.Collection.pk the primary key of the collection. Default None
    '''
    image = get_image(pk, collection_cid, request)
    if image.is_thresholded:
        raise Http404
    api_cid = pk
    if image.collection.private:
        api_cid = '%s-%s' % (image.collection.private_token,pk)
    context = {
        'image': image,
        'api_cid': api_cid,
        'mask': request.GET.get('mask', 'full')
    }
    template = 'statmaps/gene_expression.html'
    return render(request, template, context) def serve_surface_archive(request, pk, collection_cid=None):
    img = get_image(pk,collection_cid,request)
    user_owns_image = owner_or_contrib(request,img.collection)
    api_cid = pk

    filenames = [img.surface_left_file.path, img.surface_right_file.path]

    # Folder name in ZIP archive which contains the above files
    # E.g [collection.name.zip]/collection.name/img.id.nii.gz
    zip_subdir = img.name
    zip_filename = '%s.zip' % img.name

    zf = zipstream.ZipFile(mode='w', compression=zipstream.ZIP_DEFLATED)

    for fpath in filenames:
        # Calculate path for file in zip
        fdir, fname = os.path.split(fpath)
        zip_path = os.path.join(zip_subdir, fname)

        # Add file, at correct path
        zf.write(fpath, zip_path)

    response = StreamingHttpResponse(zf, content_type='application/zip')
    response['Content-Disposition'] = 'attachment; filename=%s' % zip_filename
    return response @login_required
def edit_metaanalysis(request, metaanalysis_id=None):
    '''edit_collection is a view to edit a collection, meaning showing the edit form for an existing collection, creating a new collection, or passing POST data to update an existing collection
    :param cid: statmaps.models.Collection.pk the primary key of the collection. If none, will generate form to make a new collection.
    '''
    page_header = "Start a new metaanalysis"
    if metaanalysis_id:
        metaanalysis = Metaanalysis.objects.get(pk=metaanalysis_id)
        page_header = 'Edit metaanalysis'
        if request.user != metaanalysis.owner:
            return HttpResponseForbidden()
    else:
        metaanalysis = Metaanalysis(owner=request.user)
    if request.method == "POST":
        form = MetaanalysisForm(request.POST, request.FILES, instance=metaanalysis)
        if form.is_valid():
            metaanalysis.save()
            return HttpResponseRedirect(metaanalysis.get_absolute_url())
    else:
        form = MetaanalysisForm(instance=metaanalysis)

    context = {"form": form, "page_header": page_header}
    return render(request, "statmaps/edit_metaanalysis.html", context) def view_metaanalysis(request, metaanalysis_id):
    '''view_collection returns main view to see an entire collection of images, meaning a viewer and list of images to load into it.
    :param cid: statmaps.models.Collection.pk the primary key of the collection
    '''
    metaanalysis = get_object_or_404(Metaanalysis, pk=metaanalysis_id)
    context = {'metaanalysis': metaanalysis}
    return render(request, 'statmaps/metaanalysis_details.html', context) @login_required
def activate_metaanalysis(request, metaanalysis_id):
    metaanalysis = Metaanalysis.objects.get(pk=metaanalysis_id)
    if request.user != metaanalysis.owner or metaanalysis.status == 'completed':
        return HttpResponseForbidden()
    metaanalysis.status = 'active'
    metaanalysis.save()
    return HttpResponseRedirect(metaanalysis.get_absolute_url()) @login_required
def finalize_metaanalysis(request, metaanalysis_id):
    metaanalysis = Metaanalysis.objects.get(pk=metaanalysis_id)
    if request.user != metaanalysis.owner or metaanalysis.status == 'completed':
        return HttpResponseForbidden()

    new_collection = Collection(name="Metaanalysis %d: %s" % (metaanalysis.pk, metaanalysis.name),
                                owner=metaanalysis.owner,
                                full_dataset_url=metaanalysis.get_absolute_url())
    new_collection.save()

    POSSIBLE_TEMPLATES = get_possible_templates()
    mask_path = POSSIBLE_TEMPLATES[DEFAULT_TEMPLATE]['mask']
    this_path = os.path.abspath(os.path.dirname(__file__))
    mask_img = nib.load(os.path.join(this_path, "static", 'anatomical',mask_path))

    z_imgs = []
    sizes = []
    for img in metaanalysis.maps.all():
        valid = False
        if img.map_type == 'Z':
            z_imgs.append(resample_to_img(nib.load(img.file.path), mask_img))
            valid = True
        elif img.map_type == 'T' and img.number_of_subjects:
            t_map_nii = nib.load(img.file.path)
            # assuming one sided test
            z_map_nii = nib.Nifti1Image(t_to_z(t_map_nii.get_data(), img.number_of_subjects - 1),
                                        t_map_nii.affine)
            z_imgs.append(resample_to_img(z_map_nii, mask_img))
            valid = True

        if valid and img.number_of_subjects:
            sizes.append(img.number_of_subjects)

    do_weighted = len(sizes) == len(z_imgs)

    if do_weighted:
        new_collection.description = "Two sided Stouffer's fixed-effects image-based " \
                                     "meta-analysis on " \
                                     "%d " \
                                     "maps weighted by their corresponding sample sizes. FWE " \
                                     "corrected with theoretical null distribution."% len(z_imgs)
    else:
        new_collection.description = "Two sided Stouffer's fixed-effects image-based " \
                                     "meta-analysis on %d " \
                                     "maps. FWE " \
                                     "corrected with theoretical null distribution." % len(
            z_imgs)

    new_collection.save()

    z_data = apply_mask(z_imgs, mask_img)
    if do_weighted:
        result = weighted_stouffers(z_data, np.array(sizes), mask_img, corr='FWE', two_sided=True)
    else:
        result = stouffers(z_data, mask_img, inference='ffx', null='theoretical', corr='FWE',
                           two_sided=True)

    z_map = StatisticMap(name='FWE corrected Z map', description='', collection=new_collection,
                         modality='Other',
                         map_type='Z',
                         analysis_level='M',
                         target_template_image='GenericMNI')
    p_map = StatisticMap(name='FWE corrected P map', description='', collection=new_collection,
                         modality='Other',
                         map_type='P',
                         analysis_level='M',
                         number_of_subjects=-1,
                         target_template_image='GenericMNI')
    if do_weighted:
        ffx_map = StatisticMap(name='FFX map', description='', collection=new_collection,
                               modality='Other',
                               map_type='Other',
                               analysis_level='M',
                               target_template_image='GenericMNI')

    tmp_dir = tempfile.mkdtemp()
    try:
        z_path = os.path.join(tmp_dir, 'z_fwe_corr.nii.gz')
        result.images['z'].to_filename(z_path)
        z_map.file = SimpleUploadedFile('z_fwe_corr.nii.gz',
                                        open(z_path).read())
        z_map.save()

        p_path = os.path.join(tmp_dir, 'p_fwe_corr.nii.gz')
        result.images['p'].to_filename(p_path)
        p_map.file = SimpleUploadedFile('p_fwe_corr.nii.gz',
                                        open(p_path).read())
        p_map.save()
        if do_weighted:
            ffx_path = os.path.join(tmp_dir, 'ffx_stat.nii.gz')
            result.images['ffx_stat'].to_filename(ffx_path)
            ffx_map.file = SimpleUploadedFile('ffx_stat.nii.gz',
                                            open(ffx_path).read())
            ffx_map.save()
    finally:
        shutil.rmtree(tmp_dir)

    metaanalysis.output_maps = new_collection
    metaanalysis.status='completed'
    metaanalysis.save()

    return HttpResponseRedirect(new_collection.get_absolute_url())
from django.conf.urls import patterns, url
from django.contrib.auth.decorators import login_required
from django.views.generic import ListView, TemplateView
from django.views.generic.base import RedirectView
from neurovault import settings
from neurovault.apps.statmaps.models import KeyValueTag
from neurovault.apps.statmaps.views import ImagesInCollectionJson, \
    PublicCollectionsJson, MyCollectionsJson, AtlasesAndParcellationsJson, \
    ImagesByTaskJson, GroupImagesInCollectionJson, SingleSubjectImagesInCollectionJson, \
    OtherImagesInCollectionJson, AllDOIPublicGroupImages, MyMetaanalysesJson, \
    toggle_active_metaanalysis
from .views import edit_collection, view_image, delete_image, edit_image, \
                view_collection, delete_collection, download_collection, upload_folder, add_image_for_neurosynth, \
                serve_image, serve_pycortex, view_collection_with_pycortex, add_image, \
                papaya_js_embed, view_images_by_tag, add_image_for_neuropower, \
                view_image_with_pycortex, stats_view, serve_nidm, serve_nidm_image, \
                view_nidm_results, find_similar, find_similar_json, compare_images, edit_metadata, \
                export_images_filenames, delete_nidm_results, view_task, search, gene_expression_json, \
                gene_expression, serve_surface_archive, edit_metaanalysis, view_metaanalysis, \
                activate_metaanalysis, finalize_metaanalysis
        edit_metaanalysis,
       name='edit_metaanalysis'),
    url(r'^metaanalyses/(?P<metaanalysis_id>\d+)/activate$',
        activate_metaanalysis,
       name='activate_metaanalysis'),
    url(r'^metaanalyses/(?P<metaanalysis_id>\d+)/finalize$',
        finalize_metaanalysis,
       name='finalize_metaanalysis'),
    url(r'^metaanalyses/(?P<metaanalysis_id>\d+)$',
       view_metaanalysis,
       name='view_metaanalysis'),
    url(r'^metaanalyses/(?P<metaanalysis_id>\d+)/images_json',
        AllDOIPublicGroupImages.as_view(),
       name='metaanalysis_images_json'),
    url(r'^my_metaanalyses/json$',
        login_required(MyMetaanalysesJson.as_view()),
        name='my_metaanalyses_json'),
    url(r'^metaanalysis_selection/json$',
        AllDOIPublicGroupImages.as_view(),
       name='metaanalysis_selection_json'),
    url(r'^my_collections/$',
        login_required(TemplateView.as_view(template_name='statmaps/my_collections.html')),
        name='my_collections'),
    url(r'^my_collections/json$',
        login_required(MyCollectionsJson.as_view()),
        name='my_collections_json'),
    url(r'^collections/$',
        TemplateView.as_view(template_name='statmaps/collections_index.html'),
        name='collections_list'),
    url(r'^collections/json$',
        PublicCollectionsJson.as_view(),
        name='collections_list_json'),
    url(r'^collections/stats$',
        stats_view,
        name='collections_stats'),
    url(r'^collections/(?P<cid>\d+|[A-Z]{8})/$',
        view_collection,
        name='collection_details'),
    url(r'^collections/(?P<cid>\d+|[A-Z]{8})/json$',
        ImagesInCollectionJson.as_view(),
        name='collection_images_json'),
    url(r'^collections/(?P<cid>\d+|[A-Z]{8})/gjson$',
        GroupImagesInCollectionJson.as_view(),
        name='collection_gimages_json'),
    url(r'^collections/(?P<cid>\d+|[A-Z]{8})/sjson$',
        SingleSubjectImagesInCollectionJson.as_view(),
        name='collection_simages_json'),
    url(r'^collections/(?P<cid>\d+|[A-Z]{8})/ojson$',
        OtherImagesInCollectionJson.as_view(),
        name='collection_oimages_json'),
    url(r'^collections/new$',
        edit_collection,
        name='new_collection'),
    url(r'^collections/(?P<cid>\d+|[A-Z]{8})/edit$',
        edit_collection,
        name='edit_collection'),
    url(r'^collections/(?P<cid>\d+|[A-Z]{8})/delete$',
        delete_collection,
        name='delete_collection'),
    url(r'^collections/(?P<cid>\d+|[A-Z]{8})/download$',
       download_collection,
       name='download_collection'),
    url(r'^collections/(?P<collection_cid>\d+|[A-Z]{8})/addimage',
        add_image,
        name="add_image"),
    url(r'^collections/(?P<collection_cid>\d+|[A-Z]{8})/upload_folder$',
        upload_folder,
        name="upload_folder"),
    url(r'^collections/(?P<collection_cid>\d+|[A-Z]{8})/images/(?P<pk>\d+)/$',
        view_image,
        name="private_image_details"),
    url(r'^collections/(?P<cid>\d+|[A-Z]{8})/pycortex$',
        view_collection_with_pycortex,
        name='view_collection_pycortex'),
    url(r'^collections/(?P<collection_cid>\d+|[A-Z]{8})/export/imagesfilenames$',
        export_images_filenames,
        name="export_images_filenames"),
    url(r'^collections/(?P<collection_cid>\d+|[A-Z]{8})/editmetadata$',
        edit_metadata,
        name="edit_metadata"),
    url(r'^atlases/$', TemplateView.as_view(template_name="statmaps/atlases.html"),
        name="atlases_and_parcellations"),
    url(r'^atlases/json$',
        AtlasesAndParcellationsJson.as_view(),
        name='atlases_and_parcellations_json'),
    url(r'^images/tags/$',
        ListView.as_view(
            queryset=KeyValueTag.objects.all(),
            context_object_name='tags',
            template_name='statmaps/tags_index.html'),
        name='tags_list'),
    url(r'^images/tags/(?P<tag>[A-Za-z0-9@\.\+\-\_\s]+)/$',
        view_images_by_tag,
        name='images_by_tag'),
    url(r'^images/(?P<pk>\d+)/$',
        view_image,
        name='image_details'),
    url(r'^images/(?P<pk>\d+)/toggle_active_meta$',
        toggle_active_metaanalysis,
        name='toggle_active_metaanalysis'),
    url(
        r'^collections/(?P<collection_cid>\d+|[A-Z]{8})/images/(?P<pk>\d+)/toggle_active_meta$',
        toggle_active_metaanalysis,
        name="private_toggle_active_metaanalysis"),
    url(r'^images/(?P<pk>\d+)/pycortex$',
        view_image_with_pycortex,
        name='pycortex_view_image'),
    url(r'^collections/(?P<collection_cid>\d+|[A-Z]{8})/images/(?P<pk>\d+)/pycortex$',
        view_image_with_pycortex,
        name="private_pycortex_view_image"),
    url(r'^images/(?P<pk>\d+)/edit$',
        edit_image,
        name='edit_image'),
    url(r'^images/(?P<pk>\d+)/delete$',
        delete_image,
        name='delete_image'),
    url(r'^images/add_for_neurosynth$',
        add_image_for_neurosynth,
        name='add_for_neurosynth'),
    url(r'^images/add_for_neuropower$',

        add_image_for_neuropower,