<commit_before>"""Serializers for REST framework"""
from rest_framework import serializers

from geomat.stein.models import CrystalSystem, Handpiece, MineralType, Photograph, QuizQuestion,\
    QuizAnswer, Cleavage, GlossaryEntry, TreeNode

from drf_yasg.utils import swagger_serializer_method

class StdImageField(serializers.ImageField):
    """
    Get all the variations of the StdImageField
    """

    def to_native(self, obj):
        return self.get_variations_urls(obj)

    def to_representation(self, obj):
        return self.get_variations_urls(obj)

    def get_variations_urls(self, obj):
        """
        Get all the logo urls.
        """

        # Initiate return object
        return_object = {}

        # Get the field of the object
        field = obj.field

        # A lot of ifs going araound, first check if it has the field variations
        if hasattr(field, 'variations'):
            # Get the variations
            variations = field.variations
            # Go through the variations dict
            for key in list(variations.keys()):
                # Just to be sure if the stdimage object has it stored in the obj
                if hasattr(obj, key):
                    # get the by stdimage properties
                    field_obj = getattr(obj, key, None)
                    if field_obj and hasattr(field_obj, 'url'):
                        # store it, with the name of the variation type into our return object
                        return_object[key] = super(
                            StdImageField, self).to_representation(field_obj)

        # Also include the original (if possible)
        if hasattr(obj, 'url'):
            return_object['original'] = super(StdImageField,
                                              self).to_representation(obj)

        return return_object

class RecursiveField(serializers.Serializer):

    def to_representation(self, instance):
        serializer = self.parent.parent.__class__(instance, context=self.context)
        return serializer.data

class CleavageSerializer(serializers.ModelSerializer):

    cleavage = serializers.SerializerMethodField()

    class Meta:
        model = Cleavage
        fields = ("cleavage", "coordinates")

    def get_cleavage(self, obj):
        return obj.get_cleavage_display()

class CrystalSystemLessSerializer(serializers.ModelSerializer):
    """
    This Serializer is used to represent a Version without the full mineraltype
    """

    crystal_system = serializers.SerializerMethodField()

    def get_crystal_system(self, obj):
        choice_dict = dict(obj.CRYSTAL_SYSTEM_CHOICES)
        key = obj.crystal_system
        if key:
            return choice_dict[key]

        return key

    class Meta:
        model = CrystalSystem
        fields = ('id', 'mineral_type', 'crystal_system', 'temperature',
                  'pressure')

class MineralTypeSerializer(serializers.ModelSerializer):
    systematics = serializers.SerializerMethodField()
    fracture = serializers.SerializerMethodField()
    lustre = serializers.SerializerMethodField()
    density = serializers.SerializerMethodField()
    mohs_scale = serializers.SerializerMethodField()
    crystal_system = CrystalSystemLessSerializer(many=True)
    cleavage = CleavageSerializer(many=True)

    class Meta:
        model = MineralType
        fields = '__all__'
        depth = 2

    def get_systematics(self, obj):
        systematic = obj.systematics
        if systematic:
            return systematic.node_name
        return None

    @swagger_serializer_method(serializer_or_field=serializers.ListField)
    def get_fracture(self, obj):
        lst = []
        choice_dict = dict(obj.FRACTURE_CHOICES)
        fracture = obj.fracture
        if fracture:
            lst = [choice_dict.get(choice) for choice in fracture]
        return lst

    @swagger_serializer_method(serializer_or_field=serializers.ListField)
    def get_lustre(self, obj):
        lst = []
        choice_dict = dict(obj.LUSTRE_CHOICES)
        lustre = obj.lustre
        if lustre:
            lst = [choice_dict.get(choice) for choice in lustre]
        return lst

    def get_density(self,obj):
        if float(obj.density.upper) == float(obj.density.lower) + 0.001:
            return "{}".format(obj.density.lower).replace(".", ",")
        return "{0} - {1}".format(obj.density.lower, obj.density.upper).replace(".", ",")

    def get_mohs_scale(self, obj):
        if float(obj.mohs_scale.upper) == float(obj.mohs_scale.lower) + 0.001:
            return "{}".format(obj.mohs_scale.lower).replace(".", ",")
        return "{0} - {1}".format(obj.mohs_scale.lower, obj.mohs_scale.upper).replace(".", ",")

class MineralProfilesSerializer(MineralTypeSerializer):

    image_file = serializers.SerializerMethodField()
    IMAGE_DICT = {
        # Min : #Photo
        1: 34, 2: 31, 3: 32, 4: 35, 5: 36, 6: 37, 7: 139, 8: 47, 9: 99, 10: 40,
        11: 44, 12: 98, 13: 41, 14: 151, 15: 97, 16: 131, 18: 134, 19: 135,
        20: 132, 21: 133, 22: 144, 25: 48, 26: 113, 28: 117, 29: 122, 30: 140, 31: 74,
        34: 50, 35: 146, 36: 49, 37: 54, 38: 150, 39: 51, 40: 52, 41: 56, 42: 59, 44: 60,
        46: 61, 48: 63, 49: 64, 50: 67, 51: 65, 52: 68, 53: 77, 54: 79, 55: 80, 56: 75, 57: 69,
        58: 70, 59: 71, 60: 143, 61: 123, 62: 72, 63: 73, 64: 101, 66: 137, 67: 136, 68: 141,
        69: 102, 70: 82, 71: 81, 83: 84, 84: 85, 85: 145, 86: 112, 87: 104, 88: 138, 89: 108,
        90: 111, 91: 114, 92: 114, 93: 107, 96: 129, 97: 119, 98: 120, 99: 116, 100: 128,
        101: 105, 102: 109, 104: 29, 105: 124, 106: 125, 107: 126, 108: 127, 114: 153,
        115: 154, 116: 162, 117: 176
    }

    def get_image_file(self, obj):
        if obj.pk in list(MineralProfilesSerializer.IMAGE_DICT.keys()):
            photo_pk = MineralProfilesSerializer.IMAGE_DICT[obj.pk]
            photo = Photograph.objects.only("image_file").get(pk=photo_pk)
            return StdImageField().to_representation(obj=photo.image_file)
        return None

class CrystalSystemFullSerializer(serializers.ModelSerializer):
    mineral_type = MineralTypeSerializer()

    class Meta:
        model = CrystalSystem
        fields = ('id', 'mineral_type', 'crystal_system', 'temperature',
                  'pressure')

class HandpieceSerializer(serializers.ModelSerializer):
    mineral_type = MineralTypeSerializer(many=True)

    class Meta:
        model = Handpiece
        fields = ('id', 'name', 'mineral_type', 'finding_place',
                  'current_location', 'old_inventory_number', 'created_at',
                  'last_modified')

class PhotographSerializer(serializers.ModelSerializer):
    image_file = StdImageField()
    handpiece = HandpieceSerializer()
    online_status = serializers.BooleanField()

    class Meta:
        model = Photograph
        fields = ('id', 'image_file', 'handpiece', 'orig_height', 'orig_width', 'audio_file', 'description',
                  'online_status', 'created_at', 'last_modified')

class QuizAnswerLessSerializer(serializers.ModelSerializer):

    class Meta:
        model = QuizAnswer
        fields = ('id', 'atext', 'correct', 'feedback_correct', 'feedback_incorrect')

class QuizQuestionLessSerializer(serializers.ModelSerializer):
    qtype = serializers.SerializerMethodField()

    class Meta:
        model = QuizQuestion
        fields = ('id', 'qtext', 'qtype', 'tags', 'difficulty')

    def get_qtype(self, obj):
        choice_dict = dict(obj.QTYPE_CHOICES)
        return choice_dict.get(obj.qtype)

class QuizAnswerFullSerializer(serializers.ModelSerializer):
    question = QuizQuestionLessSerializer()

    class Meta:
        model = QuizAnswer
        fields = '__all__'

class QuizQuestionFullSerializer(serializers.ModelSerializer):
    qtype = serializers.SerializerMethodField()
    answers = QuizAnswerLessSerializer(many=True)

    class Meta:
        model = QuizQuestion
        fields = '__all__'
        ordering = ("pk",)

    def get_qtype(self, obj):
        choice_dict = dict(obj.QTYPE_CHOICES)
        return choice_dict.get(obj.qtype)

class GlossaryEntrySerializer(serializers.ModelSerializer):

    class Meta:
        model = GlossaryEntry
        fields = '__all__'

class TreeNodeSerializer(serializers.ModelSerializer):

    mineraltypes = MineralProfilesSerializer(many=True)
    leaf_nodes = serializers.SerializerMethodField()

    class Meta:
        depth = 1
        model = TreeNode
        fields = ("node_name", "mineraltypes", "leaf_nodes", "info_text")

    def get_leaf_nodes(self, obj):
        return TreeNodeSerializer(obj.get_children(), many=True).data
<commit_msg>new images in MineralProfile serializer<commit_after>"""Serializers for REST framework"""
from rest_framework import serializers

from geomat.stein.models import CrystalSystem, Handpiece, MineralType, Photograph, QuizQuestion,\
    QuizAnswer, Cleavage, GlossaryEntry, TreeNode

from drf_yasg.utils import swagger_serializer_method

class StdImageField(serializers.ImageField):
    """
    Get all the variations of the StdImageField
    """

    def to_native(self, obj):
        return self.get_variations_urls(obj)

    def to_representation(self, obj):
        return self.get_variations_urls(obj)

    def get_variations_urls(self, obj):
        """
        Get all the logo urls.
        """

        # Initiate return object
        return_object = {}

        # Get the field of the object
        field = obj.field

        # A lot of ifs going araound, first check if it has the field variations
        if hasattr(field, 'variations'):
            # Get the variations
            variations = field.variations
            # Go through the variations dict
            for key in list(variations.keys()):
                # Just to be sure if the stdimage object has it stored in the obj
                if hasattr(obj, key):
                    # get the by stdimage properties
                    field_obj = getattr(obj, key, None)
                    if field_obj and hasattr(field_obj, 'url'):
                        # store it, with the name of the variation type into our return object
                        return_object[key] = super(
                            StdImageField, self).to_representation(field_obj)

        # Also include the original (if possible)
        if hasattr(obj, 'url'):
            return_object['original'] = super(StdImageField,
                                              self).to_representation(obj)

        return return_object

class RecursiveField(serializers.Serializer):

    def to_representation(self, instance):
        serializer = self.parent.parent.__class__(instance, context=self.context)
        return serializer.data

class CleavageSerializer(serializers.ModelSerializer):

    cleavage = serializers.SerializerMethodField()

    class Meta:
        model = Cleavage
        fields = ("cleavage", "coordinates")

    def get_cleavage(self, obj):
        return obj.get_cleavage_display()

