<commit_before>#!/usr/bin/env python2.5
#
# Copyright 2011 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Module for the GSoC participant dashboard.
"""

__authors__ = [
  '"Lennard de Rijk" <ljvderijk@gmail.com>',
  ]

from soc.logic.lists import Lists
from soc.views.helper import lists
from soc.views.template import Template

from soc.modules.gsoc.logic.models.student_project import logic as \
    project_logic
from soc.modules.gsoc.views.base import RequestHandler
from soc.modules.gsoc.views.helper import url_patterns

class Dashboard(RequestHandler):
  """View for the participant dashboard.
  """

  def djangoURLPatterns(self):
    """The URL pattern for the dashboard.
    """
    return [(r'^gsoc/dashboard/%s$' % url_patterns.PROGRAM, self)]

  def checkAccess(self):
    """Denies access if you don't have a role in the current program.
    """
    # TODO: Should raise exception when you are not a participant in the program
    return

  def templatePath(self):
    """Returns the path to the template.
    """
    return 'v2/modules/gsoc/dashboard/base.html'

  def context(self):
    """Handler for default HTTP GET request.
    """
    components = []
    if self.data.student:
      components = self._getStudentComponents()
    elif self.data.mentors or self.data.org_admins:
      components = self._getOrgMemberComponents()

    context = {}
    context['page_name'] = self.data.program.name
    context['user'] = self.data.user
    context['alert_msg'] = 'Default Messsage goes here'
    context['components'] = components

    return context

  def _getStudentComponents(self):
    """Get the dashboard components for a student.
    """
    # Add all the proposals of this current user
    components = [MyProposalsComponent(self)]

    fields = {'student': self.data.student}
    project = project_logic.getForFields(fields, unique=True)
    if project:
      # Add a component to show all the projects
      #MyProjectsComponent()
      # Add a component to show the evaluations
      #MyEvaluationsComponent()
      pass

    return components

  def _getOrgMemberComponents(self):
    """Get the dashboard components for Organization members.
    """
    # Figure out which orgs are involved
    #org_roles = self.data.mentors + self.data.org_admins
    pass

class Component(Template):
  """A component on the dashboard.
  """

  def __init__(self, handler, name, title):
    """Initializes the name and title of the component.

    Args:
      name: the name of the component, used for the identifier in the HTML.
      title: the display title of the component.
    """
    self._handler = handler
    self._name = name
    self._title = title

  def context(self):
    """Returns the context of a base component.
    """
    return {'title': self._title,
            'name': self._name}

class MyProposalsComponent(Component):
  """Component for listing all the proposals of the current Student.
  """

  def __init__(self, handler):
    """Initializes the template location, name and title of this component.
    """
    super(MyProposalsComponent, self).__init__(handler, name="proposals",
                                               title="PROPOSALS")

  def templatePath(self):
    """Returns the path to the template that should be used in render().
    """
    return'v2/modules/gsoc/dashboard/proposals_component.html'

  def context(self):
    """Returns the context of this component.
    """
    from soc.modules.gsoc.views.models.student_proposal import view

    context = super(MyProposalsComponent, self).context()
    list_config = lists.getListGenerator(self._handler.request,
                                         view.getParams())
    context['lists'] = Lists([list_config])
    return context
<commit_msg>Remove Component class<commit_after>#!/usr/bin/env python2.5
#
# Copyright 2011 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Module for the GSoC participant dashboard.
"""

__authors__ = [
  '"Lennard de Rijk" <ljvderijk@gmail.com>',
  ]

from soc.logic.lists import Lists
from soc.views.helper import lists
from soc.views.template import Template

from soc.modules.gsoc.logic.models.student_project import logic as \
    project_logic
from soc.modules.gsoc.views.base import RequestHandler
from soc.modules.gsoc.views.helper import url_patterns

class Dashboard(RequestHandler):
  """View for the participant dashboard.
  """

  def djangoURLPatterns(self):
    """The URL pattern for the dashboard.
    """
    return [(r'^gsoc/dashboard/%s$' % url_patterns.PROGRAM, self)]

  def checkAccess(self):
    """Denies access if you don't have a role in the current program.
    """
    # TODO: Should raise exception when you are not a participant in the program
    return

  def templatePath(self):
    """Returns the path to the template.
    """
    return 'v2/modules/gsoc/dashboard/base.html'

  def context(self):
    """Handler for default HTTP GET request.
    """
    components = []
    if self.data.student:
      components = self._getStudentComponents()
    elif self.data.mentors or self.data.org_admins:
      components = self._getOrgMemberComponents()

    context = {}
    context['page_name'] = self.data.program.name
    context['user'] = self.data.user
    context['alert_msg'] = 'Default Messsage goes here'
    context['components'] = components

    return context

  def _getStudentComponents(self):
    """Get the dashboard components for a student.
    """
    # Add all the proposals of this current user
    components = [MyProposalsComponent(self)]

    fields = {'student': self.data.student}
    project = project_logic.getForFields(fields, unique=True)
    if project:
      # Add a component to show all the projects
      #MyProjectsComponent()
      # Add a component to show the evaluations
      #MyEvaluationsComponent()
      pass

    return components

  def _getOrgMemberComponents(self):
    """Get the dashboard components for Organization members.
    """
    # Figure out which orgs are involved
    #org_roles = self.data.mentors + self.data.org_admins
    pass

class MyProposalsComponent(Component):
  """Component for listing all the proposals of the current Student.
  """

  def __init__(self, handler):
    """Initializes the template location, name and title of this component.
    """
    self.handler = handle

  def templatePath(self):
    """Returns the path to the template that should be used in render().
    """
    return'v2/modules/gsoc/dashboard/proposals_component.html'

  def context(self):
    """Returns the context of this component.
    """
    from soc.modules.gsoc.views.models.student_proposal import view

    list_config = lists.getListGenerator(self._handler.request,
                                         view.getParams())
    return {
        'name': "proposals",
        'title': "PROPOSALS"
        'lists' = Lists([list_config])
    }
<|endoftext|>


<commit_before>import bson
import json
import pytz
import os.path
import tarfile
import datetime
import cStringIO

from . import base
from . import validators

from . import util
from . import config

log = config.log

def _filter_check(property_filter, property_values):
    minus = set(property_filter.get('-', []))
    plus = set(property_filter.get('+', []))
    if not minus.isdisjoint(property_values):
        return False
    if plus and plus.isdisjoint(property_values):
        return False
    return True

def _append_targets(targets, container, prefix, total_size, total_cnt, optional, data_path, filters):
    for f in container.get('files', []):
        if filters:
            filtered = True
            for filter_ in filters:
                type_as_list = [f['type']] if f.get('type') else []
                if (
                    _filter_check(filter_.get('tags', {}), f.get('tags', [])) and
                    _filter_check(filter_.get('types', {}), type_as_list)
                    ):
                    filtered = False
                    break
            if filtered:
                continue
        if optional or not f.get('optional', False):
            filepath = os.path.join(data_path, util.path_from_hash(f['hash']))
            if os.path.exists(filepath): # silently skip missing files
                targets.append((filepath, prefix + '/' + f['name'], f['size']))
                total_size += f['size']
                total_cnt += 1
    return total_size, total_cnt

def symlinkarchivestream(ticket, data_path):
    for filepath, arcpath, _ in ticket['target']:
        t = tarfile.TarInfo(name=arcpath)
        t.type = tarfile.SYMTYPE
        t.linkname = os.path.relpath(filepath, data_path)
        yield t.tobuf()
    stream = cStringIO.StringIO()
    with tarfile.open(mode='w|', fileobj=stream) as archive:
        pass
    yield stream.getvalue() # get tar stream trailer
    stream.close()

def archivestream(ticket):
    BLOCKSIZE = 512
    CHUNKSIZE = 2**20  # stream files in 1MB chunks
    stream = cStringIO.StringIO()
    with tarfile.open(mode='w|', fileobj=stream) as archive:
        for filepath, arcpath, _ in ticket['target']:
            yield archive.gettarinfo(filepath, arcpath).tobuf()
            with open(filepath, 'rb') as fd:
                for chunk in iter(lambda: fd.read(CHUNKSIZE), ''):
                    yield chunk
                if len(chunk) % BLOCKSIZE != 0:
                    yield (BLOCKSIZE - (len(chunk) % BLOCKSIZE)) * b'\0'
    yield stream.getvalue() # get tar stream trailer
    stream.close()

class Download(base.RequestHandler):

    def _bulk_preflight_archivestream(self, file_refs):
        data_path = config.get_item('persistent', 'data_path')
        arc_prefix = 'sdm'
        file_cnt = 0
        total_size = 0
        targets = []

        for fref in file_refs:
            cont_name   = fref.get('container_name','')+'s'
            cont_id     = fref.get('container_id', '')
            filename    = fref.get('filename', '')

            if cont_name not in ['projects', 'sessions', 'acquisitions']:
                self.abort(400, 'Bulk download only supports files in projects, sessions and acquisitions')
            file_obj = None
            try:
                # Try to find the file reference in the database (filtering on user permissions)
                bid = bson.ObjectId(cont_id)
                query = {'_id': bid}
                if not self.superuser_request:
                    query['permissions._id'] = self.uid
                file_obj = config.db[cont_name].find_one(
                    query,
                    {'files': { '$elemMatch': {
                        'name': filename
                    }}
                })['files'][0]
            except:
                # self.abort(404, 'File {} on Container {} {} not found'.format(filename, cont_name, cont_id))
                # silently skip missing files/files user does not have access to
                continue

            filepath = os.path.join(data_path, util.path_from_hash(file_obj['hash']))
            if os.path.exists(filepath): # silently skip missing files
                targets.append((filepath, cont_name+'/'+cont_id+'/'+file_obj['name'], file_obj['size']))
                total_size += file_obj['size']
                file_cnt += 1

        if len(targets) > 0:
            filename = arc_prefix + datetime.datetime.utcnow().strftime('%Y%m%d_%H%M%S') + '.tar'
            ticket = util.download_ticket(self.request.client_addr, 'batch', targets, filename, total_size)
            config.db.downloads.insert_one(ticket)
            return {'ticket': ticket['_id'], 'file_cnt': file_cnt, 'size': total_size}
        else:
            self.abort(404, 'No files requested could be found')

    def _preflight_archivestream(self, req_spec):
        data_path = config.get_item('persistent', 'data_path')
        arc_prefix = 'sdm'
        file_cnt = 0
        total_size = 0
        targets = []
        # FIXME: check permissions of everything
        used_subpaths = {}
        for item in req_spec['nodes']:
            item_id = bson.ObjectId(item['_id'])
            if item['level'] == 'project':
                project = config.db.projects.find_one({'_id': item_id}, ['group', 'label', 'files'])
                prefix = '/'.join([arc_prefix, project['group'], project['label']])
                total_size, file_cnt = _append_targets(targets, project, prefix, total_size, file_cnt, req_spec['optional'], data_path, req_spec.get('filters'))
                sessions = config.db.sessions.find({'project': item_id}, ['label', 'files', 'uid', 'timestamp', 'timezone'])
                session_dict = {session['_id']: session for session in sessions}
                acquisitions = config.db.acquisitions.find({'session': {'$in': session_dict.keys()}}, ['label', 'files', 'session', 'uid', 'timestamp', 'timezone'])
                session_prefixes = {}
                for session in session_dict.itervalues():
                    session_prefix = prefix + '/' + self._path_from_container(session, used_subpaths, project['_id'])
                    session_prefixes[session['_id']] = session_prefix
                    total_size, file_cnt = _append_targets(targets, session, session_prefix, total_size, file_cnt, req_spec['optional'], data_path, req_spec.get('filters'))
                for acq in acquisitions:
                    session = session_dict[acq['session']]
                    acq_prefix = session_prefixes[session['_id']] + '/' + self._path_from_container(acq, used_subpaths, session['_id'])
                    total_size, file_cnt = _append_targets(targets, acq, acq_prefix, total_size, file_cnt, req_spec['optional'], data_path, req_spec.get('filters'))
            elif item['level'] == 'session':
                session = config.db.sessions.find_one({'_id': item_id}, ['project', 'label', 'files', 'uid', 'timestamp', 'timezone'])
                project = config.db.projects.find_one({'_id': session['project']}, ['group', 'label'])
                prefix = project['group'] + '/' + project['label'] + '/' + self._path_from_container(session, used_subpaths, project['_id'])
                total_size, file_cnt = _append_targets(targets, session, prefix, total_size, file_cnt, req_spec['optional'], data_path, req_spec.get('filters'))
                acquisitions = config.db.acquisitions.find({'session': item_id}, ['label', 'files', 'uid', 'timestamp', 'timezone'])
                for acq in acquisitions:
                    acq_prefix = prefix + '/' + self._path_from_container(acq, used_subpaths, session['_id'])
                    total_size, file_cnt = _append_targets(targets, acq, acq_prefix, total_size, file_cnt, req_spec['optional'], data_path, req_spec.get('filters'))
            elif item['level'] == 'acquisition':
                acq = config.db.acquisitions.find_one({'_id': item_id}, ['session', 'label', 'files', 'uid', 'timestamp', 'timezone'])
                session = config.db.sessions.find_one({'_id': acq['session']}, ['project', 'label', 'uid', 'timestamp', 'timezone'])
                project = config.db.projects.find_one({'_id': session['project']}, ['group', 'label'])
                prefix = project['group'] + '/' + project['label'] + '/' + self._path_from_container(session, used_subpaths, project['_id']) + '/' + self._path_from_container(acq, used_subpaths, session['_id'])
                total_size, file_cnt = _append_targets(targets, acq, prefix, total_size, file_cnt, req_spec['optional'], data_path, req_spec.get('filters'))
        log.debug(json.dumps(targets, sort_keys=True, indent=4, separators=(',', ': ')))
        filename = 'sdm_' + datetime.datetime.utcnow().strftime('%Y%m%d_%H%M%S') + '.tar'
        ticket = util.download_ticket(self.request.client_addr, 'batch', targets, filename, total_size)
        config.db.downloads.insert_one(ticket)
        return {'ticket': ticket['_id'], 'file_cnt': file_cnt, 'size': total_size}

    def _path_from_container(self, container, used_subpaths, parent_id):
        def _find_new_path(path, list_used_subpaths):
            """from the input path finds a path that hasn't been used"""
            if path not in list_used_subpaths:
                return path
            i = 0
            while True:
                modified_path = path + '_' + str(i)
                if modified_path not in list_used_subpaths:
                    return modified_path
                i += 1
        path = None
        if not path and container.get('label'):
            path = container['label']
        if not path and container.get('timestamp'):
            timezone = container.get('timezone')
            if timezone:
                path = pytz.timezone('UTC').localize(container['timestamp']).astimezone(pytz.timezone(timezone)).strftime('%Y%m%d_%H%M')
            else:
                path = container['timestamp'].strftime('%Y%m%d_%H%M')
        if not path and container.get('uid'):
            path = container['uid']
        if not path:
            path = 'untitled'
        path = _find_new_path(path, used_subpaths.get(parent_id, []))
        used_subpaths[parent_id] = used_subpaths.get(parent_id, []) + [path]
        return path

    def download(self):
        """
        .. http:get::  /api/download

            Download GET Description...

            :statuscode 400: describe me
            :statuscode 404: describe me

        .. http:post:: /api/download

            Download POST Description...

            :statuscode 400: describe me
            :statuscode 404: describe me
        """

        """
        In downloads we use filters in the payload to exclude/include files.
        To pass a single filter, each of its conditions should be satisfied.
        If a file pass at least one filter, it is included in the targets.
        For example:

        download_payload = {
            'optional': True,
            'nodes': [{'level':'project', '_id':project_id}],
            'filters':[{
                'tags':{'+':['incomplete']}
            },
            {
                'types':{'-':['dicom']}
            }]
        }
        will download files with tag 'incomplete' OR type different from 'dicom'

        download_payload = {
            'optional': True,
            'nodes': [{'level':'project', '_id':project_id}],
            'filters':[{
                'tags':{'+':['incomplete']},
                'types':{'+':['dicom']}
            }]
        }
        will download only files with tag 'incomplete' AND type different from 'dicom'
        """
        ticket_id = self.get_param('ticket')
        if ticket_id:
            ticket = config.db.downloads.find_one({'_id': ticket_id})
            if not ticket:
                self.abort(404, 'no such ticket')
            if ticket['ip'] != self.request.client_addr:
                self.abort(400, 'ticket not for this source IP')
            if self.get_param('symlinks'):
                self.response.app_iter = symlinkarchivestream(ticket, config.get_item('persistent', 'data_path'))
            else:
                self.response.app_iter = archivestream(ticket)
            self.response.headers['Content-Type'] = 'application/octet-stream'
            self.response.headers['Content-Disposition'] = 'attachment; filename=' + str(ticket['filename'])
            for project_id in ticket['projects']:
                config.db.projects.update_one({'_id': project_id}, {'$inc': {'counter': 1}})
        else:
            req_spec = self.request.json_body
            if self.is_true('bulk'):
                return self._bulk_preflight_archivestream(req_spec.get('files', []))
            else:
                payload_schema_uri = validators.schema_uri('input', 'download.json')
                validator = validators.from_schema_path(payload_schema_uri)
                validator(req_spec, 'POST')
                log.debug(json.dumps(req_spec, sort_keys=True, indent=4, separators=(',', ': ')))
                return self._preflight_archivestream(req_spec)