class CrystalSystemLessSerializer(serializers.ModelSerializer):
    """
    This Serializer is used to represent a Version without the full mineraltype
    """

    crystal_system = serializers.SerializerMethodField()

    def get_crystal_system(self, obj):
        choice_dict = dict(obj.CRYSTAL_SYSTEM_CHOICES)
        key = obj.crystal_system
        if key:
            return choice_dict[key]

        return key

    class Meta:
        model = CrystalSystem
        fields = ('id', 'mineral_type', 'crystal_system', 'temperature',
                  'pressure')

class MineralTypeSerializer(serializers.ModelSerializer):
    systematics = serializers.SerializerMethodField()
    fracture = serializers.SerializerMethodField()
    lustre = serializers.SerializerMethodField()
    density = serializers.SerializerMethodField()
    mohs_scale = serializers.SerializerMethodField()
    crystal_system = CrystalSystemLessSerializer(many=True)
    cleavage = CleavageSerializer(many=True)

    class Meta:
        model = MineralType
        fields = '__all__'
        depth = 2

    def get_systematics(self, obj):
        systematic = obj.systematics
        if systematic:
            return systematic.node_name
        return None

    @swagger_serializer_method(serializer_or_field=serializers.ListField)
    def get_fracture(self, obj):
        lst = []
        choice_dict = dict(obj.FRACTURE_CHOICES)
        fracture = obj.fracture
        if fracture:
            lst = [choice_dict.get(choice) for choice in fracture]
        return lst

    @swagger_serializer_method(serializer_or_field=serializers.ListField)
    def get_lustre(self, obj):
        lst = []
        choice_dict = dict(obj.LUSTRE_CHOICES)
        lustre = obj.lustre
        if lustre:
            lst = [choice_dict.get(choice) for choice in lustre]
        return lst

    def get_density(self,obj):
        if float(obj.density.upper) == float(obj.density.lower) + 0.001:
            return "{}".format(obj.density.lower).replace(".", ",")
        return "{0} - {1}".format(obj.density.lower, obj.density.upper).replace(".", ",")

    def get_mohs_scale(self, obj):
        if float(obj.mohs_scale.upper) == float(obj.mohs_scale.lower) + 0.001:
            return "{}".format(obj.mohs_scale.lower).replace(".", ",")
        return "{0} - {1}".format(obj.mohs_scale.lower, obj.mohs_scale.upper).replace(".", ",")

class MineralProfilesSerializer(MineralTypeSerializer):

    image_file = serializers.SerializerMethodField()
    IMAGE_DICT = {
        # Min : #Photo
        1: 34, 2: 31, 3: 32, 4: 35, 5: 36, 6: 37, 7: 139, 8: 47, 9: 99, 10: 40,
        11: 44, 12: 98, 13: 41, 14: 151, 15: 97, 16: 131, 18: 134, 19: 135,
        20: 132, 21: 133, 22: 144, 25: 48, 26: 113, 27: 180, 28: 117, 29: 122, 30: 140, 31: 74,
        34: 50, 35: 146, 36: 49, 37: 54, 38: 150, 39: 51, 40: 52, 41: 56, 42: 59, 44: 60,
        46: 61, 48: 63, 49: 64, 50: 67, 51: 65, 52: 68, 53: 77, 54: 79, 55: 80, 56: 75, 57: 69,
        58: 70, 59: 71, 60: 143, 61: 123, 62: 72, 63: 73, 64: 101, 65: 177, 66: 137, 67: 136, 68: 141,
        69: 102, 70: 82, 71: 81, 83: 84, 84: 85, 85: 145, 86: 112, 87: 104, 88: 138, 89: 108,
        90: 111, 91: 114, 92: 114, 93: 107, 94: 178, 95: 179, 96: 129, 97: 119, 98: 120, 99: 116, 100: 128,
        101: 105, 102: 109, 104: 29, 105: 124, 106: 125, 107: 126, 108: 127, 114: 153,
        115: 154, 116: 162
    }

    def get_image_file(self, obj):
        if obj.pk in list(MineralProfilesSerializer.IMAGE_DICT.keys()):
            photo_pk = MineralProfilesSerializer.IMAGE_DICT[obj.pk]
            photo = Photograph.objects.only("image_file").get(pk=photo_pk)
            return StdImageField().to_representation(obj=photo.image_file)
        return None

class CrystalSystemFullSerializer(serializers.ModelSerializer):
    mineral_type = MineralTypeSerializer()

    class Meta:
        model = CrystalSystem
        fields = ('id', 'mineral_type', 'crystal_system', 'temperature',
                  'pressure')

class HandpieceSerializer(serializers.ModelSerializer):
    mineral_type = MineralTypeSerializer(many=True)

    class Meta:
        model = Handpiece
        fields = ('id', 'name', 'mineral_type', 'finding_place',
                  'current_location', 'old_inventory_number', 'created_at',
                  'last_modified')

class PhotographSerializer(serializers.ModelSerializer):
    image_file = StdImageField()
    handpiece = HandpieceSerializer()
    online_status = serializers.BooleanField()

    class Meta:
        model = Photograph
        fields = ('id', 'image_file', 'handpiece', 'orig_height', 'orig_width', 'audio_file', 'description',
                  'online_status', 'created_at', 'last_modified')

class QuizAnswerLessSerializer(serializers.ModelSerializer):

    class Meta:
        model = QuizAnswer
        fields = ('id', 'atext', 'correct', 'feedback_correct', 'feedback_incorrect')

class QuizQuestionLessSerializer(serializers.ModelSerializer):
    qtype = serializers.SerializerMethodField()

    class Meta:
        model = QuizQuestion
        fields = ('id', 'qtext', 'qtype', 'tags', 'difficulty')

    def get_qtype(self, obj):
        choice_dict = dict(obj.QTYPE_CHOICES)
        return choice_dict.get(obj.qtype)

class QuizAnswerFullSerializer(serializers.ModelSerializer):
    question = QuizQuestionLessSerializer()

    class Meta:
        model = QuizAnswer
        fields = '__all__'

class QuizQuestionFullSerializer(serializers.ModelSerializer):
    qtype = serializers.SerializerMethodField()
    answers = QuizAnswerLessSerializer(many=True)

    class Meta:
        model = QuizQuestion
        fields = '__all__'
        ordering = ("pk",)

    def get_qtype(self, obj):
        choice_dict = dict(obj.QTYPE_CHOICES)
        return choice_dict.get(obj.qtype)

class GlossaryEntrySerializer(serializers.ModelSerializer):

    class Meta:
        model = GlossaryEntry
        fields = '__all__'

class TreeNodeSerializer(serializers.ModelSerializer):

    mineraltypes = MineralProfilesSerializer(many=True)
    leaf_nodes = serializers.SerializerMethodField()

    class Meta:
        depth = 1
        model = TreeNode
        fields = ("node_name", "mineraltypes", "leaf_nodes", "info_text")

    def get_leaf_nodes(self, obj):
        return TreeNodeSerializer(obj.get_children(), many=True).data
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-

# This file is part of wger Workout Manager.
#
# wger Workout Manager is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# wger Workout Manager is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License

from tastypie import fields
from tastypie.resources import ModelResource
from tastypie.constants import ALL, ALL_WITH_RELATIONS

from easy_thumbnails.alias import aliases
from easy_thumbnails.files import get_thumbnailer

from wger.exercises.models import Exercise
from wger.exercises.models import ExerciseCategory
from wger.exercises.models import ExerciseComment
from wger.exercises.models import ExerciseImage
from wger.exercises.models import Muscle
from wger.exercises.models import Language
from wger.exercises.models import Equipment

class ExerciseResource(ModelResource):
    category = fields.ToOneField('wger.exercises.api.resources.ExerciseCategoryResource',
                                 'category')
    muscles = fields.ToManyField('wger.exercises.api.resources.MuscleResource', 'muscles')
    muscles_secondary = fields.ToManyField('wger.exercises.api.resources.MuscleResource', 'muscles_secondary')
    comments = fields.ToManyField('wger.exercises.api.resources.ExerciseCommentResource',
                                  'exercisecomment_set')
    images = fields.ToManyField('wger.exercises.api.resources.ExerciseImageResource',
                                'exerciseimage_set')
    equipment = fields.ToManyField('wger.exercises.api.resources.EquipmentResource',
                                   'equipment')
    language = fields.ToOneField('wger.exercises.api.resources.LanguageResource', 'language')
    creation_date = fields.DateField(attribute='creation_date', null=True)

    class Meta:
        queryset = Exercise.objects.all()
        filtering = {'id': ALL,
                     "category": ALL_WITH_RELATIONS,
                     "creation_date": ALL,
                     "description": ALL,
                     "images": ALL_WITH_RELATIONS,
                     "language": ALL_WITH_RELATIONS,
                     "muscles": ALL_WITH_RELATIONS,
                     "status": ALL,
                     "name": ALL}

class EquipmentResource(ModelResource):

    class Meta:
        queryset = Equipment.objects.all()
        filtering = {'id': ALL,
                     "name": ALL}

class ExerciseCategoryResource(ModelResource):

    class Meta:
        queryset = ExerciseCategory.objects.all()
        filtering = {'id': ALL,
                     "name": ALL}

class ExerciseImageResource(ModelResource):
    exercise = fields.ToOneField('wger.exercises.api.resources.ExerciseResource', 'exercise')

    class Meta:
        queryset = ExerciseImage.objects.all()
        filtering = {'id': ALL,
                     "image": ALL,
                     "is_main": ALL,
                     "license": ALL,
                     "license_author": ALL}

    def dehydrate(self, bundle):
        '''
        Also send the URLs for the thumbnailed pictures
        '''
        thumbnails = {}
        for alias in aliases.all():
            t = get_thumbnailer(bundle.obj.image)
            thumbnails[alias] = {'url': t.get_thumbnail(aliases.get(alias)).url,
                                 'settings': aliases.get(alias)}

        bundle.data['thumbnails'] = thumbnails
        return bundle

class ExerciseCommentResource(ModelResource):
    exercise = fields.ToOneField('wger.exercises.api.resources.ExerciseResource', 'exercise')

    class Meta:
        queryset = ExerciseComment.objects.all()
        filtering = {'id': ALL,
                     "comment": ALL,
                     "exercise": ALL_WITH_RELATIONS}

class MuscleResource(ModelResource):
    class Meta:
        queryset = Muscle.objects.all()
        filtering = {'id': ALL,
                     "name": ALL,
                     "is_front": ALL}

class LanguageResource(ModelResource):
    class Meta:
        queryset = Language.objects.all()
        filtering = {'id': ALL,
                     "full_name": ALL,
                     "short_name": ALL}
<commit_msg>Fix line formatting<commit_after># -*- coding: utf-8 -*-

# This file is part of wger Workout Manager.
#
# wger Workout Manager is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# wger Workout Manager is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License

from tastypie import fields
from tastypie.resources import ModelResource
from tastypie.constants import ALL, ALL_WITH_RELATIONS

from easy_thumbnails.alias import aliases
from easy_thumbnails.files import get_thumbnailer