<commit_msg>Add collection context to download<commit_after>import bson
import json
import pytz
import os.path
import tarfile
import datetime
import cStringIO

from . import base
from . import validators

from . import util
from . import config

log = config.log

def _filter_check(property_filter, property_values):
    minus = set(property_filter.get('-', []))
    plus = set(property_filter.get('+', []))
    if not minus.isdisjoint(property_values):
        return False
    if plus and plus.isdisjoint(property_values):
        return False
    return True

def _append_targets(targets, container, prefix, total_size, total_cnt, optional, data_path, filters):
    for f in container.get('files', []):
        if filters:
            filtered = True
            for filter_ in filters:
                type_as_list = [f['type']] if f.get('type') else []
                if (
                    _filter_check(filter_.get('tags', {}), f.get('tags', [])) and
                    _filter_check(filter_.get('types', {}), type_as_list)
                    ):
                    filtered = False
                    break
            if filtered:
                continue
        if optional or not f.get('optional', False):
            filepath = os.path.join(data_path, util.path_from_hash(f['hash']))
            if os.path.exists(filepath): # silently skip missing files
                targets.append((filepath, prefix + '/' + f['name'], f['size']))
                total_size += f['size']
                total_cnt += 1
    return total_size, total_cnt

def symlinkarchivestream(ticket, data_path):
    for filepath, arcpath, _ in ticket['target']:
        t = tarfile.TarInfo(name=arcpath)
        t.type = tarfile.SYMTYPE
        t.linkname = os.path.relpath(filepath, data_path)
        yield t.tobuf()
    stream = cStringIO.StringIO()
    with tarfile.open(mode='w|', fileobj=stream) as archive:
        pass
    yield stream.getvalue() # get tar stream trailer
    stream.close()

def archivestream(ticket):
    BLOCKSIZE = 512
    CHUNKSIZE = 2**20  # stream files in 1MB chunks
    stream = cStringIO.StringIO()
    with tarfile.open(mode='w|', fileobj=stream) as archive:
        for filepath, arcpath, _ in ticket['target']:
            yield archive.gettarinfo(filepath, arcpath).tobuf()
            with open(filepath, 'rb') as fd:
                for chunk in iter(lambda: fd.read(CHUNKSIZE), ''):
                    yield chunk
                if len(chunk) % BLOCKSIZE != 0:
                    yield (BLOCKSIZE - (len(chunk) % BLOCKSIZE)) * b'\0'
    yield stream.getvalue() # get tar stream trailer
    stream.close()

class Download(base.RequestHandler):

    def _bulk_preflight_archivestream(self, file_refs):
        data_path = config.get_item('persistent', 'data_path')
        arc_prefix = 'sdm'
        file_cnt = 0
        total_size = 0
        targets = []

        for fref in file_refs:
            cont_name   = fref.get('container_name','')+'s'
            cont_id     = fref.get('container_id', '')
            filename    = fref.get('filename', '')

            if cont_name not in ['projects', 'sessions', 'acquisitions']:
                self.abort(400, 'Bulk download only supports files in projects, sessions and acquisitions')
            file_obj = None
            try:
                # Try to find the file reference in the database (filtering on user permissions)
                bid = bson.ObjectId(cont_id)
                query = {'_id': bid}
                if not self.superuser_request:
                    query['permissions._id'] = self.uid
                file_obj = config.db[cont_name].find_one(
                    query,
                    {'files': { '$elemMatch': {
                        'name': filename
                    }}
                })['files'][0]
            except:
                # self.abort(404, 'File {} on Container {} {} not found'.format(filename, cont_name, cont_id))
                # silently skip missing files/files user does not have access to
                continue

            filepath = os.path.join(data_path, util.path_from_hash(file_obj['hash']))
            if os.path.exists(filepath): # silently skip missing files
                targets.append((filepath, cont_name+'/'+cont_id+'/'+file_obj['name'], file_obj['size']))
                total_size += file_obj['size']
                file_cnt += 1

        if len(targets) > 0:
            filename = arc_prefix + datetime.datetime.utcnow().strftime('%Y%m%d_%H%M%S') + '.tar'
            ticket = util.download_ticket(self.request.client_addr, 'batch', targets, filename, total_size)
            config.db.downloads.insert_one(ticket)
            return {'ticket': ticket['_id'], 'file_cnt': file_cnt, 'size': total_size}
        else:
            self.abort(404, 'No files requested could be found')

    def _preflight_archivestream(self, req_spec, collection=None):
        data_path = config.get_item('persistent', 'data_path')
        arc_prefix = 'sdm'
        file_cnt = 0
        total_size = 0
        targets = []

        # FIXME: check permissions of everything
        used_subpaths = {}
        for item in req_spec['nodes']:
            item_id = bson.ObjectId(item['_id'])
            if item['level'] == 'project':
                project = config.db.projects.find_one({'_id': item_id}, ['group', 'label', 'files'])
                prefix = '/'.join([arc_prefix, project['group'], project['label']])
                total_size, file_cnt = _append_targets(targets, project, prefix, total_size, file_cnt, req_spec['optional'], data_path, req_spec.get('filters'))

                sessions = config.db.sessions.find({'project': item_id}, ['label', 'files', 'uid', 'timestamp', 'timezone'])
                session_dict = {session['_id']: session for session in sessions}
                acquisitions = config.db.acquisitions.find({'session': {'$in': session_dict.keys()}}, ['label', 'files', 'session', 'uid', 'timestamp', 'timezone'])
                session_prefixes = {}

                for session in session_dict.itervalues():
                    session_prefix = prefix + '/' + self._path_from_container(session, used_subpaths, project['_id'])
                    session_prefixes[session['_id']] = session_prefix
                    total_size, file_cnt = _append_targets(targets, session, session_prefix, total_size, file_cnt, req_spec['optional'], data_path, req_spec.get('filters'))

                for acq in acquisitions:
                    session = session_dict[acq['session']]
                    acq_prefix = session_prefixes[session['_id']] + '/' + self._path_from_container(acq, used_subpaths, session['_id'])
                    total_size, file_cnt = _append_targets(targets, acq, acq_prefix, total_size, file_cnt, req_spec['optional'], data_path, req_spec.get('filters'))

            elif item['level'] == 'session':
                session = config.db.sessions.find_one({'_id': item_id}, ['project', 'label', 'files', 'uid', 'timestamp', 'timezone'])
                project = config.db.projects.find_one({'_id': session['project']}, ['group', 'label'])
                prefix = project['group'] + '/' + project['label'] + '/' + self._path_from_container(session, used_subpaths, project['_id'])
                total_size, file_cnt = _append_targets(targets, session, prefix, total_size, file_cnt, req_spec['optional'], data_path, req_spec.get('filters'))

                # If the param `collection` holding a collection id is not None, filter out acquisitions that are not in the collection
                a_query = {'session': item_id}
                if collection:
                    a_query['collections'] = bson.ObjectId(collection)
                acquisitions = config.db.acquisitions.find(a_query, ['label', 'files', 'uid', 'timestamp', 'timezone'])

                for acq in acquisitions:
                    acq_prefix = prefix + '/' + self._path_from_container(acq, used_subpaths, session['_id'])
                    total_size, file_cnt = _append_targets(targets, acq, acq_prefix, total_size, file_cnt, req_spec['optional'], data_path, req_spec.get('filters'))

            elif item['level'] == 'acquisition':
                acq = config.db.acquisitions.find_one({'_id': item_id}, ['session', 'label', 'files', 'uid', 'timestamp', 'timezone'])
                session = config.db.sessions.find_one({'_id': acq['session']}, ['project', 'label', 'uid', 'timestamp', 'timezone'])
                project = config.db.projects.find_one({'_id': session['project']}, ['group', 'label'])
                prefix = project['group'] + '/' + project['label'] + '/' + self._path_from_container(session, used_subpaths, project['_id']) + '/' + self._path_from_container(acq, used_subpaths, session['_id'])
                total_size, file_cnt = _append_targets(targets, acq, prefix, total_size, file_cnt, req_spec['optional'], data_path, req_spec.get('filters'))

        log.debug(json.dumps(targets, sort_keys=True, indent=4, separators=(',', ': ')))
        filename = 'sdm_' + datetime.datetime.utcnow().strftime('%Y%m%d_%H%M%S') + '.tar'
        ticket = util.download_ticket(self.request.client_addr, 'batch', targets, filename, total_size)
        config.db.downloads.insert_one(ticket)
        return {'ticket': ticket['_id'], 'file_cnt': file_cnt, 'size': total_size}

    def _path_from_container(self, container, used_subpaths, parent_id):
        def _find_new_path(path, list_used_subpaths):
            """from the input path finds a path that hasn't been used"""
            if path not in list_used_subpaths:
                return path
            i = 0
            while True:
                modified_path = path + '_' + str(i)
                if modified_path not in list_used_subpaths:
                    return modified_path
                i += 1
        path = None
        if not path and container.get('label'):
            path = container['label']
        if not path and container.get('timestamp'):
            timezone = container.get('timezone')
            if timezone:
                path = pytz.timezone('UTC').localize(container['timestamp']).astimezone(pytz.timezone(timezone)).strftime('%Y%m%d_%H%M')
            else:
                path = container['timestamp'].strftime('%Y%m%d_%H%M')
        if not path and container.get('uid'):
            path = container['uid']
        if not path:
            path = 'untitled'
        path = _find_new_path(path, used_subpaths.get(parent_id, []))
        used_subpaths[parent_id] = used_subpaths.get(parent_id, []) + [path]
        return path

    def download(self):
        """
        .. http:get::  /api/download

            Download GET Description...

            :statuscode 400: describe me
            :statuscode 404: describe me

        .. http:post:: /api/download

            Download POST Description...

            :statuscode 400: describe me
            :statuscode 404: describe me
        """

        """
        In downloads we use filters in the payload to exclude/include files.
        To pass a single filter, each of its conditions should be satisfied.
        If a file pass at least one filter, it is included in the targets.
        For example:

        download_payload = {
            'optional': True,
            'nodes': [{'level':'project', '_id':project_id}],
            'filters':[{
                'tags':{'+':['incomplete']}
            },
            {
                'types':{'-':['dicom']}
            }]
        }
        will download files with tag 'incomplete' OR type different from 'dicom'

        download_payload = {
            'optional': True,
            'nodes': [{'level':'project', '_id':project_id}],
            'filters':[{
                'tags':{'+':['incomplete']},
                'types':{'+':['dicom']}
            }]
        }
        will download only files with tag 'incomplete' AND type different from 'dicom'
        """
        ticket_id = self.get_param('ticket')
        if ticket_id:
            ticket = config.db.downloads.find_one({'_id': ticket_id})
            if not ticket:
                self.abort(404, 'no such ticket')
            if ticket['ip'] != self.request.client_addr:
                self.abort(400, 'ticket not for this source IP')
            if self.get_param('symlinks'):
                self.response.app_iter = symlinkarchivestream(ticket, config.get_item('persistent', 'data_path'))
            else:
                self.response.app_iter = archivestream(ticket)
            self.response.headers['Content-Type'] = 'application/octet-stream'
            self.response.headers['Content-Disposition'] = 'attachment; filename=' + str(ticket['filename'])
            for project_id in ticket['projects']:
                config.db.projects.update_one({'_id': project_id}, {'$inc': {'counter': 1}})
        else:
            req_spec = self.request.json_body
            if self.is_true('bulk'):
                return self._bulk_preflight_archivestream(req_spec.get('files', []))
            else:
                payload_schema_uri = validators.schema_uri('input', 'download.json')
                validator = validators.from_schema_path(payload_schema_uri)
                validator(req_spec, 'POST')
                log.debug(json.dumps(req_spec, sort_keys=True, indent=4, separators=(',', ': ')))

                return self._preflight_archivestream(req_spec, collection=self.get_param('collection'))
<|endoftext|>


<commit_before><commit_msg>initial effort to get test_pca working<commit_after><|endoftext|>


<commit_before>from typing import Iterable, List, Tuple

from werkzeug.http import HTTP_STATUS_CODES

from apistar import http

__all__ = ['WSGIEnviron', 'WSGIResponse']

STATUS_CODES = {
    code: "%d %s" % (code, msg)
    for code, msg in HTTP_STATUS_CODES.items()
}

WSGIEnviron = http.WSGIEnviron

class WSGIResponse(object):
    __slots__ = ('status', 'headers', 'iterator')

    def __init__(self,
                 status: str,
                 headers: List[Tuple[str, str]],
                 iterator: Iterable[bytes]) -> None:
        self.status = status
        self.headers = headers
        self.iterator = iterator

    @classmethod
    def build(cls, response: http.Response):
        try:
            status_text = STATUS_CODES[response.status]
        except KeyError:
            status_text = str(response.status)

        return WSGIResponse(
            status=status_text,
            headers=list(response.headers.items()),
            iterator=[response.content]
        )
<commit_msg>Set "Access-Control-Allow-Origin: *" by default<commit_after>from typing import Iterable, List, Tuple

from werkzeug.http import HTTP_STATUS_CODES

from apistar import http

__all__ = ['WSGIEnviron', 'WSGIResponse']

STATUS_CODES = {
    code: "%d %s" % (code, msg)
    for code, msg in HTTP_STATUS_CODES.items()
}

ACCESS_CONTROL_ALLOW_ORIGIN = 'Access-Control-Allow-Origin'

WSGIEnviron = http.WSGIEnviron

class WSGIResponse(object):
    __slots__ = ('status', 'headers', 'iterator')

    def __init__(self,
                 status: str,
                 headers: List[Tuple[str, str]],
                 iterator: Iterable[bytes]) -> None:
        self.status = status
        self.headers = headers
        self.iterator = iterator

    @classmethod
    def build(cls, response: http.Response):
        try:
            status_text = STATUS_CODES[response.status]
        except KeyError:
            status_text = str(response.status)

        headers = list(response.headers.items())

        if ACCESS_CONTROL_ALLOW_ORIGIN not in response.headers:
            headers += [(ACCESS_CONTROL_ALLOW_ORIGIN, '*')]

        return WSGIResponse(
            status=status_text,
            headers=headers,
            iterator=[response.content]
        )
<|endoftext|>


<commit_before>import logging
from datetime import datetime, timedelta

import persistent_messages
from django.conf import settings
from django.core.mail import send_mail
from django.template import TemplateDoesNotExist
from django.template.loader import get_template, render_to_string
from django.utils.safestring import mark_safe
from django.utils.translation import ugettext
from django_bouncy.models import Bounce, Complaint
from gadjo.requestprovider.signals import get_request
from notification.backends import BaseBackend
from notification.backends.email import EmailBackend as BaseEmailBackend

log = logging.getLogger('apps')

def shadow_ban_applies(notice_type, recipient, context):
    from astrobin_apps_users.services import UserService

    if notice_type.label == 'received_email':
        message_sender = context.get('message').sender
        if UserService(recipient).shadow_bans(message_sender):
            return True

    return False

class PersistentMessagesBackend(BaseBackend):
    spam_sensitivity = 1

    def deliver(self, recipient, sender, notice_type, extra_context):
        try:
            request = get_request()
        except IndexError:
            # This may happen during unit testing
            return

        context = self.default_context()
        context.update(extra_context)

        if shadow_ban_applies(notice_type, recipient, context):
            return

        messages = self.get_formatted_messages(
            ['notice.html'],
            notice_type.label, context)
        persistent_messages.add_message(
            request,
            persistent_messages.INFO,
            messages['notice.html'],
            user=recipient)