from wger.exercises.models import Exercise
from wger.exercises.models import ExerciseCategory
from wger.exercises.models import ExerciseComment
from wger.exercises.models import ExerciseImage
from wger.exercises.models import Muscle
from wger.exercises.models import Language
from wger.exercises.models import Equipment

class ExerciseResource(ModelResource):
    category = fields.ToOneField('wger.exercises.api.resources.ExerciseCategoryResource',
                                 'category')
    muscles = fields.ToManyField('wger.exercises.api.resources.MuscleResource', 'muscles')
    muscles_secondary = fields.ToManyField('wger.exercises.api.resources.MuscleResource',
                                           'muscles_secondary')
    comments = fields.ToManyField('wger.exercises.api.resources.ExerciseCommentResource',
                                  'exercisecomment_set')
    images = fields.ToManyField('wger.exercises.api.resources.ExerciseImageResource',
                                'exerciseimage_set')
    equipment = fields.ToManyField('wger.exercises.api.resources.EquipmentResource',
                                   'equipment')
    language = fields.ToOneField('wger.exercises.api.resources.LanguageResource', 'language')
    creation_date = fields.DateField(attribute='creation_date', null=True)

    class Meta:
        queryset = Exercise.objects.all()
        filtering = {'id': ALL,
                     "category": ALL_WITH_RELATIONS,
                     "creation_date": ALL,
                     "description": ALL,
                     "images": ALL_WITH_RELATIONS,
                     "language": ALL_WITH_RELATIONS,
                     "muscles": ALL_WITH_RELATIONS,
                     "status": ALL,
                     "name": ALL}

class EquipmentResource(ModelResource):

    class Meta:
        queryset = Equipment.objects.all()
        filtering = {'id': ALL,
                     "name": ALL}

class ExerciseCategoryResource(ModelResource):

    class Meta:
        queryset = ExerciseCategory.objects.all()
        filtering = {'id': ALL,
                     "name": ALL}

class ExerciseImageResource(ModelResource):
    exercise = fields.ToOneField('wger.exercises.api.resources.ExerciseResource', 'exercise')

    class Meta:
        queryset = ExerciseImage.objects.all()
        filtering = {'id': ALL,
                     "image": ALL,
                     "is_main": ALL,
                     "license": ALL,
                     "license_author": ALL}

    def dehydrate(self, bundle):
        '''
        Also send the URLs for the thumbnailed pictures
        '''
        thumbnails = {}
        for alias in aliases.all():
            t = get_thumbnailer(bundle.obj.image)
            thumbnails[alias] = {'url': t.get_thumbnail(aliases.get(alias)).url,
                                 'settings': aliases.get(alias)}

        bundle.data['thumbnails'] = thumbnails
        return bundle

class ExerciseCommentResource(ModelResource):
    exercise = fields.ToOneField('wger.exercises.api.resources.ExerciseResource', 'exercise')

    class Meta:
        queryset = ExerciseComment.objects.all()
        filtering = {'id': ALL,
                     "comment": ALL,
                     "exercise": ALL_WITH_RELATIONS}

class MuscleResource(ModelResource):
    class Meta:
        queryset = Muscle.objects.all()
        filtering = {'id': ALL,
                     "name": ALL,
                     "is_front": ALL}

class LanguageResource(ModelResource):
    class Meta:
        queryset = Language.objects.all()
        filtering = {'id': ALL,
                     "full_name": ALL,
                     "short_name": ALL}
<|endoftext|>


<commit_before>import os
import platform
import psutil
import subprocess
from ARKDaemon.ServerRcon import ServerRcon

class ArkServer(object):
    def __init__(self, config):
        self.config = config
        self.pid_file = os.path.join('ark.pid')
        self.platform = platform.system()

    def start(self):
        """
            subprocess create the process, record the PID of the process and write to a PID file.
            Only issue I can think of is if the server is forcefully closed by the OS
            and the server isn't actually running.
            Maybe I need to add a check in the status module to delete the PID file if the server
            is not running... Will have to test and find out.
        """
        if os.path.isfile(self.pid_file):
            # Get a list of active PID's and see if the PID in the file exists. If not, delete the PID file.
            with open(self.pid_file, 'r') as pidfile:
                pid = pidfile.read()
            try:
                p = psutil.Process(pid)
                sys.exit('ARK is running. Shut the server down with --stop before starting it.')
            except:
                os.remove(self.pid_file)
                pid = ''

        # Get the correct binary to start the server
        if self.platform is "Windows":
            binary = os.path.join('ARK', 'ShooterGame', 'Binaries', 'Win64', 'ShooterGameServer.exe')
        elif self.platform is "Linux":
            binary = os.path.join('ARK', 'ShooterGame', 'Binaries', 'Linux', 'ShooterGameServer')
        else:
            sys.exit("You are running on an unsupported platform: {}".format(self.platform))

        # Format the password command so we can spit it out properly
        if self.config['ARK']['serverpassword'] is not '':
            server_password = "={}".format(self.config['ARK']['serverpassword'])
        else:
            server_password = ''

        # Make a comma seperated list of the mods from config
        try:
            if self.config['ARK']['mods']:
                my_mods = ",".join(self.config['ARK']['mods'])
        except(KeyError):
            my_mods = ''

        # Get the start command formed.
        start_cmd = "{my_binary} " \
                    "{map}" \
                    "?GameModIds={mods}" \
                    "?MaxPlayers={players}" \
                    "?Port=7777" \
                    "?QueryPort=27015" \
                    "?RCONEnabled=True" \
                    "?RCONPort=32330" \
                    "?ServerAdminPassword={adminpass}" \
                    "?ServerPassword{serverpass}" \
                    "?SessionName={sessionname}" \
                    "?listen"\
            .format(my_binary=binary,
                    map=self.config['ARK']['map'],
                    mods=my_mods,
                    players=self.config['ARK']['players'],
                    adminpass=self.config['ARK']['serveradminpassword'],
                    serverpass=server_password,
                    sessionname=self.config['ARK']['sessionname'],
                    )
        # Start the server, you nut!
        server_process = subprocess.call(start_cmd, shell=False)
        pid = server_process.pid
        with open(self.pid_file, 'w') as file:
            file.write(pid)
        print "Server launched! Please allow 5 to 10 minutes for server to start"

    def stop(self, safe=False):
        if not os.path.isfile(self.pid_file):
            sys.exit("Server is not running or PID file is missing! No need to stop (or you broke something).")
        else:
            # Check if the safe flag is set! If set, run the saveworld operation before screwing with the server.
            # Read the contents of the PID file and kill the process by PID.
            if safe:
                this = ServerRcon('127.0.0.1', 32330, self.config['ARK']['ServerAdminPassword'], 'saveworld')
                this.run_command()

            # Move forward to stop the server by PID. PID file defined as self.pid_file
            with open(self.pid_file, 'r') as pidfile:
                pid = pidfile.read()

            print "Killing process with PID: {}".format(pid)
            p = psutil.Process(pid)
            p.terminate()
            os.remove(self.pid_file)
            pid = ''
            print "All stop operations are complete."
<commit_msg>subprocess didn't give control back to the script. Trying another method.<commit_after>import os
import platform
import psutil
import subprocess
from ARKDaemon.ServerRcon import ServerRcon

class ArkServer(object):
    def __init__(self, config):
        self.config = config
        self.pid_file = os.path.join('ark.pid')
        self.platform = platform.system()

    def start(self):
        """
            subprocess create the process, record the PID of the process and write to a PID file.
            Only issue I can think of is if the server is forcefully closed by the OS
            and the server isn't actually running.
            Maybe I need to add a check in the status module to delete the PID file if the server
            is not running... Will have to test and find out.
        """
        if os.path.isfile(self.pid_file):
            # Get a list of active PID's and see if the PID in the file exists. If not, delete the PID file.
            with open(self.pid_file, 'r') as pidfile:
                pid = pidfile.read()
            try:
                p = psutil.Process(pid)
                sys.exit('ARK is running. Shut the server down with --stop before starting it.')
            except:
                os.remove(self.pid_file)
                pid = ''

        # Get the correct binary to start the server
        if self.platform is "Windows":
            binary = os.path.join('ARK', 'ShooterGame', 'Binaries', 'Win64', 'ShooterGameServer.exe')
        elif self.platform is "Linux":
            binary = os.path.join('ARK', 'ShooterGame', 'Binaries', 'Linux', 'ShooterGameServer')
        else:
            sys.exit("You are running on an unsupported platform: {}".format(self.platform))

        # Format the password command so we can spit it out properly
        if self.config['ARK']['serverpassword'] is not '':
            server_password = "={}".format(self.config['ARK']['serverpassword'])
        else:
            server_password = ''

        # Make a comma seperated list of the mods from config
        try:
            if self.config['ARK']['mods']:
                my_mods = ",".join(self.config['ARK']['mods'])
        except(KeyError):
            my_mods = ''

        # Get the start command formed.
        start_cmd = "{my_binary} " \
                    "{map}" \
                    "?GameModIds={mods}" \
                    "?MaxPlayers={players}" \
                    "?Port=7777" \
                    "?QueryPort=27015" \
                    "?RCONEnabled=True" \
                    "?RCONPort=32330" \
                    "?ServerAdminPassword={adminpass}" \
                    "?ServerPassword{serverpass}" \
                    "?SessionName={sessionname}" \
                    "?listen"\
            .format(my_binary=binary,
                    map=self.config['ARK']['map'],
                    mods=my_mods,
                    players=self.config['ARK']['players'],
                    adminpass=self.config['ARK']['serveradminpassword'],
                    serverpass=server_password,
                    sessionname=self.config['ARK']['sessionname'],
                    )
        # Start the server, you nut!
        server_process = subprocess.Popen(start_cmd, shell=False)
        pid = server_process.pid
        with open(self.pid_file, 'w') as file:
            file.write(pid)
        print "Server launched! Please allow 5 to 10 minutes for server to start"

    def stop(self, safe=False):
        if not os.path.isfile(self.pid_file):
            sys.exit("Server is not running or PID file is missing! No need to stop (or you broke something).")
        else:
            # Check if the safe flag is set! If set, run the saveworld operation before screwing with the server.
            # Read the contents of the PID file and kill the process by PID.
            if safe:
                this = ServerRcon('127.0.0.1', 32330, self.config['ARK']['ServerAdminPassword'], 'saveworld')
                this.run_command()

            # Move forward to stop the server by PID. PID file defined as self.pid_file
            with open(self.pid_file, 'r') as pidfile:
                pid = pidfile.read()

            print "Killing process with PID: {}".format(pid)
            p = psutil.Process(pid)
            p.terminate()
            os.remove(self.pid_file)
            pid = ''
            print "All stop operations are complete."
<|endoftext|>


<commit_before>import json
from functools import wraps

from django.http import HttpResponseRedirect
from django.shortcuts import render
from django.utils import translation
from django.views.decorators.cache import never_cache
from django.views.decorators.csrf import csrf_exempt, csrf_protect
from django.views.decorators.http import require_POST