class EmailBackend(BaseEmailBackend):
    def can_send(self, user, notice_type):
        hard_bounces = Bounce.objects.filter(
            hard=True,
            bounce_type="Permanent",
            address=user.email)
        soft_bounces = Bounce.objects.filter(
            hard=False,
            bounce_type="Transient",
            address=user.email,
            created_at__gte=datetime.now() - timedelta(days=7))
        complaints = Complaint.objects.filter(
            address=user.email)
        deleted = user.userprofile.deleted is not None
        ignored = 'ASTROBIN_IGNORE' in user.email

        if deleted or hard_bounces.exists() or soft_bounces.count() > 2 or complaints or ignored:
            return False

        return super(EmailBackend, self).can_send(user, notice_type)

    def deliver(self, recipient, sender, notice_type, extra_context):
        context = self.default_context()
        context.update({
            "recipient": recipient,
            "sender": sender,
            "notice": ugettext(notice_type.display),
        })
        context.update(extra_context)

        if shadow_ban_applies(notice_type, recipient, context):
            return

        messages = self.get_formatted_messages((
            "short.txt",
            "full.txt",
            "full.html"
        ), notice_type.label, context)

        subject = "".join(render_to_string("notification/email_subject.txt", dict(context, **{
            "message": messages["short.txt"],
        })).splitlines())

        body = render_to_string("notification/email_body.txt", dict(context, **{
            "message": messages["full.txt"]
        }))

        try:
            get_template("notification/%s/full.html" % notice_type.label)
            message = messages["full.html"]
        except TemplateDoesNotExist:
            message = messages["full.txt"]

        html_body = render_to_string("notification/email_body.html", dict(context, **{
            "message": mark_safe(unicode(message))
        }))

        send_mail(
            subject,
            body,
            settings.DEFAULT_FROM_EMAIL,
            [settings.EMAIL_DEV_RECIPIENT if settings.SEND_EMAILS == 'dev' else recipient.email],
            html_message=html_body)

        log.info("Email sent to %s: %s" % (recipient.email, subject))
<commit_msg>(notifications) Fix on-site notification if sent from a requestless context (e.g. celery) (#1937)<commit_after>import logging
from datetime import datetime, timedelta

import persistent_messages
from django.conf import settings
from django.core.mail import send_mail
from django.template import TemplateDoesNotExist
from django.template.loader import get_template, render_to_string
from django.utils.safestring import mark_safe
from django.utils.translation import ugettext
from django_bouncy.models import Bounce, Complaint
from gadjo.requestprovider.signals import get_request
from notification.backends import BaseBackend
from notification.backends.email import EmailBackend as BaseEmailBackend
from persistent_messages.models import Message

log = logging.getLogger('apps')

def shadow_ban_applies(notice_type, recipient, context):
    from astrobin_apps_users.services import UserService

    if notice_type.label == 'received_email':
        message_sender = context.get('message').sender
        if UserService(recipient).shadow_bans(message_sender):
            return True

    return False

class PersistentMessagesBackend(BaseBackend):
    spam_sensitivity = 1

    def deliver(self, recipient, sender, notice_type, extra_context):
        context = self.default_context()
        context.update(extra_context)

        if shadow_ban_applies(notice_type, recipient, context):
            return

        template = 'notice.html'
        message = self.get_formatted_messages([template], notice_type.label, context)[template]
        level = persistent_messages.INFO

        try:
            request = get_request()
            persistent_messages.add_message(request, level, message, user=recipient)
        except IndexError:
            persistent_message = Message(user=recipient, level=level, message=message)
            persistent_message.save()

class EmailBackend(BaseEmailBackend):
    def can_send(self, user, notice_type):
        hard_bounces = Bounce.objects.filter(
            hard=True,
            bounce_type="Permanent",
            address=user.email)
        soft_bounces = Bounce.objects.filter(
            hard=False,
            bounce_type="Transient",
            address=user.email,
            created_at__gte=datetime.now() - timedelta(days=7))
        complaints = Complaint.objects.filter(
            address=user.email)
        deleted = user.userprofile.deleted is not None
        ignored = 'ASTROBIN_IGNORE' in user.email

        if deleted or hard_bounces.exists() or soft_bounces.count() > 2 or complaints or ignored:
            return False

        return super(EmailBackend, self).can_send(user, notice_type)

    def deliver(self, recipient, sender, notice_type, extra_context):
        context = self.default_context()
        context.update({
            "recipient": recipient,
            "sender": sender,
            "notice": ugettext(notice_type.display),
        })
        context.update(extra_context)

        if shadow_ban_applies(notice_type, recipient, context):
            return

        messages = self.get_formatted_messages((
            "short.txt",
            "full.txt",
            "full.html"
        ), notice_type.label, context)

        subject = "".join(render_to_string("notification/email_subject.txt", dict(context, **{
            "message": messages["short.txt"],
        })).splitlines())

        body = render_to_string("notification/email_body.txt", dict(context, **{
            "message": messages["full.txt"]
        }))

        try:
            get_template("notification/%s/full.html" % notice_type.label)
            message = messages["full.html"]
        except TemplateDoesNotExist:
            message = messages["full.txt"]

        html_body = render_to_string("notification/email_body.html", dict(context, **{
            "message": mark_safe(unicode(message))
        }))

        send_mail(
            subject,
            body,
            settings.DEFAULT_FROM_EMAIL,
            [settings.EMAIL_DEV_RECIPIENT if settings.SEND_EMAILS == 'dev' else recipient.email],
            html_message=html_body)

        log.info("Email sent to %s: %s" % (recipient.email, subject))
<|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] == "Not logged in.  You must be logged in to download data."

    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]
<commit_msg>Added test for retrieve_data()<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] == "Not logged in.  You must be logged in to download data."

    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]

    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>import datetime
import unittest

import mock

from ..signer import JWTAuthSigner
from .utils import get_new_rsa_private_key_in_pem_format

class TestJWTAuthSigner(unittest.TestCase):

    """ tests for the JWTAuthSigner class. """

    def setUp(self):
        self.key = get_new_rsa_private_key_in_pem_format()
        self.algorithm = 'RS256'

    def get_example_jwt_auth_signer(self):
        """ returns an example jwt_auth_signer instance. """
        return JWTAuthSigner('issuer', 'key_id', key=self.key)

    def test__get_claims(self):
        """ tests that _get_claims works as expected. """
        expected_now = datetime.datetime(year=2001, day=1, month=1)
        expected_audience = 'example_aud'
        expected_iss = 'eg'
        expected_key_id = 'eg/ex'
        jwt_auth_signer = JWTAuthSigner(
            expected_iss,
            expected_key_id,
            key=self.key)
        jwt_auth_signer._now = lambda: expected_now
        expected_claims = {
            'iss': expected_iss,
            'exp': expected_now + datetime.timedelta(hours=1),
            'iat': expected_now,
            'aud': expected_audience,
            'nbf': expected_now,
            'sub': expected_iss,
            }
        claims = jwt_auth_signer._get_claims(expected_audience)
        self.assertIsNotNone(claims['jti'])
        del claims['jti']
        self.assertEqual(claims, expected_claims)

    def test_jti_changes(self):
        """ tests that the jti of a claim changes. """
        expected_now = datetime.datetime(year=2001, day=1, month=1)
        aud = 'aud'
        jwt_auth_signer = self.get_example_jwt_auth_signer()
        jwt_auth_signer._now = lambda: expected_now
        first = jwt_auth_signer._get_claims(aud)['jti']
        second = jwt_auth_signer._get_claims(aud)['jti']
        self.assertNotEquals(first, second)
        self.assertTrue(str(expected_now.timestamp()) in first)
        self.assertTrue(str(expected_now.timestamp()) in second)

    @mock.patch('jwt.encode')
    def test_get_signed_claims(self, m_jwt_encode):
        """ tests that _get_signed_claims works as expected. """
        expected_aud = 'aud_x'
        expected_claims = {'eg': 'ex'}
        expected_key_id = 'key_id'
        expected_issuer = 'a_issuer'
        jwt_auth_signer = JWTAuthSigner(
            expected_issuer, expected_key_id, key=self.key)
        jwt_auth_signer._get_claims = lambda aud: expected_claims
        jwt_auth_signer.get_signed_claims(expected_aud)
        m_jwt_encode.assert_called_with(
            expected_claims,
            key=self.key,
            algorithm=self.algorithm,
            headers={'kid': expected_key_id})
<commit_msg>create the JWTAuthSigner instance in get_example_jwt_auth_signer with key as a non-keyword style argument.<commit_after>import datetime
import unittest

import mock

from ..signer import JWTAuthSigner
from .utils import get_new_rsa_private_key_in_pem_format