from mobility.decorators import mobile_template
from statsd import statsd
import waffle

from fjord.base.browsers import UNKNOWN
from fjord.base.urlresolvers import reverse
from fjord.base.utils import (
    actual_ip_plus_context,
    ratelimit,
    smart_str,
    translate_country_name
)
from fjord.feedback import config
from fjord.feedback import models
from fjord.feedback.forms import ResponseForm
from fjord.feedback.utils import clean_url
from fjord.feedback.config import TRUNCATE_LENGTH

def happy_redirect(request):
    # TODO: Remove this when the addon gets fixed and is pointing to
    # the correct urls.
    return HttpResponseRedirect(reverse('feedback') + '#happy')

def sad_redirect(request):
    # TODO: Remove this when the addon gets fixed and is pointing to
    # the correct urls.
    return HttpResponseRedirect(reverse('feedback') + '#sad')

@mobile_template('feedback/{mobile/}download_firefox.html')
def download_firefox(request, template):
    return render(request, template)

def thanks(request):
    template = 'feedback/thanks.html'
    return render(request, template)

def requires_firefox(func):
    """Redirects to "download firefox" page if not Firefox.

    If it isn't a Firefox browser, then we don't want to deal with it.

    This is a temporary solution. See bug #848568.

    """
    @wraps(func)
    def _requires_firefox(request, *args, **kwargs):
        # Note: This is sort of a lie. What's going on here is that
        # parse_ua only parses Firefox-y browsers. So if it's UNKNOWN
        # at this point, then it's not Firefox-y. If parse_ua ever
        # changes, then this will cease to be true.
        if request.BROWSER.browser == UNKNOWN:
            return HttpResponseRedirect(reverse('download-firefox'))
        return func(request, *args, **kwargs)
    return _requires_firefox

@ratelimit(rulename='doublesubmit_1p10m',
           keyfun=actual_ip_plus_context(
               lambda req: req.POST.get('description', u'no description')),
           rate='1/10m')
@ratelimit(rulename='50ph', rate='50/h')
def _handle_feedback_post(request, locale=None, product=None,
                          version=None, channel=None):
    """Saves feedback post to db accounting for throttling

    :arg request: request we're handling the post for
    :arg locale: locale specified in the url
    :arg product: None or the Product
    :arg version: validated and sanitized version specified in the url
    :arg channel: validated and sanitized channel specified in the url

    """
    if getattr(request, 'limited', False):
        # If we're throttled, then return the thanks page, but don't
        # add the response to the db.
        return HttpResponseRedirect(reverse('thanks'))

    # Get the form and run is_valid() so it goes through the
    # validation and cleaning machinery. We don't really care if it's
    # valid, though, since we will take what we got and do the best we
    # can with it. Error validation is now in JS.
    form = ResponseForm(request.POST)
    form.is_valid()

    get_data = request.GET.copy()

    data = form.cleaned_data

    description = data.get('description', u'').strip()
    if not description:
        # If there's no description, then there's nothing to do here,
        # so thank the user and move on.
        return HttpResponseRedirect(reverse('thanks'))

    opinion = models.Response(
        # Data coming from the user
        happy=data['happy'],
        url=clean_url(data.get('url', u'').strip()),
        description=description,

        # Pulled from the form data or the url
        locale=data.get('locale', locale),

        # Data from mobile devices which is probably only
        # applicable to mobile devices
        manufacturer=data.get('manufacturer', ''),
        device=data.get('device', ''),
    )

    # Add user_agent and inferred data.
    user_agent = request.META.get('HTTP_USER_AGENT', '')
    if user_agent:
        browser = request.BROWSER

        opinion.user_agent = user_agent
        opinion.browser = browser.browser
        opinion.browser_version = browser.browser_version
        opinion.browser_platform = browser.platform
        if browser.platform == 'Windows':
            opinion.browser_platform += ' ' + browser.platform_version

    # source is src or utm_source
    source = (
        get_data.pop('src', [u''])[0] or
        get_data.pop('utm_source', [u''])[0]
    )
    if source:
        opinion.source = source[:100]

    campaign = get_data.pop('utm_campaign', [u''])[0]
    if campaign:
        opinion.campaign = campaign[:100]

    platform = u''

    if product:
        # If we have a product at this point, then it came from the
        # url and it's a Product instance and we need to turn it into
        # the product.db_name which is a string.
        product = product.db_name
    else:
        # Check the POST data for the product.
        product = data.get('product', '')

    # For the version, we try the url data, then the POST data.
    version = version or data.get('version', '')

    # At this point, we have a bunch of values, but we might be
    # missing some values, too. We're going to cautiously infer data
    # from the user agent where we're very confident it's appropriate
    # to do so.
    if request.BROWSER != UNKNOWN:
        # If we don't have a product, try to infer that from the user
        # agent.
        if not product:
            product = models.Response.infer_product(request.BROWSER)

        # If we have a product and it matches the user agent product,
        # then we can infer the version and platform from the user
        # agent if they're missing.
        if (product and product == models.Response.infer_product(
                request.BROWSER)):
            if not version:
                version = request.BROWSER.browser_version
            if not platform:
                platform = models.Response.infer_platform(
                    product, request.BROWSER)

    # Make sure values are at least empty strings--no Nones.
    opinion.product = product or u''
    opinion.version = version or u''
    opinion.channel = channel or u''
    opinion.platform = platform or u''

    opinion.save()

    # If there was an email address, save that separately.
    if data.get('email_ok') and data.get('email'):
        e = models.ResponseEmail(email=data['email'], opinion=opinion)
        e.save()

    # If there's browser data, save that separately.
    if data.get('browser_ok'):
        # This comes in as a JSON string. Because we're using
        # JSONObjectField, we need to convert it back to Python and
        # then save it. This is kind of silly, but it does guarantee
        # we have valid JSON.
        try:
            browser_data = data['browser_data']
            browser_data = json.loads(browser_data)

        except ValueError:
            # Handles empty string and any non-JSON value.
            statsd.incr('feedback.browserdata.badvalue')

        except KeyError:
            # Handles the case where it's missing from the data
            # dict. If it's missing, we don't want to do anything
            # including metrics.
            pass

        else:
            # If browser_data isn't an empty dict, then save it.
            if browser_data:
                rti = models.ResponseTroubleshootingInfo(
                    data=browser_data, opinion=opinion)
                rti.save()
                statsd.incr('feedback.browserdata.optin')

    else:
        statsd.incr('feedback.browserdata.optout')

    if get_data:
        # There was extra context in the query string, so we grab that
        # with some restrictions and save it separately.
        slop = {}

        # We capture at most the first 20 key/val pairs
        get_data_items = sorted(get_data.items())[:20]

        for key, val in get_data_items:
            # Keys can be at most 20 characters long.
            key = key[:20]
            if len(val) == 1:
                val = val[0]

            # Values can be at most 20 characters long.
            val = val[:100]
            slop[key.encode('utf-8')] = val.encode('utf-8')

        context = models.ResponseContext(data=slop, opinion=opinion)
        context.save()

    if data['happy']:
        statsd.incr('feedback.happy')
    else:
        statsd.incr('feedback.sad')

    request.session['opinion_id'] = opinion.id

    return HttpResponseRedirect(reverse('thanks'))

@csrf_protect
def generic_feedback(request, locale=None, product=None, version=None,
                     channel=None):
    """Generic feedback form for desktop and mobile"""
    form = ResponseForm()

    if request.method == 'POST':
        return _handle_feedback_post(request, locale, product,
                                     version, channel)

    return render(request, 'feedback/generic_feedback.html', {
        'form': form,
        'product': product,
        'TRUNCATE_LENGTH': TRUNCATE_LENGTH,
    })

@csrf_exempt
def firefox_os_stable_feedback(request, locale=None, product=None,
                               version=None, channel=None):
    # Localized country names are in region files in product
    # details. We try really hard to use localized country names, so
    # we use gettext and if that's not available, use whatever is in
    # product details.
    countries = [
        (code, translate_country_name(translation.get_language(),
                                      code, name, name_l10n))
        for code, name, name_l10n in config.FIREFOX_OS_COUNTRIES
    ]

    return render(request, 'feedback/fxos_feedback.html', {
        'countries': countries,
        'devices': config.FIREFOX_OS_DEVICES,
        'TRUNCATE_LENGTH': TRUNCATE_LENGTH,
    })

@csrf_exempt
@require_POST
def android_about_feedback(request, locale=None):
    """A view specifically for Firefox for Android.

    Firefox for Android has a feedback form built in that generates
    POSTS directly to Input, and is always sad or ideas. Since Input no
    longer supports idea feedbacks, everything is Sad.

    FIXME - measure usage of this and nix it when we can. See bug
    #964292.

    """
    # Firefox for Android only sends up sad and idea responses, but it
    # uses the old `_type` variable from old Input. Tweak the data to do
    # what FfA means, not what it says.

    # Make `request.POST` mutable.
    request.POST = request.POST.copy()

    # For _type, 1 is happy, 2 is sad, 3 is idea. We convert that so
    # that _type = 1 -> happy = 1 and everything else -> happy = 0.
    if request.POST.get('_type') == '1':
        happy = 1
    else:
        happy = 0
    request.POST['happy'] = happy

    # Note: product, version and channel are always None in this view
    # since this is to handle backwards-compatibility. So we don't
    # bother passing them along.

    # We always return Thanks! now and ignore errors.
    return _handle_feedback_post(request, locale)

PRODUCT_OVERRIDE = {
}

def persist_feedbackdev(fun):
    """Persists a feedbackdev flag set via querystring in the cookies"""
    @wraps(fun)
    def _persist_feedbackdev(request, *args, **kwargs):
        qs_feedbackdev = request.GET.get('feedbackdev', None)
        resp = fun(request, *args, **kwargs)
        if resp is not None and qs_feedbackdev is not None:
            resp.set_cookie(waffle.COOKIE_NAME % 'feedbackdev', qs_feedbackdev)

        return resp
    return _persist_feedbackdev

@csrf_exempt
@never_cache
@persist_feedbackdev
def feedback_router(request, product=None, version=None, channel=None,
                    *args, **kwargs):
    """Determine a view to use, and call it.

    If product is given, reference `product_routes` to look up a view.
    If `product` is not passed, or isn't found in `product_routes`,
    asssume the user is either a stable desktop Firefox or a stable
    mobile Firefox based on the parsed UA, and serve them the
    appropriate page. This is to handle the old formname way of doing
    things. At some point P, we should measure usage of the old
    formnames and deprecate them.

    This also handles backwards-compatability with the old Firefox for
    Android form which can't have a CSRF token.

    Note: We never want to cache this view.

    """
    view = None

    if '_type' in request.POST:
        # Checks to see if `_type` is in the POST data and if so this
        # is coming from Firefox for Android which doesn't know
        # anything about csrf tokens. If that's the case, we send it
        # to a view specifically for FfA Otherwise we pass it to one
        # of the normal views, which enforces CSRF. Also, nix the
        # product just in case we're crossing the streams and
        # confusing new-style product urls with old-style backwards
        # compatability for the Android form.
        #
        # FIXME: Remove this hairbrained monstrosity when we don't need to
        # support the method that Firefox for Android currently uses to
        # post feedback which worked with the old input.mozilla.org.
        view = android_about_feedback
        product = None

        # This lets us measure how often this section of code kicks
        # off and thus how often old android stuff is happening. When
        # we're not seeing this anymore, we can nix all the old
        # android stuff.
        statsd.incr('feedback.oldandroid')

        return android_about_feedback(request, request.locale)

    # FIXME - validate these better
    product = smart_str(product, fallback=None)
    version = smart_str(version)
    channel = smart_str(channel).lower()

    if product == 'fxos' or request.BROWSER.browser == 'Firefox OS':
        # Firefox OS gets shunted to a different form which has
        # different Firefox OS specific questions.
        view = firefox_os_stable_feedback
        product = 'fxos'

    elif product in PRODUCT_OVERRIDE:
        # If the product is really a form name, we use that
        # form specifically.
        view = PRODUCT_OVERRIDE[product]
        product = None

    elif (product is None
          or product not in models.Product.get_product_map()):

        picker_products = models.Product.objects.filter(
            enabled=True, on_picker=True)
        return render(request, 'feedback/picker.html', {
            'products': picker_products
        })

    product = models.Product.from_slug(product)

    if view is None:
        view = generic_feedback

    return view(request, request.locale, product, version, channel,
                *args, **kwargs)

def cyoa(request):
    template = 'feedback/picker.html'

    products = models.Product.objects.all()
    return render(request, template, {
        'products': products
    })
<commit_msg>[bug 1120952] Fix remotetroubleshooting metrics<commit_after>import json
from functools import wraps

from django.http import HttpResponseRedirect
from django.shortcuts import render
from django.utils import translation
from django.views.decorators.cache import never_cache
from django.views.decorators.csrf import csrf_exempt, csrf_protect
from django.views.decorators.http import require_POST

from mobility.decorators import mobile_template
from statsd import statsd
import waffle

from fjord.base.browsers import UNKNOWN
from fjord.base.urlresolvers import reverse
from fjord.base.utils import (
    actual_ip_plus_context,
    ratelimit,
    smart_str,
    translate_country_name
)
from fjord.feedback import config
from fjord.feedback import models
from fjord.feedback.forms import ResponseForm
from fjord.feedback.utils import clean_url
from fjord.feedback.config import TRUNCATE_LENGTH

def happy_redirect(request):
    # TODO: Remove this when the addon gets fixed and is pointing to
    # the correct urls.
    return HttpResponseRedirect(reverse('feedback') + '#happy')

def sad_redirect(request):
    # TODO: Remove this when the addon gets fixed and is pointing to
    # the correct urls.
    return HttpResponseRedirect(reverse('feedback') + '#sad')

@mobile_template('feedback/{mobile/}download_firefox.html')
def download_firefox(request, template):
    return render(request, template)

def thanks(request):
    template = 'feedback/thanks.html'
    return render(request, template)

def requires_firefox(func):
    """Redirects to "download firefox" page if not Firefox.

    If it isn't a Firefox browser, then we don't want to deal with it.

    This is a temporary solution. See bug #848568.

    """
    @wraps(func)
    def _requires_firefox(request, *args, **kwargs):
        # Note: This is sort of a lie. What's going on here is that
        # parse_ua only parses Firefox-y browsers. So if it's UNKNOWN
        # at this point, then it's not Firefox-y. If parse_ua ever
        # changes, then this will cease to be true.
        if request.BROWSER.browser == UNKNOWN:
            return HttpResponseRedirect(reverse('download-firefox'))
        return func(request, *args, **kwargs)
    return _requires_firefox

@ratelimit(rulename='doublesubmit_1p10m',
           keyfun=actual_ip_plus_context(
               lambda req: req.POST.get('description', u'no description')),
           rate='1/10m')
@ratelimit(rulename='50ph', rate='50/h')
def _handle_feedback_post(request, locale=None, product=None,
                          version=None, channel=None):
    """Saves feedback post to db accounting for throttling

    :arg request: request we're handling the post for
    :arg locale: locale specified in the url
    :arg product: None or the Product
    :arg version: validated and sanitized version specified in the url
    :arg channel: validated and sanitized channel specified in the url

    """
    if getattr(request, 'limited', False):
        # If we're throttled, then return the thanks page, but don't
        # add the response to the db.
        return HttpResponseRedirect(reverse('thanks'))

    # Get the form and run is_valid() so it goes through the
    # validation and cleaning machinery. We don't really care if it's
    # valid, though, since we will take what we got and do the best we
    # can with it. Error validation is now in JS.
    form = ResponseForm(request.POST)
    form.is_valid()

    get_data = request.GET.copy()

    data = form.cleaned_data

    description = data.get('description', u'').strip()
    if not description:
        # If there's no description, then there's nothing to do here,
        # so thank the user and move on.
        return HttpResponseRedirect(reverse('thanks'))

    opinion = models.Response(
        # Data coming from the user
        happy=data['happy'],
        url=clean_url(data.get('url', u'').strip()),
        description=description,

        # Pulled from the form data or the url
        locale=data.get('locale', locale),

        # Data from mobile devices which is probably only
        # applicable to mobile devices
        manufacturer=data.get('manufacturer', ''),
        device=data.get('device', ''),
    )

    # Add user_agent and inferred data.
    user_agent = request.META.get('HTTP_USER_AGENT', '')
    if user_agent:
        browser = request.BROWSER

        opinion.user_agent = user_agent
        opinion.browser = browser.browser
        opinion.browser_version = browser.browser_version
        opinion.browser_platform = browser.platform
        if browser.platform == 'Windows':
            opinion.browser_platform += ' ' + browser.platform_version

    # source is src or utm_source
    source = (
        get_data.pop('src', [u''])[0] or
        get_data.pop('utm_source', [u''])[0]
    )
    if source:
        opinion.source = source[:100]

    campaign = get_data.pop('utm_campaign', [u''])[0]
    if campaign:
        opinion.campaign = campaign[:100]

    platform = u''

    if product:
        # If we have a product at this point, then it came from the
        # url and it's a Product instance and we need to turn it into
        # the product.db_name which is a string.
        product = product.db_name
    else:
        # Check the POST data for the product.
        product = data.get('product', '')

    # For the version, we try the url data, then the POST data.
    version = version or data.get('version', '')

    # At this point, we have a bunch of values, but we might be
    # missing some values, too. We're going to cautiously infer data
    # from the user agent where we're very confident it's appropriate
    # to do so.
    if request.BROWSER != UNKNOWN:
        # If we don't have a product, try to infer that from the user
        # agent.
        if not product:
            product = models.Response.infer_product(request.BROWSER)

        # If we have a product and it matches the user agent product,
        # then we can infer the version and platform from the user
        # agent if they're missing.
        if (product and product == models.Response.infer_product(
                request.BROWSER)):
            if not version:
                version = request.BROWSER.browser_version
            if not platform:
                platform = models.Response.infer_platform(
                    product, request.BROWSER)

    # Make sure values are at least empty strings--no Nones.
    opinion.product = product or u''
    opinion.version = version or u''
    opinion.channel = channel or u''
    opinion.platform = platform or u''

    opinion.save()

    # If there was an email address, save that separately.
    if data.get('email_ok') and data.get('email'):
        e = models.ResponseEmail(email=data['email'], opinion=opinion)
        e.save()
        statsd.incr('feedback.emaildata.optin')

    # If there's browser data, save that separately.
    if data.get('browser_ok'):
        # This comes in as a JSON string. Because we're using
        # JSONObjectField, we need to convert it back to Python and
        # then save it. This is kind of silly, but it does guarantee
        # we have valid JSON.
        try:
            browser_data = data['browser_data']
            browser_data = json.loads(browser_data)

        except ValueError:
            # Handles empty string and any non-JSON value.
            statsd.incr('feedback.browserdata.badvalue')

        except KeyError:
            # Handles the case where it's missing from the data
            # dict. If it's missing, we don't want to do anything
            # including metrics.
            pass

        else:
            # If browser_data isn't an empty dict, then save it.
            if browser_data:
                rti = models.ResponseTroubleshootingInfo(
                    data=browser_data, opinion=opinion)
                rti.save()
                statsd.incr('feedback.browserdata.optin')

    if get_data:
        # There was extra context in the query string, so we grab that
        # with some restrictions and save it separately.
        slop = {}

        # We capture at most the first 20 key/val pairs
        get_data_items = sorted(get_data.items())[:20]

        for key, val in get_data_items:
            # Keys can be at most 20 characters long.
            key = key[:20]
            if len(val) == 1:
                val = val[0]

            # Values can be at most 20 characters long.
            val = val[:100]
            slop[key.encode('utf-8')] = val.encode('utf-8')

        context = models.ResponseContext(data=slop, opinion=opinion)
        context.save()
        statsd.incr('feedback.contextdata.optin')

    if data['happy']:
        statsd.incr('feedback.happy')
    else:
        statsd.incr('feedback.sad')

    request.session['opinion_id'] = opinion.id

    return HttpResponseRedirect(reverse('thanks'))

@csrf_protect
def generic_feedback(request, locale=None, product=None, version=None,
                     channel=None):
    """Generic feedback form for desktop and mobile"""
    form = ResponseForm()

    if request.method == 'POST':
        return _handle_feedback_post(request, locale, product,
                                     version, channel)

    return render(request, 'feedback/generic_feedback.html', {
        'form': form,
        'product': product,
        'TRUNCATE_LENGTH': TRUNCATE_LENGTH,
    })

@csrf_exempt
def firefox_os_stable_feedback(request, locale=None, product=None,
                               version=None, channel=None):
    # Localized country names are in region files in product
    # details. We try really hard to use localized country names, so
    # we use gettext and if that's not available, use whatever is in
    # product details.
    countries = [
        (code, translate_country_name(translation.get_language(),
                                      code, name, name_l10n))
        for code, name, name_l10n in config.FIREFOX_OS_COUNTRIES
    ]

    return render(request, 'feedback/fxos_feedback.html', {
        'countries': countries,
        'devices': config.FIREFOX_OS_DEVICES,
        'TRUNCATE_LENGTH': TRUNCATE_LENGTH,
    })