class TestJWTAuthSigner(unittest.TestCase):

    """ tests for the JWTAuthSigner class. """

    def setUp(self):
        self.key = get_new_rsa_private_key_in_pem_format()
        self.algorithm = 'RS256'

    def get_example_jwt_auth_signer(self):
        """ returns an example jwt_auth_signer instance. """
        return JWTAuthSigner('issuer', 'key_id', self.key)

    def test__get_claims(self):
        """ tests that _get_claims works as expected. """
        expected_now = datetime.datetime(year=2001, day=1, month=1)
        expected_audience = 'example_aud'
        expected_iss = 'eg'
        expected_key_id = 'eg/ex'
        jwt_auth_signer = JWTAuthSigner(
            expected_iss,
            expected_key_id,
            key=self.key)
        jwt_auth_signer._now = lambda: expected_now
        expected_claims = {
            'iss': expected_iss,
            'exp': expected_now + datetime.timedelta(hours=1),
            'iat': expected_now,
            'aud': expected_audience,
            'nbf': expected_now,
            'sub': expected_iss,
            }
        claims = jwt_auth_signer._get_claims(expected_audience)
        self.assertIsNotNone(claims['jti'])
        del claims['jti']
        self.assertEqual(claims, expected_claims)

    def test_jti_changes(self):
        """ tests that the jti of a claim changes. """
        expected_now = datetime.datetime(year=2001, day=1, month=1)
        aud = 'aud'
        jwt_auth_signer = self.get_example_jwt_auth_signer()
        jwt_auth_signer._now = lambda: expected_now
        first = jwt_auth_signer._get_claims(aud)['jti']
        second = jwt_auth_signer._get_claims(aud)['jti']
        self.assertNotEquals(first, second)
        self.assertTrue(str(expected_now.timestamp()) in first)
        self.assertTrue(str(expected_now.timestamp()) in second)

    @mock.patch('jwt.encode')
    def test_get_signed_claims(self, m_jwt_encode):
        """ tests that _get_signed_claims works as expected. """
        expected_aud = 'aud_x'
        expected_claims = {'eg': 'ex'}
        expected_key_id = 'key_id'
        expected_issuer = 'a_issuer'
        jwt_auth_signer = JWTAuthSigner(
            expected_issuer, expected_key_id, key=self.key)
        jwt_auth_signer._get_claims = lambda aud: expected_claims
        jwt_auth_signer.get_signed_claims(expected_aud)
        m_jwt_encode.assert_called_with(
            expected_claims,
            key=self.key,
            algorithm=self.algorithm,
            headers={'kid': expected_key_id})
<|endoftext|>


<commit_before><commit_msg>avoid error on 404 and no session available<commit_after><|endoftext|>


<commit_before><commit_msg> fter emily's review Please enter the commit message for your changes. Lines starting<commit_after><|endoftext|>


<commit_before><commit_msg>Create config dirs that can be moved<commit_after><|endoftext|>


<commit_before><commit_msg>Added missing events file_system_events.py from required plaso changes<commit_after><|endoftext|>


<commit_before>#!/usr/bin/python3.4
# -*-coding:Utf-8 -*
'''module to manage Quality settings'''
import xml.etree.ElementTree as xmlMod
from usefullFunctions import *
from Preferences.PresetList.Preset.OSA import *
from Preferences.PresetList.Preset.ValueType.Size import *
import os

class Quality:
	'''class to manage Quality settings'''
	FORMATS = [ 'BMP', 'IRIS', 'PNG', 'JPEG', 'JPEG2000', 'TARGA', 'TARGA_RAW',\
				'CINEON', 'DPX', 'OPEN_EXR_MULTILAYER', 'OPEN_EXR', 'HDR', 'TIFF']
	COLOR_DEPTH = {
					'BMP'					:		[8],
					'IRIS'					:		[8],
					'JPEG'					:		[8],
					'TARGA'					:		[8],
					'TARGA_RAW'				:		[8],
					'CINEON'				:		[10],
					'PNG'					:		[8, 16],
					'TIFF'					:		[8, 16],
					'JPEG2000'				:		[8, 12, 16],
					'DPX'					:		[8, 10, 12, 16],
					'OPEN_EXR'				:		[16, 32],
					'OPEN_EXR_MULTILAYER'	:		[32],
					'HDR'					:		[32]
					}

	def __init__(self, xml= None):
		'''initialize Quality settings with default value or values extracted from an xml object'''
		if xml is None:
			self.defaultInit()
		else:
			self.fromXml(xml)

	def defaultInit(self):
		'''initialize Quality settings with default value'''
		self.pourcent = 100
		self.size = Size('1920x1080')
		self.samples = 1500
		self.simplify = None
		self.format = 'OPEN_EXR_MULTILAYER'
		self.colorDepth = 32
		self.JPEGquality = 100
		self.PNGcompression = 0
		self.OSA = OSA()

	def fromXml(self, xml):
		'''initialize Quality settings with values extracted from an xml object'''
		node = xml.find('resolution')
		self.pourcent = int(node.get('pourcent'))
		self.size = Size(xml = node)
		self.samples = int(xml.find('samples').get('value'))
		if xml.find('simplify') is not None:
			self.simplify = int(xml.find('simplify').get('value'))
		else:
			self.simplify = None
		node = xml.find('format')
		self.format = node.get('value')
		self.colorDepth = int(node.get('depth'))
		self.JPEGquality = int(node.get('quality'))
		self.PNGcompression = int(node.get('compression'))

		self.OSA = OSA(xml.find('OSA'))

	def toXml(self):
		'''export Quality settings into xml syntaxed string'''
		txt = '<quality>\n'
		txt += '  <resolution pourcent="'+str(self.pourcent)+'" '+self.size.toXmlAttr()+' />\n'
		txt += '  <samples value="'+str(self.samples)+'" />\n'

		if self.simplify is not None:
			txt += '  <simplify value="'+str(self.simplify)+'" />\n'

		txt += '  <format value="'+self.format+'" depth="'+str(self.colorDepth)\
				+'" quality="'+str(self.JPEGquality)\
				+'" compression="'+str(self.PNGcompression)+'"/>\n'

		txt += self.OSA.toXml()

		txt += '</quality>\n'
		return txt

	def menu(self, log):
		'''menu to explore and edit Quality settings settings'''
		change = False
		log.menuIn('Quality')

		while True:

			log.print()

			self.print()

			print('''\n\n        Menu :
1- Edit Resolution Size
2- Edit Pourcent Setting
3- Edit Cycles Samples
4- Edit OSA settings (for Blender Render only)
5- Edit Simplify Setting
6- Edit Format''')

			if len(self.COLOR_DEPTH[self.format]) > 1:
				print('7- Change Color Depth')

			if self.format == 'PNG':
				print('8- Edit Compression Settings')
			elif self.format in ['JPEG', 'JPEGG2000']:
				print('8- Edit Quality Settings')

			print('0- Quit\n\n')

			choice = input('Action?').strip().lower()

			if choice in ['0', 'q', 'quit', 'cancel']:
				log.menuOut()
				return change
			elif choice == '1':
				change = (self.size.edit(log, 'Resolution Size') or change)
			elif choice in ['2', '3']:
				change = (self.edit(log, int(choice)) or change)
			elif choice == '4':
				change = (self.OSA.menu(log) or change)
			elif choice == '5':
				change = (self.editSimplify(log) or change)
			elif choice == '6':
				change = (self.editFormat(log) or change)
			elif choice == '7' and len(self.COLOR_DEPTH[self.format]) > 1:
				change = (self.editColorDepth(log) or change)
			elif choice == '8' and self.format in ['PNG','JPEG', 'JPEGG2000']:
				change = (self.editCompressionQuality(log) or change)
			else:
				log.error('Unvalid menu choice', False)

	def print(self):
		'''a method to print preset'''
		print('Resolution :            '+self.size.toStr()+'@'+str(self.pourcent))
		print('Cycles Samples :        '+str(self.samples))
		print('Simplify :              '+self.getSimplify())

		if self.format in ['JPEG', 'JPEG2000']:
			print('Format :                '+self.format+' (@'\
						+str(self.JPEGquality)+'%)')
		elif self.format == 'PNG':
			print('Format :                '+self.format+' (@'\
						+str(self.PNGcompression)+'%)')
		else
			print('Format :                '+self.format)

		print('Color Depth :           '+str(self.colorDepth))
		self.OSA.print()

	def getSimplify(self):
		'''A method to get simplify setting'''
		if self.simplify is None:
			return 'Disabled'
		else:
			return str(self.simplify)+' subdiv'

	def edit(self, log, choice):
		'''A method to edit pourcent setting'''
		if choice == 2:
			log.menuIn('Edit Resolution Pourcent')
		else:
			log.menuIn('Edit Cycles Samples')

		while True:

			log.print()

			# print current setting and get new one
			if choice == 2:
				print('\n\n        Edit Pourcent :\nCurrent Pourcent : '+str(self.pourcent)+'\n')
				new = input('New pourcent setting?').strip().lower()
			else:
				print('\n\n        Edit Cycles Samples :\nCurrent Sammples : '+str(self.samples)+'\n')
				new = input('New Samples setting?').strip().lower()

			# exit menu
			if new in ['', 'q', 'quit', 'cancel']:
				log.menuOut()
				return False

			#check new setting
			try:
				new = int(new)
			except ValueError:
				log.error('New setting must be an integer.')
				continue

			if new < 0:
				log.error('New setting must be a positive integer.')
				continue

			# apply new setting and exit
			if choice == 2:
				self.pourcent = new
				log.write('Resolution pourcent setting is set to : '\
							+str(self.pourcent)+'%')
			else:
				self.samples = new
				log.write('Cycles samples set to : '+str(self.samples)+'%')
			log.menuOut()
			return True

	def editSimplify(self, log):
		'''A method to Edit Simplify settings'''
		log.menuIn('Edit Simplify settings')

		while True:

			log.print()

			# print current setting and get new one
			print('\n\n        Edit Simplify Settings :\nCurrent settings : '+self.getSimplify()+'\n')
			choice = input('New setting?').strip().lower()

			# exit menu
			if choice in ['', 'q', 'quit', 'cancel']:
				log.menuOut()
				return False

			#check new setting
			try:
				choice = int(choice)
			except ValueError:
				log.error('New setting must be an integer.')
				continue

			if choice < 0:
				log.error('New setting must be a positive integer.')
				continue

			if choice < 11:
				self.simplify = choice
			else:
				self.simplify = None
			log.write('Simplify set to : '+self.getSimplify())
			log.menuOut()
			return True

	def editFormat(self, log):
		'''A method to edit format settings'''

		log.menuIn('Choose Output Format')

		while True:

			log.print()

			print('\n\n        Edit Output Format :\n\nCurrent format : '+self.format)

			indexPrintList(self.FORMATS)

			choice = input('new format?').strip()

			if choice.lower() in ['', 'q', 'quit', 'cancel']:
				log.menuOut()
				return False

			try:
				choice = int(choice)
			except ValueError:
				log.error('An integer is expected!')
				continue

			if choice < 0 or choice >= len(self.FORMATS):
				log.error('Out of choice range!')
				continue

			self.format = self.FORMATS[choice]
			self.checkDepth(log)
			log.write('Output format is set to : '+self.format)
			log.menuOut()
			return True

	def checkDepth(self, log):
		'''a method to check if current color depth is coherent with new set format'''
		depths = self.COLOR_DEPTH[self.format]

		if len(depths) == 1:
			self.colorDepth = depths[0]
		elif self.colorDepth not in depths:
			if self.colorDepth == 32:
				self.colorDepth = depths[-1]
			else:
				self.colorDepth = 16
		log.write('Color depth automatically set to '+str(self.colorDepth))

	def editColorDepth(self, log):
		'''a method to change color depth setting'''
		log.menuIn('Change Color Depth')
		depths = self.COLOR_DEPTH[self.format]
		while True:
			log.print()
			choice = input('\n\ncurrent settings : '+str(self.colorDepth)+' bits\n\navailable depth :['+ (','.join(str(x) for x in depths)) +'].\nwhat\'s the new depth (\'q\' to quit) :').lower().strip()

			if choice in ['0', 'q', 'quit', 'cancel']:
				log.menuOut()
				return False

			try:
				choice = int(choice)
			except ValueError:
				log.error('error, integer value expected!')
				continue

			if choice in depths:
				self.colorDepth = choice
				log.write('Color depth set to '+str(self.colorDepth))
				log.menuOut()
				return True
			else:
				log.error('error, unvalid value! excpeted value are:'+(','.join(str(x) for x in depths)))

	def editCompressionQuality(self, log):
		'''a method to edit output compression (png) or quality (jpg)'''
		png = (self.format == 'PNG')
		if png:
			log.menuIn('Edit Compression')
			current = self.PNGcompression
		else:
			log.menuIn('Edit Quality')
			current = self.JPEGquality

		while True:
			log.print()

			choice = input('\n\nCurrent setting : '+str(current)\
					+'%\n\nNew settings (integer between 0 and 100 or "q" to quit) : ')).strip().lower()

			if choice in ['q', 'quit', 'cancel']:
				log.menuOut()
				return False

			try:
				choice = int(choice)
			except ValueError:
				log.error('error, integer value expected!')
				continue

	def apply(self, scene):
		'''apply settings to a blender scene object'''
		scene.render.resolution_percentage = self.pourcent
		scene.render.resolution_x = self.size.X
		scene.render.resolution_y = self.size.Y

		scene.cycles.use_square_samples = False
		scene.cycles.samples = self.samples

		if self.simplify is None:
			scene.render.use_simplify = False
		else:
			scene.render.use_simplify = True
			scene.render.simplify_subdivision_render

		scene.render.image_settings.file_format = self.format
		scene.render.image_settings.color_depth = str(self.colorDepth)

		self.OSA.apply(scene)

	def getExtension(self):
		'''return file extension'''
		if self.format == 'PNG':
			return '.png'
		if self.format == 'JPEG':
			return '.jpg'
		return '.exr'