@csrf_exempt
@require_POST
def android_about_feedback(request, locale=None):
    """A view specifically for Firefox for Android.

    Firefox for Android has a feedback form built in that generates
    POSTS directly to Input, and is always sad or ideas. Since Input no
    longer supports idea feedbacks, everything is Sad.

    FIXME - measure usage of this and nix it when we can. See bug
    #964292.

    """
    # Firefox for Android only sends up sad and idea responses, but it
    # uses the old `_type` variable from old Input. Tweak the data to do
    # what FfA means, not what it says.

    # Make `request.POST` mutable.
    request.POST = request.POST.copy()

    # For _type, 1 is happy, 2 is sad, 3 is idea. We convert that so
    # that _type = 1 -> happy = 1 and everything else -> happy = 0.
    if request.POST.get('_type') == '1':
        happy = 1
    else:
        happy = 0
    request.POST['happy'] = happy

    # Note: product, version and channel are always None in this view
    # since this is to handle backwards-compatibility. So we don't
    # bother passing them along.

    # We always return Thanks! now and ignore errors.
    return _handle_feedback_post(request, locale)

PRODUCT_OVERRIDE = {
}

def persist_feedbackdev(fun):
    """Persists a feedbackdev flag set via querystring in the cookies"""
    @wraps(fun)
    def _persist_feedbackdev(request, *args, **kwargs):
        qs_feedbackdev = request.GET.get('feedbackdev', None)
        resp = fun(request, *args, **kwargs)
        if resp is not None and qs_feedbackdev is not None:
            resp.set_cookie(waffle.COOKIE_NAME % 'feedbackdev', qs_feedbackdev)

        return resp
    return _persist_feedbackdev

@csrf_exempt
@never_cache
@persist_feedbackdev
def feedback_router(request, product=None, version=None, channel=None,
                    *args, **kwargs):
    """Determine a view to use, and call it.

    If product is given, reference `product_routes` to look up a view.
    If `product` is not passed, or isn't found in `product_routes`,
    asssume the user is either a stable desktop Firefox or a stable
    mobile Firefox based on the parsed UA, and serve them the
    appropriate page. This is to handle the old formname way of doing
    things. At some point P, we should measure usage of the old
    formnames and deprecate them.

    This also handles backwards-compatability with the old Firefox for
    Android form which can't have a CSRF token.

    Note: We never want to cache this view.

    """
    view = None

    if '_type' in request.POST:
        # Checks to see if `_type` is in the POST data and if so this
        # is coming from Firefox for Android which doesn't know
        # anything about csrf tokens. If that's the case, we send it
        # to a view specifically for FfA Otherwise we pass it to one
        # of the normal views, which enforces CSRF. Also, nix the
        # product just in case we're crossing the streams and
        # confusing new-style product urls with old-style backwards
        # compatability for the Android form.
        #
        # FIXME: Remove this hairbrained monstrosity when we don't need to
        # support the method that Firefox for Android currently uses to
        # post feedback which worked with the old input.mozilla.org.
        view = android_about_feedback
        product = None

        # This lets us measure how often this section of code kicks
        # off and thus how often old android stuff is happening. When
        # we're not seeing this anymore, we can nix all the old
        # android stuff.
        statsd.incr('feedback.oldandroid')

        return android_about_feedback(request, request.locale)

    # FIXME - validate these better
    product = smart_str(product, fallback=None)
    version = smart_str(version)
    channel = smart_str(channel).lower()

    if product == 'fxos' or request.BROWSER.browser == 'Firefox OS':
        # Firefox OS gets shunted to a different form which has
        # different Firefox OS specific questions.
        view = firefox_os_stable_feedback
        product = 'fxos'

    elif product in PRODUCT_OVERRIDE:
        # If the product is really a form name, we use that
        # form specifically.
        view = PRODUCT_OVERRIDE[product]
        product = None

    elif (product is None
          or product not in models.Product.get_product_map()):

        picker_products = models.Product.objects.filter(
            enabled=True, on_picker=True)
        return render(request, 'feedback/picker.html', {
            'products': picker_products
        })

    product = models.Product.from_slug(product)

    if view is None:
        view = generic_feedback

    return view(request, request.locale, product, version, channel,
                *args, **kwargs)

def cyoa(request):
    template = 'feedback/picker.html'

    products = models.Product.objects.all()
    return render(request, template, {
        'products': products
    })
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
from __future__ import (absolute_import, division, print_function,
                        unicode_literals)

import codecs

import marisa_trie
import regex as re

ASCII_CHARS = ''.join([chr(i) for i in range(128)])

class CEDict(object):
    def __init__(self, cedict_file, traditional=False):
        self.hanzi = self._read_cedict(cedict_file, traditional=False)

    def _read_cedict(self, cedict_file, traditional=False):
        comment_re = re.compile('\s*#')
        lemma_re = re.compile('(?P<hanzi>[^]]+) \[(?P<pinyin>[^]]+)\] /(?P<english>.+)/')
        cedict = {}
        # cedict_file = os.path.join('data', cedict_file + '.txt')
        # cedict_file = pkg_resources.resource_filename(__name__, cedict_file)
        with codecs.open(cedict_file, 'r', 'utf-8') as f:
            for line in f:
                if comment_re.match(line):
                    pass
                elif lemma_re.match(line):
                    match = lemma_re.match(line)
                    hanzi = match.group('hanzi').split(' ')
                    pinyin = match.group('pinyin').split(' ')
                    english = match.group('english').split('/')
                    if traditional:
                        cedict[hanzi[0]] = (pinyin, english)  # traditional characters only
                    else:
                        cedict[hanzi[1]] = (pinyin, english)  # simplified characters only.
        return cedict

class CEDictTrie(CEDict):
    def __init__(self, dict_file, traditional=False):
        self.hanzi = self._read_cedict(dict_file, traditional=traditional)
        self.trie = self.construct_trie(self.hanzi)

    def construct_trie(self, hanzi):
        pairs = []
        for hz, df in self.hanzi.items():
            py, en = df
            py = str(''.join(filter(lambda x: x in ASCII_CHARS, ' '.join(py))))
            pairs.append((hz, (py.encode('utf-8'),)))
        trie = marisa_trie.RecordTrie(str('@s'), pairs)
        return trie

    def has_key(self, key):
        return key in self.hanzi

    def prefixes(self, s):
        return self.trie.prefixes(s)

    def longest_prefix(self, s):
        prefixes = self.prefixes(s)
        if not prefixes:
            return ''
        else:
            return sorted(prefixes, key=len)[-1]  # Sort by length and return last.

    def tokenize(self, s):
        tokens = []
        while s:
            token = self.longest_prefix(s)
            if token:
                tokens.append(token)
                s = s[len(token):]
            else:
                tokens.append(s[0])
                s = s[1:]
        return tokens
<commit_msg>Factorer CEDict class back into CEDictTrie<commit_after># -*- coding: utf-8 -*-
from __future__ import (absolute_import, division, print_function,
                        unicode_literals)

import codecs

import marisa_trie
import regex as re

ASCII_CHARS = ''.join([chr(i) for i in range(128)])

class CEDictTrie(object):
    def __init__(self, cedict_file, traditional=False):
        """Construct a trie over CC-CEDict

        Args:
            cedict_file (str): path to the CC-CEDict dictionary
            traditional (bool): if True, use traditional characters
        """
        self.hanzi = self._read_cedict(cedict_file, traditional=traditional)
        self.trie = self._construct_trie(self.hanzi)

    def _read_cedict(self, cedict_file, traditional=False):
        comment_re = re.compile('\s*#')
        lemma_re = re.compile('(?P<hanzi>[^]]+) \[(?P<pinyin>[^]]+)\] /(?P<english>.+)/')
        cedict = {}
        with codecs.open(cedict_file, 'r', 'utf-8') as f:
            for line in f:
                if comment_re.match(line):
                    pass
                elif lemma_re.match(line):
                    match = lemma_re.match(line)
                    hanzi = match.group('hanzi').split(' ')
                    pinyin = match.group('pinyin').split(' ')
                    english = match.group('english').split('/')
                    if traditional:
                        cedict[hanzi[0]] = (pinyin, english)  # traditional characters only
                    else:
                        cedict[hanzi[1]] = (pinyin, english)  # simplified characters only.
        return cedict

    def _construct_trie(self, hanzi):
        pairs = []
        for hz, df in self.hanzi.items():
            py, en = df
            py = str(''.join(filter(lambda x: x in ASCII_CHARS, ' '.join(py))))
            pairs.append((hz, (py.encode('utf-8'),)))
        trie = marisa_trie.RecordTrie(str('@s'), pairs)
        return trie

    def has_key(self, key):
        return key in self.hanzi

    def prefixes(self, s):
        return self.trie.prefixes(s)

    def longest_prefix(self, s):
        prefixes = self.prefixes(s)
        if not prefixes:
            return ''
        else:
            return sorted(prefixes, key=len)[-1]  # Sort by length and return last.

    def tokenize(self, s):
        tokens = []
        while s:
            token = self.longest_prefix(s)
            if token:
                tokens.append(token)
                s = s[len(token):]
            else:
                tokens.append(s[0])
                s = s[1:]
        return tokens
<|endoftext|>


<commit_before>"""Main Flask integration."""

from os import environ

from boto3.session import Session
from flask import current_app

from .errors import ConfigurationError

class DynamoLazyTables(object):
    """Manages access to Dynamo Tables."""
    def __init__(self, connection, table_config):
        self._table_config = table_config
        self._connection = connection

    def __getitem__(self, name):
        """Get the connection for a table by name."""
        return self._connection.Table(name)

    def keys(self):
        """The table names in our config."""
        return [t['TableName'] for t in self._table_config]

    def len(self):
        """The number of tables we are configured for."""
        return len(self.keys())

    def items(self):
        """The table tuples (name, connection.Table())."""
        for table_name in self.keys():
            yield (table_name, self[table_name])

    def _wait(self, table_name, type_waiter):
        waiter = self._connection.meta.client.get_waiter(type_waiter)
        waiter.wait(TableName=table_name)

    def wait_exists(self, table_name):
        self._wait(table_name, 'table_exists')

    def wait_not_exists(self, table_name):
        self._wait(table_name, 'table_not_exists')

    def create_all(self, wait=False):
        for table in self._table_config:
            self._connection.create_table(**table)
        if wait:
            for table in self._table_config:
                self.wait_exists(table['TableName'])

    def destroy_all(self, wait=False):
        for table in self._table_config:
            table = self._connection.Table(table['TableName'])
            table.delete()
        if wait:
            for table in self._table_config:
                self.wait_not_exists(table['TableName'])

class Dynamo(object):
    """DynamoDB engine manager."""

    DEFAULT_REGION = 'us-east-1'

    def __init__(self, app=None):
        """
        Initialize this extension.

        :param obj app: The Flask application (optional).
        """
        self.app = app
        if app is not None:
            self.init_app(app)

    def init_app(self, app):
        """
        Initialize this extension.

        :param obj app: The Flask application.
        """

        self._init_settings(app)
        self._check_settings(app)

        app.extensions['dynamo'] = self

        self.tables = DynamoLazyTables(self.connection, app.config['DYNAMO_TABLES'])

    @staticmethod
    def _init_settings(app):
        """Initialize all of the extension settings."""
        app.config.setdefault('DYNAMO_TABLES', [])
        app.config.setdefault('DYNAMO_ENABLE_LOCAL', environ.get('DYNAMO_ENABLE_LOCAL', False))
        app.config.setdefault('DYNAMO_LOCAL_HOST', environ.get('DYNAMO_LOCAL_HOST'))
        app.config.setdefault('DYNAMO_LOCAL_PORT', environ.get('DYNAMO_LOCAL_PORT'))
        app.config.setdefault('AWS_ACCESS_KEY_ID', environ.get('AWS_ACCESS_KEY_ID'))
        app.config.setdefault('AWS_SECRET_ACCESS_KEY', environ.get('AWS_SECRET_ACCESS_KEY'))
        app.config.setdefault('AWS_SESSION_TOKEN', environ.get('AWS_SESSION_TOKEN'))
        app.config.setdefault('AWS_REGION', environ.get('AWS_REGION', Dynamo.DEFAULT_REGION))

    @staticmethod
    def _check_settings(app):
        """
        Check all user-specified settings to ensure they're correct.

        We'll raise an error if something isn't configured properly.

        :raises: ConfigurationError
        """
        if app.config['AWS_ACCESS_KEY_ID'] and not app.config['AWS_SECRET_ACCESS_KEY']:
            raise ConfigurationError('You must specify AWS_SECRET_ACCESS_KEY if you are specifying AWS_ACCESS_KEY_ID.')

        if app.config['AWS_SECRET_ACCESS_KEY'] and not app.config['AWS_ACCESS_KEY_ID']:
            raise ConfigurationError('You must specify AWS_ACCESS_KEY_ID if you are specifying AWS_SECRET_ACCESS_KEY.')

        if app.config['DYNAMO_ENABLE_LOCAL'] and not (app.config['DYNAMO_LOCAL_HOST'] and app.config['DYNAMO_LOCAL_PORT']):
            raise ConfigurationError('If you have enabled Dynamo local, you must specify the host and port.')

    def _get_app(self):
        """
        Helper method that implements the logic to look up an application.
        pass
        """
        if current_app:
            return current_app

        if self.app is not None:
            return self.app

        raise RuntimeError(
            'application not registered on dynamo instance and no application'
            'bound to current context'
        )

    @staticmethod
    def _get_ctx(app):
        """
        Gets the dyanmo app context state.
        """

        try:
            return app.extensions['dynamo']
        except KeyError:
            raise RuntimeError(
                'flask-dynamo extension not registered on flask app'
            )

    @property
    def connection(self):
        """
        Our DynamoDB connection.

        This will be lazily created if this is the first time this is being
        accessed.  This connection is reused for performance.
        """
        app = self._get_app()
        ctx = self._get_ctx(app)
        try:
            return ctx._connection
        except AttributeError:
            session_kwargs = {}
            client_kwargs = {}
            local = True if app.config['DYNAMO_ENABLE_LOCAL'] else False
            if local:
                client_kwargs['endpoint_url'] = 'http://{}:{}'.format(
                    app.config['DYNAMO_LOCAL_HOST'],
                    app.config['DYNAMO_LOCAL_PORT'],
                )

            # Only apply if manually specified: otherwise, we'll let boto
            # figure it out (boto will sniff for ec2 instance profile
            # credentials).
            if app.config['AWS_ACCESS_KEY_ID']:
                session_kwargs['aws_access_key_id'] = app.config['AWS_ACCESS_KEY_ID']
            if app.config['AWS_SECRET_ACCESS_KEY']:
                session_kwargs['aws_secret_access_key'] = app.config['AWS_SECRET_ACCESS_KEY']
            if app.config.get('AWS_SESSION_TOKEN', None):
                session_kwargs['aws_session_token'] = app.config['AWS_SESSION_TOKEN']
            if app.config.get('AWS_REGION', None):
                session_kwargs['region_name'] = app.config['AWS_REGION']

            ctx._session = Session(**session_kwargs)
            ctx._connection = ctx._session.resource('dynamodb', **client_kwargs)

            return ctx._connection

    def get_table(self, table_name):
        return self.tables[table_name]

    def create_all(self, wait=False):
        """
        Create all user-specified DynamoDB tables.

        We'll ignore table(s) that already exists.
        We'll error out if the tables can't be created for some reason.
        """
        self.tables.create_all(wait=wait)

    def destroy_all(self, wait=False):
        """
        Destroy all user-specified DynamoDB tables.

        We'll error out if the tables can't be destroyed for some reason.
        """
        self.tables.destroy_all(wait=wait)
<commit_msg>Check if dynamobd table exists before trying to create it<commit_after>"""Main Flask integration."""

from os import environ

from boto3.session import Session
from flask import current_app

from .errors import ConfigurationError

class DynamoLazyTables(object):
    """Manages access to Dynamo Tables."""
    def __init__(self, connection, table_config):
        self._table_config = table_config
        self._connection = connection

    def __getitem__(self, name):
        """Get the connection for a table by name."""
        return self._connection.Table(name)

    def keys(self):
        """The table names in our config."""
        return [t['TableName'] for t in self._table_config]

    def len(self):
        """The number of tables we are configured for."""
        return len(self.keys())

    def items(self):
        """The table tuples (name, connection.Table())."""
        for table_name in self.keys():
            yield (table_name, self[table_name])

    def _wait(self, table_name, type_waiter):
        waiter = self._connection.meta.client.get_waiter(type_waiter)
        waiter.wait(TableName=table_name)

    def wait_exists(self, table_name):
        self._wait(table_name, 'table_exists')

    def wait_not_exists(self, table_name):
        self._wait(table_name, 'table_not_exists')

    def create_all(self, wait=False):
        tables_name_list = [table.name for table in self._connection.tables.all()]
        for table in self._table_config:
            if table['TableName'] not in tables_name_list:
                self._connection.create_table(**table)
        if wait:
            for table in self._table_config:
                self.wait_exists(table['TableName'])

    def destroy_all(self, wait=False):
        for table in self._table_config:
            table = self._connection.Table(table['TableName'])
            table.delete()
        if wait:
            for table in self._table_config:
                self.wait_not_exists(table['TableName'])

class Dynamo(object):
    """DynamoDB engine manager."""

    DEFAULT_REGION = 'us-east-1'

    def __init__(self, app=None):
        """
        Initialize this extension.

        :param obj app: The Flask application (optional).
        """
        self.app = app
        if app is not None:
            self.init_app(app)

    def init_app(self, app):
        """
        Initialize this extension.

        :param obj app: The Flask application.
        """

        self._init_settings(app)
        self._check_settings(app)

        app.extensions['dynamo'] = self

        self.tables = DynamoLazyTables(self.connection, app.config['DYNAMO_TABLES'])

    @staticmethod
    def _init_settings(app):
        """Initialize all of the extension settings."""
        app.config.setdefault('DYNAMO_TABLES', [])
        app.config.setdefault('DYNAMO_ENABLE_LOCAL', environ.get('DYNAMO_ENABLE_LOCAL', False))
        app.config.setdefault('DYNAMO_LOCAL_HOST', environ.get('DYNAMO_LOCAL_HOST'))
        app.config.setdefault('DYNAMO_LOCAL_PORT', environ.get('DYNAMO_LOCAL_PORT'))
        app.config.setdefault('AWS_ACCESS_KEY_ID', environ.get('AWS_ACCESS_KEY_ID'))
        app.config.setdefault('AWS_SECRET_ACCESS_KEY', environ.get('AWS_SECRET_ACCESS_KEY'))
        app.config.setdefault('AWS_SESSION_TOKEN', environ.get('AWS_SESSION_TOKEN'))
        app.config.setdefault('AWS_REGION', environ.get('AWS_REGION', Dynamo.DEFAULT_REGION))

    @staticmethod
    def _check_settings(app):
        """
        Check all user-specified settings to ensure they're correct.

        We'll raise an error if something isn't configured properly.

        :raises: ConfigurationError
        """
        if app.config['AWS_ACCESS_KEY_ID'] and not app.config['AWS_SECRET_ACCESS_KEY']:
            raise ConfigurationError('You must specify AWS_SECRET_ACCESS_KEY if you are specifying AWS_ACCESS_KEY_ID.')

        if app.config['AWS_SECRET_ACCESS_KEY'] and not app.config['AWS_ACCESS_KEY_ID']:
            raise ConfigurationError('You must specify AWS_ACCESS_KEY_ID if you are specifying AWS_SECRET_ACCESS_KEY.')

        if app.config['DYNAMO_ENABLE_LOCAL'] and not (app.config['DYNAMO_LOCAL_HOST'] and app.config['DYNAMO_LOCAL_PORT']):
            raise ConfigurationError('If you have enabled Dynamo local, you must specify the host and port.')

    def _get_app(self):
        """
        Helper method that implements the logic to look up an application.
        pass
        """
        if current_app:
            return current_app

        if self.app is not None:
            return self.app

        raise RuntimeError(
            'application not registered on dynamo instance and no application'
            'bound to current context'
        )

    @staticmethod
    def _get_ctx(app):
        """
        Gets the dyanmo app context state.
        """

        try:
            return app.extensions['dynamo']
        except KeyError:
            raise RuntimeError(
                'flask-dynamo extension not registered on flask app'
            )

    @property
    def connection(self):
        """
        Our DynamoDB connection.

        This will be lazily created if this is the first time this is being
        accessed.  This connection is reused for performance.
        """
        app = self._get_app()
        ctx = self._get_ctx(app)
        try:
            return ctx._connection
        except AttributeError:
            session_kwargs = {}
            client_kwargs = {}
            local = True if app.config['DYNAMO_ENABLE_LOCAL'] else False
            if local:
                client_kwargs['endpoint_url'] = 'http://{}:{}'.format(
                    app.config['DYNAMO_LOCAL_HOST'],
                    app.config['DYNAMO_LOCAL_PORT'],
                )

            # Only apply if manually specified: otherwise, we'll let boto
            # figure it out (boto will sniff for ec2 instance profile
            # credentials).
            if app.config['AWS_ACCESS_KEY_ID']:
                session_kwargs['aws_access_key_id'] = app.config['AWS_ACCESS_KEY_ID']
            if app.config['AWS_SECRET_ACCESS_KEY']:
                session_kwargs['aws_secret_access_key'] = app.config['AWS_SECRET_ACCESS_KEY']
            if app.config.get('AWS_SESSION_TOKEN', None):
                session_kwargs['aws_session_token'] = app.config['AWS_SESSION_TOKEN']
            if app.config.get('AWS_REGION', None):
                session_kwargs['region_name'] = app.config['AWS_REGION']

            ctx._session = Session(**session_kwargs)
            ctx._connection = ctx._session.resource('dynamodb', **client_kwargs)

            return ctx._connection

    def get_table(self, table_name):
        return self.tables[table_name]

    def create_all(self, wait=False):
        """
        Create all user-specified DynamoDB tables.

        We'll ignore table(s) that already exists.
        We'll error out if the tables can't be created for some reason.
        """
        self.tables.create_all(wait=wait)

    def destroy_all(self, wait=False):
        """
        Destroy all user-specified DynamoDB tables.

        We'll error out if the tables can't be destroyed for some reason.
        """
        self.tables.destroy_all(wait=wait)