<commit_msg>edit png compression or jpeg quality<commit_after>#!/usr/bin/python3.4
# -*-coding:Utf-8 -*
'''module to manage Quality settings'''
import xml.etree.ElementTree as xmlMod
from usefullFunctions import *
from Preferences.PresetList.Preset.OSA import *
from Preferences.PresetList.Preset.ValueType.Size import *
import os

class Quality:
	'''class to manage Quality settings'''
	FORMATS = [ 'BMP', 'IRIS', 'PNG', 'JPEG', 'JPEG2000', 'TARGA', 'TARGA_RAW',\
				'CINEON', 'DPX', 'OPEN_EXR_MULTILAYER', 'OPEN_EXR', 'HDR', 'TIFF']
	COLOR_DEPTH = {
					'BMP'					:		[8],
					'IRIS'					:		[8],
					'JPEG'					:		[8],
					'TARGA'					:		[8],
					'TARGA_RAW'				:		[8],
					'CINEON'				:		[10],
					'PNG'					:		[8, 16],
					'TIFF'					:		[8, 16],
					'JPEG2000'				:		[8, 12, 16],
					'DPX'					:		[8, 10, 12, 16],
					'OPEN_EXR'				:		[16, 32],
					'OPEN_EXR_MULTILAYER'	:		[32],
					'HDR'					:		[32]
					}

	def __init__(self, xml= None):
		'''initialize Quality settings with default value or values extracted from an xml object'''
		if xml is None:
			self.defaultInit()
		else:
			self.fromXml(xml)

	def defaultInit(self):
		'''initialize Quality settings with default value'''
		self.pourcent = 100
		self.size = Size('1920x1080')
		self.samples = 1500
		self.simplify = None
		self.format = 'OPEN_EXR_MULTILAYER'
		self.colorDepth = 32
		self.JPEGquality = 100
		self.PNGcompression = 0
		self.OSA = OSA()

	def fromXml(self, xml):
		'''initialize Quality settings with values extracted from an xml object'''
		node = xml.find('resolution')
		self.pourcent = int(node.get('pourcent'))
		self.size = Size(xml = node)
		self.samples = int(xml.find('samples').get('value'))
		if xml.find('simplify') is not None:
			self.simplify = int(xml.find('simplify').get('value'))
		else:
			self.simplify = None
		node = xml.find('format')
		self.format = node.get('value')
		self.colorDepth = int(node.get('depth'))
		self.JPEGquality = int(node.get('quality'))
		self.PNGcompression = int(node.get('compression'))

		self.OSA = OSA(xml.find('OSA'))

	def toXml(self):
		'''export Quality settings into xml syntaxed string'''
		txt = '<quality>\n'
		txt += '  <resolution pourcent="'+str(self.pourcent)+'" '+self.size.toXmlAttr()+' />\n'
		txt += '  <samples value="'+str(self.samples)+'" />\n'

		if self.simplify is not None:
			txt += '  <simplify value="'+str(self.simplify)+'" />\n'

		txt += '  <format value="'+self.format+'" depth="'+str(self.colorDepth)\
				+'" quality="'+str(self.JPEGquality)\
				+'" compression="'+str(self.PNGcompression)+'"/>\n'

		txt += self.OSA.toXml()

		txt += '</quality>\n'
		return txt

	def menu(self, log):
		'''menu to explore and edit Quality settings settings'''
		change = False
		log.menuIn('Quality')

		while True:

			log.print()

			self.print()

			print('''\n\n        Menu :
1- Edit Resolution Size
2- Edit Pourcent Setting
3- Edit Cycles Samples
4- Edit OSA settings (for Blender Render only)
5- Edit Simplify Setting
6- Edit Format''')

			if len(self.COLOR_DEPTH[self.format]) > 1:
				print('7- Change Color Depth')

			if self.format == 'PNG':
				print('8- Edit Compression Settings')
			elif self.format in ['JPEG', 'JPEGG2000']:
				print('8- Edit Quality Settings')

			print('0- Quit\n\n')

			choice = input('Action?').strip().lower()

			if choice in ['0', 'q', 'quit', 'cancel']:
				log.menuOut()
				return change
			elif choice == '1':
				change = (self.size.edit(log, 'Resolution Size') or change)
			elif choice in ['2', '3']:
				change = (self.edit(log, int(choice)) or change)
			elif choice == '4':
				change = (self.OSA.menu(log) or change)
			elif choice == '5':
				change = (self.editSimplify(log) or change)
			elif choice == '6':
				change = (self.editFormat(log) or change)
			elif choice == '7' and len(self.COLOR_DEPTH[self.format]) > 1:
				change = (self.editColorDepth(log) or change)
			elif choice == '8' and self.format in ['PNG','JPEG', 'JPEGG2000']:
				change = (self.editCompressionQuality(log) or change)
			else:
				log.error('Unvalid menu choice', False)

	def print(self):
		'''a method to print preset'''
		print('Resolution :            '+self.size.toStr()+'@'+str(self.pourcent))
		print('Cycles Samples :        '+str(self.samples))
		print('Simplify :              '+self.getSimplify())

		if self.format in ['JPEG', 'JPEG2000']:
			print('Format :                '+self.format+' (@'\
						+str(self.JPEGquality)+'%)')
		elif self.format == 'PNG':
			print('Format :                '+self.format+' (@'\
						+str(self.PNGcompression)+'%)')
		else
			print('Format :                '+self.format)

		print('Color Depth :           '+str(self.colorDepth))
		self.OSA.print()

	def getSimplify(self):
		'''A method to get simplify setting'''
		if self.simplify is None:
			return 'Disabled'
		else:
			return str(self.simplify)+' subdiv'

	def edit(self, log, choice):
		'''A method to edit pourcent setting'''
		if choice == 2:
			log.menuIn('Edit Resolution Pourcent')
		else:
			log.menuIn('Edit Cycles Samples')

		while True:

			log.print()

			# print current setting and get new one
			if choice == 2:
				print('\n\n        Edit Pourcent :\nCurrent Pourcent : '+str(self.pourcent)+'\n')
				new = input('New pourcent setting?').strip().lower()
			else:
				print('\n\n        Edit Cycles Samples :\nCurrent Sammples : '+str(self.samples)+'\n')
				new = input('New Samples setting?').strip().lower()

			# exit menu
			if new in ['', 'q', 'quit', 'cancel']:
				log.menuOut()
				return False

			#check new setting
			try:
				new = int(new)
			except ValueError:
				log.error('New setting must be an integer.')
				continue

			if new < 0:
				log.error('New setting must be a positive integer.')
				continue

			# apply new setting and exit
			if choice == 2:
				self.pourcent = new
				log.write('Resolution pourcent setting is set to : '\
							+str(self.pourcent)+'%')
			else:
				self.samples = new
				log.write('Cycles samples set to : '+str(self.samples)+'%')
			log.menuOut()
			return True

	def editSimplify(self, log):
		'''A method to Edit Simplify settings'''
		log.menuIn('Edit Simplify settings')

		while True:

			log.print()

			# print current setting and get new one
			print('\n\n        Edit Simplify Settings :\nCurrent settings : '+self.getSimplify()+'\n')
			choice = input('New setting?').strip().lower()

			# exit menu
			if choice in ['', 'q', 'quit', 'cancel']:
				log.menuOut()
				return False

			#check new setting
			try:
				choice = int(choice)
			except ValueError:
				log.error('New setting must be an integer.')
				continue

			if choice < 0:
				log.error('New setting must be a positive integer.')
				continue

			if choice < 11:
				self.simplify = choice
			else:
				self.simplify = None
			log.write('Simplify set to : '+self.getSimplify())
			log.menuOut()
			return True

	def editFormat(self, log):
		'''A method to edit format settings'''

		log.menuIn('Choose Output Format')

		while True:

			log.print()

			print('\n\n        Edit Output Format :\n\nCurrent format : '+self.format)

			indexPrintList(self.FORMATS)

			choice = input('new format?').strip()

			if choice.lower() in ['', 'q', 'quit', 'cancel']:
				log.menuOut()
				return False

			try:
				choice = int(choice)
			except ValueError:
				log.error('An integer is expected!')
				continue

			if choice < 0 or choice >= len(self.FORMATS):
				log.error('Out of choice range!')
				continue

			self.format = self.FORMATS[choice]
			self.checkDepth(log)
			log.write('Output format is set to : '+self.format)
			log.menuOut()
			return True

	def checkDepth(self, log):
		'''a method to check if current color depth is coherent with new set format'''
		depths = self.COLOR_DEPTH[self.format]

		if len(depths) == 1:
			self.colorDepth = depths[0]
		elif self.colorDepth not in depths:
			if self.colorDepth == 32:
				self.colorDepth = depths[-1]
			else:
				self.colorDepth = 16
		log.write('Color depth automatically set to '+str(self.colorDepth))

	def editColorDepth(self, log):
		'''a method to change color depth setting'''
		log.menuIn('Change Color Depth')
		depths = self.COLOR_DEPTH[self.format]
		while True:
			log.print()
			choice = input('\n\ncurrent settings : '+str(self.colorDepth)+' bits\n\navailable depth :['+ (','.join(str(x) for x in depths)) +'].\nwhat\'s the new depth (\'q\' to quit) :').lower().strip()

			if choice in ['0', 'q', 'quit', 'cancel']:
				log.menuOut()
				return False

			try:
				choice = int(choice)
			except ValueError:
				log.error('error, integer value expected!')
				continue

			if choice in depths:
				self.colorDepth = choice
				log.write('Color depth set to '+str(self.colorDepth))
				log.menuOut()
				return True
			else:
				log.error('error, unvalid value! excpeted value are:'+(','.join(str(x) for x in depths)))

	def editCompressionQuality(self, log):
		'''a method to edit output compression (png) or quality (jpg)'''
		png = (self.format == 'PNG')
		if png:
			log.menuIn('Edit Compression')
			current = self.PNGcompression
		else:
			log.menuIn('Edit Quality')
			current = self.JPEGquality

		while True:
			log.print()

			choice = input('\n\nCurrent setting : '+str(current)\
					+'%\n\nNew settings (integer between 0 and 100 or "q" to quit) : ')).strip().lower()

			if choice in ['q', 'quit', 'cancel']:
				log.menuOut()
				return False

			try:
				choice = int(choice)
			except ValueError:
				log.error('error, integer value expected!')
				continue

			if choice >= 0 and choice <= 100:
				if png:
					self.PNGcompression = choice
					log.write('PNG compression set to '+str(self.PNGcompression)+'.')
				else:
					self.JPEGquality = choice
					log.write('JPEG quality set to '+str(self.JPEGquality)+'.')

				log.menuOut()
				return True
			else:
				log.error('error, expect a value between 0 and 100, not «'+str(choice)+'»!')

	def apply(self, scene):
		'''apply settings to a blender scene object'''
		scene.render.resolution_percentage = self.pourcent
		scene.render.resolution_x = self.size.X
		scene.render.resolution_y = self.size.Y

		scene.cycles.use_square_samples = False
		scene.cycles.samples = self.samples

		if self.simplify is None:
			scene.render.use_simplify = False
		else:
			scene.render.use_simplify = True
			scene.render.simplify_subdivision_render

		scene.render.image_settings.file_format = self.format
		scene.render.image_settings.color_depth = str(self.colorDepth)

		self.OSA.apply(scene)

	def getExtension(self):
		'''return file extension'''
		if self.format == 'PNG':
			return '.png'
		if self.format == 'JPEG':
			return '.jpg'
		return '.exr'

<|endoftext|>