<|endoftext|>


<commit_before>import os
import tempfile
import unittest
import uuid

from git import Repo
from mock import MagicMock, PropertyMock, patch
from skeletor.template import Template
from shutil import rmtree
from StringIO import StringIO

class TemplateTests(unittest.TestCase):
    """ Template Tests """

    def setUp(self):
        # Mock out the config class
        self.config = MagicMock(name='config')
        self.config.project_name = uuid.uuid4().hex  # Random project name
        self.config.django_secret_key = 'xxx'
        self.config.template_settings_dir = 'settings'
        self.config.cli_opts.error = MagicMock(side_effect=Exception)
        self.config.template = os.path.join(os.path.dirname(
            os.path.realpath(__file__)), 'test_template')

    def should_handle_malformed_variables_gracefully(self):
        self.config.variables = 'this,is.wrong'
        t = Template(self.config)

        self.assertEquals(len(t.place_holders), 3)

    def ensure_custom_variables_added_to_placeholders(self):
        self.config.variables = 'foo=bar,baz=1'
        t = Template(self.config)

        self.assertTrue('foo' in t.place_holders)
        self.assertEquals(t.place_holders['foo'], 'bar')
        self.assertTrue('baz' in t.place_holders)
        self.assertEquals(t.place_holders['baz'], '1')

    @patch('skeletor.template.Template.working_dir', new_callable=PropertyMock)
    def ensure_dir_cannot_be_created_if_already_exists(self, mock_working_dir):
        mock_working_dir.return_value = tempfile.gettempdir()
        tmp_dir = tempfile.mkdtemp(suffix=self.config.project_name, prefix='')
        tmp_dir_name = list(os.path.split(tmp_dir))[-1:][0]
        self.config.project_name = tmp_dir_name
        try:
            t = Template(self.config)
            t.copy_template()
            rmtree(tmp_dir)
        except Exception:
            assert True
        else:
            assert False

        self.config.cli_opts.error.assert_called_with('%s already exists' % (
            tmp_dir))

    @patch('os.mkdir', return_value=True)
    def ensure_exception_if_directory_creation_fails(self, mock_os_mkdir):
        try:
            t = Template(self.config)
            t.copy_template()
        except Exception:
            assert True
        else:
            assert False

        self.config.cli_opts.error.assert_called_with(
            'Error creating project directory')
        mock_os_mkdir.assert_called_with(os.path.join(
            t.working_dir, self.config.project_name))

    @patch('sys.stdout', new_callable=StringIO)
    @patch('tempfile.mkdtemp', return_value=True)
    @patch('skeletor.Template.git_clone', return_value=True)
    def should_detect_git_repo(self, mock_git_clone, mock_tempfile,
                               mock_stdout):
        t = Template(self.config)
        assert not t.is_git
        self.config.template = 'git+git@somewhere.com:repo.git'
        t = Template(self.config)
        self.assertEquals('Using git to clone template from '
                          'git@somewhere.com:repo.git\n',
                          mock_stdout.getvalue())
        assert t.is_git

    @patch('skeletor.template.Template.working_dir', new_callable=PropertyMock)
    def should_clone_git_repo(self, mock_working_dir):

        # Create a fake temp repo w/ commit
        git_dir = tempfile.mkdtemp(prefix='git')
        git_repo = Repo.init(git_dir)
        try:
            f = open(os.path.join(git_dir, 'foo.txt'), 'w')
            f.write('blah')
        except IOError:
            assert False
        finally:
            f.close()
        cwd = os.getcwd()
        os.chdir(git_dir)
        git_repo.index.add(['foo.txt'])
        git_repo.index.commit("Added foo.txt")
        os.chdir(cwd)

        # Fake Template Path
        mock_working_dir.return_value = tempfile.gettempdir()

        # Now attempt to clone but patch for Exception throw
        self.config.template = 'git+' + git_dir
        t = Template(self.config)
        t.copy_template()

        # Clean up
        rmtree(t.project_root)

        self.assertFalse(os.path.isdir(t.config.template))
        self.assertFalse(self.config.cli_opts.error.called)

    @patch('git.Repo.init', side_effect=Exception)
    def ensure_error_on_clone_exception(self, mock_repo_init):
        self.config.template = 'git+this/wont/work'
        try:
            Template(self.config)
        except:
            self.config.cli_opts.error.assert_called_with(
                'Error cloning repository')
            assert True
        else:
            assert False

    @patch('os.path.isdir', return_value=False)
    @patch('skeletor.template.Template.working_dir', new_callable=PropertyMock)
    def ensure_copy_template_failes_if_dir_does_not_exist(
            self, mock_working_dir, mock_isdir):
        mock_working_dir.return_value = tempfile.gettempdir()
        tmp_dir = tempfile.mkdtemp(suffix=self.config.project_name, prefix='')
        tmp_dir_name = list(os.path.split(tmp_dir))[-1:][0]
        self.config.project_name = tmp_dir_name
        try:
            t = Template(self.config)
            t.copy_template()
        except:
            self.config.cli_opts.error.assert_called_with(
                'Unable to copy template, directory does not exist')
            assert True
        else:
            assert False
<commit_msg>test to ensure eclude dirs are ignored in copy<commit_after>import os
import tempfile
import unittest
import uuid

from git import Repo
from mock import MagicMock, PropertyMock, patch
from skeletor.template import Template
from shutil import rmtree
from StringIO import StringIO

class TemplateTests(unittest.TestCase):
    """ Template Tests """

    def setUp(self):
        # Mock out the config class
        self.config = MagicMock(name='config')
        self.config.project_name = uuid.uuid4().hex  # Random project name
        self.config.django_secret_key = 'xxx'
        self.config.template_settings_dir = 'settings'
        self.config.cli_opts.error = MagicMock(side_effect=Exception)
        self.config.template = os.path.join(os.path.dirname(
            os.path.realpath(__file__)), 'test_template')

    def should_handle_malformed_variables_gracefully(self):
        self.config.variables = 'this,is.wrong'
        t = Template(self.config)

        self.assertEquals(len(t.place_holders), 3)

    def ensure_custom_variables_added_to_placeholders(self):
        self.config.variables = 'foo=bar,baz=1'
        t = Template(self.config)

        self.assertTrue('foo' in t.place_holders)
        self.assertEquals(t.place_holders['foo'], 'bar')
        self.assertTrue('baz' in t.place_holders)
        self.assertEquals(t.place_holders['baz'], '1')

    @patch('skeletor.template.Template.working_dir', new_callable=PropertyMock)
    def ensure_dir_cannot_be_created_if_already_exists(self, mock_working_dir):
        mock_working_dir.return_value = tempfile.gettempdir()
        tmp_dir = tempfile.mkdtemp(suffix=self.config.project_name, prefix='')
        tmp_dir_name = list(os.path.split(tmp_dir))[-1:][0]
        self.config.project_name = tmp_dir_name
        try:
            t = Template(self.config)
            t.copy_template()
            rmtree(tmp_dir)
        except Exception:
            assert True
        else:
            assert False

        self.config.cli_opts.error.assert_called_with('%s already exists' % (
            tmp_dir))

    @patch('os.mkdir', return_value=True)
    def ensure_exception_if_directory_creation_fails(self, mock_os_mkdir):
        try:
            t = Template(self.config)
            t.copy_template()
        except Exception:
            assert True
        else:
            assert False

        self.config.cli_opts.error.assert_called_with(
            'Error creating project directory')
        mock_os_mkdir.assert_called_with(os.path.join(
            t.working_dir, self.config.project_name))

    @patch('sys.stdout', new_callable=StringIO)
    @patch('tempfile.mkdtemp', return_value=True)
    @patch('skeletor.Template.git_clone', return_value=True)
    def should_detect_git_repo(self, mock_git_clone, mock_tempfile,
                               mock_stdout):
        t = Template(self.config)
        assert not t.is_git
        self.config.template = 'git+git@somewhere.com:repo.git'
        t = Template(self.config)
        self.assertEquals('Using git to clone template from '
                          'git@somewhere.com:repo.git\n',
                          mock_stdout.getvalue())
        assert t.is_git

    @patch('skeletor.template.Template.working_dir', new_callable=PropertyMock)
    def should_clone_git_repo(self, mock_working_dir):

        # Create a fake temp repo w/ commit
        git_dir = tempfile.mkdtemp(prefix='git')
        git_repo = Repo.init(git_dir)
        try:
            f = open(os.path.join(git_dir, 'foo.txt'), 'w')
            f.write('blah')
        except IOError:
            assert False
        finally:
            f.close()
        cwd = os.getcwd()
        os.chdir(git_dir)
        git_repo.index.add(['foo.txt'])
        git_repo.index.commit("Added foo.txt")
        os.chdir(cwd)

        # Fake Template Path
        mock_working_dir.return_value = tempfile.gettempdir()

        # Now attempt to clone but patch for Exception throw
        self.config.template = 'git+' + git_dir
        t = Template(self.config)
        t.copy_template()

        # Clean up
        rmtree(t.project_root)

        self.assertFalse(os.path.isdir(t.config.template))
        self.assertFalse(self.config.cli_opts.error.called)

    @patch('git.Repo.init', side_effect=Exception)
    def ensure_error_on_clone_exception(self, mock_repo_init):
        self.config.template = 'git+this/wont/work'
        try:
            Template(self.config)
        except:
            self.config.cli_opts.error.assert_called_with(
                'Error cloning repository')
            assert True
        else:
            assert False

    @patch('os.path.isdir', return_value=False)
    @patch('skeletor.template.Template.working_dir', new_callable=PropertyMock)
    def ensure_copy_template_failes_if_dir_does_not_exist(
            self, mock_working_dir, mock_isdir):
        mock_working_dir.return_value = tempfile.gettempdir()
        tmp_dir = tempfile.mkdtemp(suffix=self.config.project_name, prefix='')
        tmp_dir_name = list(os.path.split(tmp_dir))[-1:][0]
        self.config.project_name = tmp_dir_name
        try:
            t = Template(self.config)
            t.copy_template()
        except:
            self.config.cli_opts.error.assert_called_with(
                'Unable to copy template, directory does not exist')
            assert True
        else:
            assert False

    @patch('skeletor.template.Template.working_dir', new_callable=PropertyMock)
    def ensure_excluded_dirs_are_not_copied(self, mock_working_dir):
        mock_working_dir.return_value = tempfile.gettempdir()
        t = Template(self.config)
        t.exclude_dirs.append('.exclude_this')
        t.copy_template()
        self.assertFalse(os.path.isdir(os.path.join(t.project_root,
                                                    '.exclude_this')))
        rmtree(t.project_root)
<|endoftext|>