<commit_before># Disassembler

import sys
import string

def dis():
	tb = sys.last_traceback
	while tb.tb_next: tb = tb.tb_next
	distb(tb)

def distb(tb):
	disassemble(tb.tb_frame.f_code, tb.tb_lasti)

def disco(co):
	disassemble(co, -1)

def disassemble(co, lasti):
	code = co.co_code
	labels = findlabels(code)
	n = len(code)
	i = 0
	while i < n:
		c = code[i]
		op = ord(c)
		if op == SET_LINENO and i > 0: print # Extra blank line
		if i == lasti: print '-->',
		else: print '   ',
		if i in labels: print '>>',
		else: print '  ',
		print string.rjust(`i`, 4),
		print string.ljust(opname[op], 15),
		i = i+1
		if op >= HAVE_ARGUMENT:
			oparg = ord(code[i]) + ord(code[i+1])*256
			i = i+2
			print string.rjust(`oparg`, 5),
			if op in hasconst:
				print '(' + `co.co_consts[oparg]` + ')',
			elif op in hasname:
				print '(' + co.co_names[oparg] + ')',
			elif op in hasjrel:
				print '(to ' + `i + oparg` + ')',
			elif op in haslocal:
				print '(' + co.co_varnames[oparg] + ')',
			elif op in hascompare:
				print '(' + cmp_op[oparg] + ')',
		print

def findlabels(code):
	labels = []
	n = len(code)
	i = 0
	while i < n:
		c = code[i]
		op = ord(c)
		i = i+1
		if op >= HAVE_ARGUMENT:
			oparg = ord(code[i]) + ord(code[i+1])*256
			i = i+2
			label = -1
			if op in hasjrel:
				label = i+oparg
			elif op in hasjabs:
				label = oparg
			if label >= 0:
				if label not in labels:
					labels.append(label)
	return labels

cmp_op = ('<', '<=', '==', '!=', '>', '>=', 'in', 'not in', 'is',
        'is not', 'exception match', 'BAD')

hasconst = []
hasname = []
hasjrel = []
hasjabs = []
haslocal = []
hascompare = []

opname = [''] * 256
for op in range(256): opname[op] = '<' + `op` + '>'

def def_op(name, op):
	opname[op] = name

def name_op(name, op):
	opname[op] = name
	hasname.append(op)

def jrel_op(name, op):
	opname[op] = name
	hasjrel.append(op)

def jabs_op(name, op):
	opname[op] = name
	hasjabs.append(op)

# Instruction opcodes for compiled code

def_op('STOP_CODE', 0)
def_op('POP_TOP', 1)
def_op('ROT_TWO', 2)
def_op('ROT_THREE', 3)
def_op('DUP_TOP', 4)

def_op('UNARY_POSITIVE', 10)
def_op('UNARY_NEGATIVE', 11)
def_op('UNARY_NOT', 12)
def_op('UNARY_CONVERT', 13)
def_op('UNARY_CALL', 14)
def_op('UNARY_INVERT', 15)

def_op('BINARY_POWER', 19)
def_op('BINARY_MULTIPLY', 20)
def_op('BINARY_DIVIDE', 21)
def_op('BINARY_MODULO', 22)
def_op('BINARY_ADD', 23)
def_op('BINARY_SUBTRACT', 24)
def_op('BINARY_SUBSCR', 25)
def_op('BINARY_CALL', 26)

def_op('SLICE+0', 30)
def_op('SLICE+1', 31)
def_op('SLICE+2', 32)
def_op('SLICE+3', 33)

def_op('STORE_SLICE+0', 40)
def_op('STORE_SLICE+1', 41)
def_op('STORE_SLICE+2', 42)
def_op('STORE_SLICE+3', 43)

def_op('DELETE_SLICE+0', 50)
def_op('DELETE_SLICE+1', 51)
def_op('DELETE_SLICE+2', 52)
def_op('DELETE_SLICE+3', 53)

def_op('STORE_SUBSCR', 60)
def_op('DELETE_SUBSCR', 61)
def_op('BINARY_LSHIFT', 62)
def_op('BINARY_RSHIFT', 63)
def_op('BINARY_AND', 64)
def_op('BINARY_XOR', 65)
def_op('BINARY_OR', 66)

def_op('PRINT_EXPR', 70)
def_op('PRINT_ITEM', 71)
def_op('PRINT_NEWLINE', 72)

def_op('BREAK_LOOP', 80)
def_op('RAISE_EXCEPTION', 81)
def_op('LOAD_LOCALS', 82)
def_op('RETURN_VALUE', 83)
def_op('EXEC_STMT', 85)
def_op('BUILD_FUNCTION', 86)
def_op('POP_BLOCK', 87)
def_op('END_FINALLY', 88)
def_op('BUILD_CLASS', 89)

HAVE_ARGUMENT = 90		# Opcodes from here have an argument:

name_op('STORE_NAME', 90)	# Index in name list
name_op('DELETE_NAME', 91)	# ""
def_op('UNPACK_TUPLE', 92)	# Number of tuple items
def_op('UNPACK_LIST', 93)	# Number of list items
def_op('UNPACK_ARG', 94)	# Number of arguments expected
name_op('STORE_ATTR', 95)	# Index in name list
name_op('DELETE_ATTR', 96)	# ""
name_op('STORE_GLOBAL', 97)	# ""
name_op('DELETE_GLOBAL', 98)	# ""
name_op('UNPACK_VARARG', 99)	# Minimal number of arguments

def_op('LOAD_CONST', 100)	# Index in const list
hasconst.append(100)
name_op('LOAD_NAME', 101)	# Index in name list
hascompare.append(106)
def_op('BUILD_TUPLE', 102)	# Number of tuple items
def_op('BUILD_LIST', 103)	# Number of list items
def_op('BUILD_MAP', 104)	# Always zero for now
name_op('LOAD_ATTR', 105)	# Index in name list
def_op('COMPARE_OP', 106)	# Comparison operator
name_op('IMPORT_NAME', 107)	# Index in name list
name_op('IMPORT_FROM', 108)	# Index in name list

jrel_op('JUMP_FORWARD', 110)	# Number of bytes to skip
jrel_op('JUMP_IF_FALSE', 111)	# ""
jrel_op('JUMP_IF_TRUE', 112)	# ""
jabs_op('JUMP_ABSOLUTE', 113)	# Target byte offset from beginning of code
jrel_op('FOR_LOOP', 114)	# Number of bytes to skip

name_op('LOAD_LOCAL', 115)	# Index in name list
name_op('LOAD_GLOBAL', 116)	# Index in name list
def_op('SET_FUNC_ARGS', 117)	# Argcount

jrel_op('SETUP_LOOP', 120)	# Distance to target address
jrel_op('SETUP_EXCEPT', 121)	# ""
jrel_op('SETUP_FINALLY', 122)	# ""

def_op('LOAD_FAST', 124)	# Local variable number
haslocal.append(124)
def_op('STORE_FAST', 125)	# Local variable number
haslocal.append(125)
def_op('DELETE_FAST', 126)	# Local variable number
haslocal.append(126)

def_op('SET_LINENO', 127)	# Current line number
SET_LINENO = 127

def_op('RAISE_VARARGS', 130)
def_op('CALL_FUNCTION', 131)
def_op('MAKE_FUNCTION', 132)
def_op('BUILD_SLICE', 133)
<commit_msg>More user friedly interface:<commit_after># Disassembler

import sys
import string

def dis(x=None):
	if not x:
		distb()
	else:
		if hasattr(x, 'im_func'):
			x = x.im_func
		if hasattr(x, 'func_code'):
			x = x.func_code
		if hasattr(x, 'co_code'):
			disassemble(x)
		else:
			raise ValueError, \
			      "don't know how to disassemble %s objects" % \
			      type(x).__name__

def distb(tb=None):
	if not tb:
		try:
			tb = sys.last_traceback
		except AttributeError:
			raise RuntimeError, "no last traceback to disassemble"
		while tb.tb_next: tb = tb.tb_next
	disassemble(tb.tb_frame.f_code, tb.tb_lasti)

def disassemble(co, lasti=-1):
	code = co.co_code
	labels = findlabels(code)
	n = len(code)
	i = 0
	while i < n:
		c = code[i]
		op = ord(c)
		if op == SET_LINENO and i > 0: print # Extra blank line
		if i == lasti: print '-->',
		else: print '   ',
		if i in labels: print '>>',
		else: print '  ',
		print string.rjust(`i`, 4),
		print string.ljust(opname[op], 15),
		i = i+1
		if op >= HAVE_ARGUMENT:
			oparg = ord(code[i]) + ord(code[i+1])*256
			i = i+2
			print string.rjust(`oparg`, 5),
			if op in hasconst:
				print '(' + `co.co_consts[oparg]` + ')',
			elif op in hasname:
				print '(' + co.co_names[oparg] + ')',
			elif op in hasjrel:
				print '(to ' + `i + oparg` + ')',
			elif op in haslocal:
				print '(' + co.co_varnames[oparg] + ')',
			elif op in hascompare:
				print '(' + cmp_op[oparg] + ')',
		print

disco = disassemble

def findlabels(code):
	labels = []
	n = len(code)
	i = 0
	while i < n:
		c = code[i]
		op = ord(c)
		i = i+1
		if op >= HAVE_ARGUMENT:
			oparg = ord(code[i]) + ord(code[i+1])*256
			i = i+2
			label = -1
			if op in hasjrel:
				label = i+oparg
			elif op in hasjabs:
				label = oparg
			if label >= 0:
				if label not in labels:
					labels.append(label)
	return labels

cmp_op = ('<', '<=', '==', '!=', '>', '>=', 'in', 'not in', 'is',
        'is not', 'exception match', 'BAD')

hasconst = []
hasname = []
hasjrel = []
hasjabs = []
haslocal = []
hascompare = []

opname = [''] * 256
for op in range(256): opname[op] = '<' + `op` + '>'

def def_op(name, op):
	opname[op] = name

def name_op(name, op):
	opname[op] = name
	hasname.append(op)

def jrel_op(name, op):
	opname[op] = name
	hasjrel.append(op)

def jabs_op(name, op):
	opname[op] = name
	hasjabs.append(op)

# Instruction opcodes for compiled code

def_op('STOP_CODE', 0)
def_op('POP_TOP', 1)
def_op('ROT_TWO', 2)
def_op('ROT_THREE', 3)
def_op('DUP_TOP', 4)

def_op('UNARY_POSITIVE', 10)
def_op('UNARY_NEGATIVE', 11)
def_op('UNARY_NOT', 12)
def_op('UNARY_CONVERT', 13)
def_op('UNARY_CALL', 14)
def_op('UNARY_INVERT', 15)

def_op('BINARY_POWER', 19)
def_op('BINARY_MULTIPLY', 20)
def_op('BINARY_DIVIDE', 21)
def_op('BINARY_MODULO', 22)
def_op('BINARY_ADD', 23)
def_op('BINARY_SUBTRACT', 24)
def_op('BINARY_SUBSCR', 25)
def_op('BINARY_CALL', 26)

def_op('SLICE+0', 30)
def_op('SLICE+1', 31)
def_op('SLICE+2', 32)
def_op('SLICE+3', 33)

def_op('STORE_SLICE+0', 40)
def_op('STORE_SLICE+1', 41)
def_op('STORE_SLICE+2', 42)
def_op('STORE_SLICE+3', 43)

def_op('DELETE_SLICE+0', 50)
def_op('DELETE_SLICE+1', 51)
def_op('DELETE_SLICE+2', 52)
def_op('DELETE_SLICE+3', 53)

def_op('STORE_SUBSCR', 60)
def_op('DELETE_SUBSCR', 61)
def_op('BINARY_LSHIFT', 62)
def_op('BINARY_RSHIFT', 63)
def_op('BINARY_AND', 64)
def_op('BINARY_XOR', 65)
def_op('BINARY_OR', 66)

def_op('PRINT_EXPR', 70)
def_op('PRINT_ITEM', 71)
def_op('PRINT_NEWLINE', 72)

def_op('BREAK_LOOP', 80)
def_op('RAISE_EXCEPTION', 81)
def_op('LOAD_LOCALS', 82)
def_op('RETURN_VALUE', 83)
def_op('EXEC_STMT', 85)
def_op('BUILD_FUNCTION', 86)
def_op('POP_BLOCK', 87)
def_op('END_FINALLY', 88)
def_op('BUILD_CLASS', 89)

HAVE_ARGUMENT = 90		# Opcodes from here have an argument:

name_op('STORE_NAME', 90)	# Index in name list
name_op('DELETE_NAME', 91)	# ""
def_op('UNPACK_TUPLE', 92)	# Number of tuple items
def_op('UNPACK_LIST', 93)	# Number of list items
def_op('UNPACK_ARG', 94)	# Number of arguments expected
name_op('STORE_ATTR', 95)	# Index in name list
name_op('DELETE_ATTR', 96)	# ""
name_op('STORE_GLOBAL', 97)	# ""
name_op('DELETE_GLOBAL', 98)	# ""
name_op('UNPACK_VARARG', 99)	# Minimal number of arguments

def_op('LOAD_CONST', 100)	# Index in const list
hasconst.append(100)
name_op('LOAD_NAME', 101)	# Index in name list
hascompare.append(106)
def_op('BUILD_TUPLE', 102)	# Number of tuple items
def_op('BUILD_LIST', 103)	# Number of list items
def_op('BUILD_MAP', 104)	# Always zero for now
name_op('LOAD_ATTR', 105)	# Index in name list
def_op('COMPARE_OP', 106)	# Comparison operator
name_op('IMPORT_NAME', 107)	# Index in name list
name_op('IMPORT_FROM', 108)	# Index in name list

jrel_op('JUMP_FORWARD', 110)	# Number of bytes to skip
jrel_op('JUMP_IF_FALSE', 111)	# ""
jrel_op('JUMP_IF_TRUE', 112)	# ""
jabs_op('JUMP_ABSOLUTE', 113)	# Target byte offset from beginning of code
jrel_op('FOR_LOOP', 114)	# Number of bytes to skip

name_op('LOAD_LOCAL', 115)	# Index in name list
name_op('LOAD_GLOBAL', 116)	# Index in name list
def_op('SET_FUNC_ARGS', 117)	# Argcount

jrel_op('SETUP_LOOP', 120)	# Distance to target address
jrel_op('SETUP_EXCEPT', 121)	# ""
jrel_op('SETUP_FINALLY', 122)	# ""

def_op('LOAD_FAST', 124)	# Local variable number
haslocal.append(124)
def_op('STORE_FAST', 125)	# Local variable number
haslocal.append(125)
def_op('DELETE_FAST', 126)	# Local variable number
haslocal.append(126)

def_op('SET_LINENO', 127)	# Current line number
SET_LINENO = 127

def_op('RAISE_VARARGS', 130)
def_op('CALL_FUNCTION', 131)
def_op('MAKE_FUNCTION', 132)
def_op('BUILD_SLICE', 133)
<|endoftext|>


<commit_before>from django.db.models import Manager
from django.db.models.query import RawQuerySet
from kolibri.content.models import AssessmentMetaData, ChannelMetadataCache, ContentNode, File
from rest_framework import serializers

from .content_db_router import default_database_is_attached, get_active_content_database

class ChannelMetadataCacheSerializer(serializers.ModelSerializer):

    class Meta:
        model = ChannelMetadataCache
        fields = ('root_pk', 'id', 'name', 'description', 'author', 'last_updated')

class FileSerializer(serializers.ModelSerializer):
    storage_url = serializers.SerializerMethodField()
    preset = serializers.SerializerMethodField()
    download_url = serializers.SerializerMethodField()

    def get_storage_url(self, target_node):
        return target_node.get_storage_url()

    def get_preset(self, target_node):
        return target_node.get_preset()

    def get_download_url(self, target_node):
        return target_node.get_download_url()

    class Meta:
        model = File
        fields = ('storage_url', 'id', 'priority', 'checksum', 'available', 'file_size', 'extension', 'preset', 'lang',
                  'supplementary', 'thumbnail', 'download_url')

class AssessmentMetaDataSerializer(serializers.ModelSerializer):

    assessment_item_ids = serializers.JSONField(default='[]')
    mastery_model = serializers.JSONField(default='{}')

    class Meta:
        model = AssessmentMetaData
        fields = ('assessment_item_ids', 'number_of_assessments', 'mastery_model', 'randomize', 'is_manipulable', )

def get_progress_fraction(content_id, user):
    from kolibri.logger.models import ContentSummaryLog
    try:
        # add up all the progress for the logs, and divide by the total number of content nodes to get overall progress
        overall_progress = ContentSummaryLog.objects.get(user=user, content_id=content_id).progress
    except ContentSummaryLog.DoesNotExist:
        return None
    return round(overall_progress, 4)

def get_progress_fractions(nodes, user):
    from kolibri.logger.models import ContentSummaryLog
    if isinstance(nodes, RawQuerySet) or isinstance(nodes, list):
        leaf_ids = [datum.content_id for datum in nodes]
    else:
        leaf_ids = nodes.values_list("content_id", flat=True)

    # get all summary logs for the current user that correspond to the descendant content nodes
    if default_database_is_attached():  # if possible, do a direct join between the content and default databases
        channel_alias = get_active_content_database()
        summary_logs = ContentSummaryLog.objects.using(channel_alias).filter(user=user, content_id__in=leaf_ids)
    else:  # otherwise, convert the leaf queryset into a flat list of ids and use that
        summary_logs = ContentSummaryLog.objects.filter(user=user, content_id__in=list(leaf_ids))

    # make a lookup dict for all logs to allow mapping from content_id to current progress
    overall_progress = {log['content_id']: round(log['progress'], 4) for log in summary_logs.values('content_id', 'progress')}
    return overall_progress

class ContentNodeListSerializer(serializers.ListSerializer):

    def to_representation(self, data):

        if not data:
            return data

        if 'request' not in self.context or not self.context['request'].user.is_facility_user:
            progress_dict = {}
        else:
            user = self.context["request"].user
            progress_dict = get_progress_fractions(data, user)

        # Dealing with nested relationships, data can be a Manager,
        # so, first get a queryset from the Manager if needed
        iterable = data.all() if isinstance(data, Manager) else data

        return [
            self.child.to_representation(item, progress_dict.get(item.content_id)) for item in iterable
        ]

class ContentNodeSerializer(serializers.ModelSerializer):
    parent = serializers.PrimaryKeyRelatedField(read_only=True)
    files = FileSerializer(many=True, read_only=True)
    assessmentmetadata = AssessmentMetaDataSerializer(read_only=True, allow_null=True, many=True)
    license = serializers.StringRelatedField(many=False)
    license_description = serializers.SerializerMethodField()

    def __init__(self, *args, **kwargs):
        # Instantiate the superclass normally
        super(ContentNodeSerializer, self).__init__(*args, **kwargs)

        # enable dynamic fields specification!
        if 'request' in self.context and self.context['request'].GET.get('fields', None):
            fields = self.context['request'].GET['fields'].split(',')
            # Drop any fields that are not specified in the `fields` argument.
            allowed = set(fields)
            existing = set(self.fields.keys())
            for field_name in existing - allowed:
                self.fields.pop(field_name)

    def to_representation(self, instance, progress_fraction=None):
        if progress_fraction is None:
            if 'request' not in self.context or not self.context['request'].user.is_facility_user:
                progress_fraction = 0
            else:
                user = self.context["request"].user
                progress_fraction = get_progress_fraction(instance.content_id, user)
        value = super(ContentNodeSerializer, self).to_representation(instance)
        value['progress_fraction'] = progress_fraction
        return value

    def get_license_description(self, target_node):
        if target_node.license_id:
            return target_node.license.license_description
        return ''

    class Meta:
        model = ContentNode
        fields = (
            'pk', 'content_id', 'title', 'description', 'kind', 'available', 'sort_order', 'license_owner',
            'license', 'license_description', 'files', 'parent', 'author',
            'assessmentmetadata',
        )

        list_serializer_class = ContentNodeListSerializer
<commit_msg>Pass zero if no progress to prevent additional query.<commit_after>from django.db.models import Manager
from django.db.models.query import RawQuerySet
from kolibri.content.models import AssessmentMetaData, ChannelMetadataCache, ContentNode, File
from rest_framework import serializers

from .content_db_router import default_database_is_attached, get_active_content_database

class ChannelMetadataCacheSerializer(serializers.ModelSerializer):

    class Meta:
        model = ChannelMetadataCache
        fields = ('root_pk', 'id', 'name', 'description', 'author', 'last_updated')

class FileSerializer(serializers.ModelSerializer):
    storage_url = serializers.SerializerMethodField()
    preset = serializers.SerializerMethodField()
    download_url = serializers.SerializerMethodField()

    def get_storage_url(self, target_node):
        return target_node.get_storage_url()

    def get_preset(self, target_node):
        return target_node.get_preset()

    def get_download_url(self, target_node):
        return target_node.get_download_url()

    class Meta:
        model = File
        fields = ('storage_url', 'id', 'priority', 'checksum', 'available', 'file_size', 'extension', 'preset', 'lang',
                  'supplementary', 'thumbnail', 'download_url')

class AssessmentMetaDataSerializer(serializers.ModelSerializer):

    assessment_item_ids = serializers.JSONField(default='[]')
    mastery_model = serializers.JSONField(default='{}')

    class Meta:
        model = AssessmentMetaData
        fields = ('assessment_item_ids', 'number_of_assessments', 'mastery_model', 'randomize', 'is_manipulable', )

def get_progress_fraction(content_id, user):
    from kolibri.logger.models import ContentSummaryLog
    try:
        # add up all the progress for the logs, and divide by the total number of content nodes to get overall progress
        overall_progress = ContentSummaryLog.objects.get(user=user, content_id=content_id).progress
    except ContentSummaryLog.DoesNotExist:
        return None
    return round(overall_progress, 4)

def get_progress_fractions(nodes, user):
    from kolibri.logger.models import ContentSummaryLog
    if isinstance(nodes, RawQuerySet) or isinstance(nodes, list):
        leaf_ids = [datum.content_id for datum in nodes]
    else:
        leaf_ids = nodes.values_list("content_id", flat=True)

    # get all summary logs for the current user that correspond to the descendant content nodes
    if default_database_is_attached():  # if possible, do a direct join between the content and default databases
        channel_alias = get_active_content_database()
        summary_logs = ContentSummaryLog.objects.using(channel_alias).filter(user=user, content_id__in=leaf_ids)
    else:  # otherwise, convert the leaf queryset into a flat list of ids and use that
        summary_logs = ContentSummaryLog.objects.filter(user=user, content_id__in=list(leaf_ids))

    # make a lookup dict for all logs to allow mapping from content_id to current progress
    overall_progress = {log['content_id']: round(log['progress'], 4) for log in summary_logs.values('content_id', 'progress')}
    return overall_progress

class ContentNodeListSerializer(serializers.ListSerializer):

    def to_representation(self, data):

        if not data:
            return data

        if 'request' not in self.context or not self.context['request'].user.is_facility_user:
            progress_dict = {}
        else:
            user = self.context["request"].user
            progress_dict = get_progress_fractions(data, user)

        # Dealing with nested relationships, data can be a Manager,
        # so, first get a queryset from the Manager if needed
        iterable = data.all() if isinstance(data, Manager) else data

        return [
            self.child.to_representation(item, progress_fraction=progress_dict.get(item.content_id, 0)) for item in iterable
        ]

class ContentNodeSerializer(serializers.ModelSerializer):
    parent = serializers.PrimaryKeyRelatedField(read_only=True)
    files = FileSerializer(many=True, read_only=True)
    assessmentmetadata = AssessmentMetaDataSerializer(read_only=True, allow_null=True, many=True)
    license = serializers.StringRelatedField(many=False)
    license_description = serializers.SerializerMethodField()

    def __init__(self, *args, **kwargs):
        # Instantiate the superclass normally
        super(ContentNodeSerializer, self).__init__(*args, **kwargs)

        # enable dynamic fields specification!
        if 'request' in self.context and self.context['request'].GET.get('fields', None):
            fields = self.context['request'].GET['fields'].split(',')
            # Drop any fields that are not specified in the `fields` argument.
            allowed = set(fields)
            existing = set(self.fields.keys())
            for field_name in existing - allowed:
                self.fields.pop(field_name)

    def to_representation(self, instance, progress_fraction=None):
        if progress_fraction is None:
            if 'request' not in self.context or not self.context['request'].user.is_facility_user:
                progress_fraction = 0
            else:
                user = self.context["request"].user
                progress_fraction = get_progress_fraction(instance.content_id, user)
        value = super(ContentNodeSerializer, self).to_representation(instance)
        value['progress_fraction'] = progress_fraction
        return value

    def get_license_description(self, target_node):
        if target_node.license_id:
            return target_node.license.license_description
        return ''

    class Meta:
        model = ContentNode
        fields = (
            'pk', 'content_id', 'title', 'description', 'kind', 'available', 'sort_order', 'license_owner',
            'license', 'license_description', 'files', 'parent', 'author',
            'assessmentmetadata',
        )

        list_serializer_class = ContentNodeListSerializer
<|endoftext|>


<commit_before>#
#  Copyright (c) 2009-2010 Gintautas Miliauskas
#
#   Permission is hereby granted, free of charge, to any person
#   obtaining a copy of this software and associated documentation
#   files (the "Software"), to deal in the Software without
#   restriction, including without limitation the rights to use,
#   copy, modify, merge, publish, distribute, sublicense, and/or sell
#   copies of the Software, and to permit persons to whom the
#   Software is furnished to do so, subject to the following
#   conditions:
#
#   The above copyright notice and this permission notice shall be
#   included in all copies or substantial portions of the Software.
#
#   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
#   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
#   OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
#   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
#   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
#   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
#   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
#   OTHER DEALINGS IN THE SOFTWARE.

"""
Pickle field implementation for Django.

Modified for Evennia by Griatch and the Evennia community.

"""
from builtins import object
from ast import literal_eval

from copy import deepcopy
from base64 import b64encode, b64decode
from zlib import compress, decompress
# import six # this is actually a pypy component, not in default syslib
from django.core.exceptions import ValidationError
from django.db import models

from django.forms import CharField, Textarea
from django.forms.utils import flatatt
from django.utils.html import format_html

from pickle import loads, dumps
from django.utils.encoding import force_text

DEFAULT_PROTOCOL = 4

class PickledObject(str):
    """
    A subclass of string so it can be told whether a string is a pickled
    object or not (if the object is an instance of this class then it must
    [well, should] be a pickled one).

    Only really useful for passing pre-encoded values to ``default``
    with ``dbsafe_encode``, not that doing so is necessary. If you
    remove PickledObject and its references, you won't be able to pass
    in pre-encoded values anymore, but you can always just pass in the
    python objects themselves.
    """

class _ObjectWrapper(object):
    """
    A class used to wrap object that have properties that may clash with the
    ORM internals.

    For example, objects with the `prepare_database_save` property such as
    `django.db.Model` subclasses won't work under certain conditions and the
    same apply for trying to retrieve any `callable` object.
    """
    __slots__ = ('_obj',)

    def __init__(self, obj):
        self._obj = obj

def wrap_conflictual_object(obj):
    if hasattr(obj, 'prepare_database_save') or callable(obj):
        obj = _ObjectWrapper(obj)
    return obj

def dbsafe_encode(value, compress_object=False, pickle_protocol=DEFAULT_PROTOCOL):
    # We use deepcopy() here to avoid a problem with cPickle, where dumps
    # can generate different character streams for same lookup value if
    # they are referenced differently.
    # The reason this is important is because we do all of our lookups as
    # simple string matches, thus the character streams must be the same
    # for the lookups to work properly. See tests.py for more information.
    value = dumps(deepcopy(value), protocol=pickle_protocol)
    if compress_object:
        value = compress(value)
    value = b64encode(value).decode()  # decode bytes to str
    return PickledObject(value)

def dbsafe_decode(value, compress_object=False):
    value = value.encode()  # encode str to bytes
    value = b64decode(value)
    if compress_object:
        value = decompress(value)
    return loads(value)

class PickledWidget(Textarea):
    def render(self, name, value, attrs=None):
        """Display of the PickledField in django admin"""
        value = repr(value)
        try:
            # necessary to convert it back after repr(), otherwise validation errors will mutate it
            value = literal_eval(value)
        except ValueError:
            return value

        # fix since the signature of build_attrs changed in Django 1.11
        if attrs is not None:
            attrs["name"] = name
        else:
            attrs = {"name": name}

        final_attrs = self.build_attrs(attrs)
        return format_html('<textarea{0}>\r\n{1}</textarea>',
                           flatatt(final_attrs),
                           value)

class PickledFormField(CharField):
    widget = PickledWidget
    default_error_messages = dict(CharField.default_error_messages)
    default_error_messages['invalid'] = (
        "This is not a Python Literal. You can store things like strings, "
        "integers, or floats, but you must do it by typing them as you would "
        "type them in the Python Interpreter. For instance, strings must be "
        "surrounded by quote marks. We have converted it to a string for your "
        "convenience. If it is acceptable, please hit save again.")

    def __init__(self, *args, **kwargs):
        # This needs to fall through to literal_eval.
        kwargs['required'] = False
        super().__init__(*args, **kwargs)

    def clean(self, value):
        try:
            if not value.strip():
                # Field was left blank. Make this None.
                value = 'None'
        except AttributeError:
            value = 'None'
        try:
            return literal_eval(value)
        except (ValueError, SyntaxError):
            try:
                value = repr(value)
                return literal_eval(value)
            except (ValueError, SyntaxError):
                raise ValidationError(self.error_messages['invalid'])

class PickledObjectField(models.Field):
    """
    A field that will accept *any* python object and store it in the
    database. PickledObjectField will optionally compress its values if
    declared with the keyword argument ``compress=True``.

    Does not actually encode and compress ``None`` objects (although you
    can still do lookups using None). This way, it is still possible to
    use the ``isnull`` lookup type correctly.
    """

    def __init__(self, *args, **kwargs):
        self.compress = kwargs.pop('compress', False)
        self.protocol = kwargs.pop('protocol', DEFAULT_PROTOCOL)
        super().__init__(*args, **kwargs)

    def get_default(self):
        """
        Returns the default value for this field.

        The default implementation on models.Field calls force_text
        on the default, which means you can't set arbitrary Python
        objects as the default. To fix this, we just return the value
        without calling force_text on it. Note that if you set a
        callable as a default, the field will still call it. It will
        *not* try to pickle and encode it.

        """
        if self.has_default():
            if callable(self.default):
                return self.default()
            return self.default
        # If the field doesn't have a default, then we punt to models.Field.
        return super().get_default()

    # def to_python(self, value):
    def from_db_value(self, value, *args):
        """
        B64decode and unpickle the object, optionally decompressing it.

        If an error is raised in de-pickling and we're sure the value is
        a definite pickle, the error is allowed to propagate. If we
        aren't sure if the value is a pickle or not, then we catch the
        error and return the original value instead.

        """
        if value is not None:
            try:
                value = dbsafe_decode(value, self.compress)
            except Exception:
                # If the value is a definite pickle; and an error is raised in
                # de-pickling it should be allowed to propogate.
                if isinstance(value, PickledObject):
                    raise
            else:
                if isinstance(value, _ObjectWrapper):
                    return value._obj
        return value

    def formfield(self, **kwargs):
        return PickledFormField(**kwargs)

    def pre_save(self, model_instance, add):
        value = super().pre_save(model_instance, add)
        return wrap_conflictual_object(value)

    def get_db_prep_value(self, value, connection=None, prepared=False):
        """
        Pickle and b64encode the object, optionally compressing it.

        The pickling protocol is specified explicitly (by default 2),
        rather than as -1 or HIGHEST_PROTOCOL, because we don't want the
        protocol to change over time. If it did, ``exact`` and ``in``
        lookups would likely fail, since pickle would now be generating
        a different string.

        """
        if value is not None and not isinstance(value, PickledObject):
            # We call force_text here explicitly, so that the encoded string
            # isn't rejected by the postgresql_psycopg2 backend. Alternatively,
            # we could have just registered PickledObject with the psycopg
            # marshaller (telling it to store it like it would a string), but
            # since both of these methods result in the same value being stored,
            # doing things this way is much easier.
            value = force_text(dbsafe_encode(value, self.compress, self.protocol))
        return value

    def value_to_string(self, obj):
        value = self._get_val_from_obj(obj)
        return self.get_db_prep_value(value)

    def get_internal_type(self):
        return 'TextField'

    def get_db_prep_lookup(self, lookup_type, value, connection=None, prepared=False):
        if lookup_type not in ['exact', 'in', 'isnull']:
            raise TypeError('Lookup type %s is not supported.' % lookup_type)
        # The Field model already calls get_db_prep_value before doing the
        # actual lookup, so all we need to do is limit the lookup types.
        return super().get_db_prep_lookup(
            lookup_type, value, connection=connection, prepared=prepared)
<commit_msg>Use renderer kwarg for picklefield widget; related to #1843<commit_after>#
#  Copyright (c) 2009-2010 Gintautas Miliauskas
#
#   Permission is hereby granted, free of charge, to any person
#   obtaining a copy of this software and associated documentation
#   files (the "Software"), to deal in the Software without
#   restriction, including without limitation the rights to use,
#   copy, modify, merge, publish, distribute, sublicense, and/or sell
#   copies of the Software, and to permit persons to whom the
#   Software is furnished to do so, subject to the following
#   conditions:
#
#   The above copyright notice and this permission notice shall be
#   included in all copies or substantial portions of the Software.
#
#   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
#   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
#   OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
#   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
#   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
#   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
#   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
#   OTHER DEALINGS IN THE SOFTWARE.

"""
Pickle field implementation for Django.

Modified for Evennia by Griatch and the Evennia community.

"""
from builtins import object
from ast import literal_eval

from copy import deepcopy
from base64 import b64encode, b64decode
from zlib import compress, decompress
# import six # this is actually a pypy component, not in default syslib
from django.core.exceptions import ValidationError
from django.db import models

from django.forms import CharField, Textarea
from django.forms.utils import flatatt
from django.utils.html import format_html

from pickle import loads, dumps
from django.utils.encoding import force_text

DEFAULT_PROTOCOL = 4

class PickledObject(str):
    """
    A subclass of string so it can be told whether a string is a pickled
    object or not (if the object is an instance of this class then it must
    [well, should] be a pickled one).

    Only really useful for passing pre-encoded values to ``default``
    with ``dbsafe_encode``, not that doing so is necessary. If you
    remove PickledObject and its references, you won't be able to pass
    in pre-encoded values anymore, but you can always just pass in the
    python objects themselves.
    """

class _ObjectWrapper(object):
    """
    A class used to wrap object that have properties that may clash with the
    ORM internals.

    For example, objects with the `prepare_database_save` property such as
    `django.db.Model` subclasses won't work under certain conditions and the
    same apply for trying to retrieve any `callable` object.
    """
    __slots__ = ('_obj',)

    def __init__(self, obj):
        self._obj = obj

def wrap_conflictual_object(obj):
    if hasattr(obj, 'prepare_database_save') or callable(obj):
        obj = _ObjectWrapper(obj)
    return obj

def dbsafe_encode(value, compress_object=False, pickle_protocol=DEFAULT_PROTOCOL):
    # We use deepcopy() here to avoid a problem with cPickle, where dumps
    # can generate different character streams for same lookup value if
    # they are referenced differently.
    # The reason this is important is because we do all of our lookups as
    # simple string matches, thus the character streams must be the same
    # for the lookups to work properly. See tests.py for more information.
    value = dumps(deepcopy(value), protocol=pickle_protocol)
    if compress_object:
        value = compress(value)
    value = b64encode(value).decode()  # decode bytes to str
    return PickledObject(value)

def dbsafe_decode(value, compress_object=False):
    value = value.encode()  # encode str to bytes
    value = b64decode(value)
    if compress_object:
        value = decompress(value)
    return loads(value)

class PickledWidget(Textarea):
    def render(self, name, value, attrs=None, renderer=None):
        """Display of the PickledField in django admin"""

        value = repr(value)
        try:
            # necessary to convert it back after repr(), otherwise validation errors will mutate it
            value = literal_eval(value)
        except ValueError:
            return value

        # fix since the signature of build_attrs changed in Django 1.11
        if attrs is not None:
            attrs["name"] = name
        else:
            attrs = {"name": name}
        attrs['cols'] = 30
        # adapt rows to width
        rows = 1
        if isinstance(value, str) and "\n" in value:
            rows = max(1, len(value.split('\n')))
        attrs['rows'] = rows
        attrs = self.build_attrs(attrs)

        return super().render(name, value, attrs=attrs, renderer=renderer)
        # return format_html('<textarea{0}>\r\n{1}</textarea>',
        #                    flatatt(final_attrs),
        #                    value)

class PickledFormField(CharField):
    widget = PickledWidget
    default_error_messages = dict(CharField.default_error_messages)
    default_error_messages['invalid'] = (
        "This is not a Python Literal. You can store things like strings, "
        "integers, or floats, but you must do it by typing them as you would "
        "type them in the Python Interpreter. For instance, strings must be "
        "surrounded by quote marks. We have converted it to a string for your "
        "convenience. If it is acceptable, please hit save again.")

    def __init__(self, *args, **kwargs):
        # This needs to fall through to literal_eval.
        kwargs['required'] = False
        super().__init__(*args, **kwargs)

    def clean(self, value):
        try:
            if not value.strip():
                # Field was left blank. Make this None.
                value = 'None'
        except AttributeError:
            value = 'None'
        try:
            return literal_eval(value)
        except (ValueError, SyntaxError):
            try:
                value = repr(value)
                return literal_eval(value)
            except (ValueError, SyntaxError):
                raise ValidationError(self.error_messages['invalid'])

class PickledObjectField(models.Field):
    """
    A field that will accept *any* python object and store it in the
    database. PickledObjectField will optionally compress its values if
    declared with the keyword argument ``compress=True``.

    Does not actually encode and compress ``None`` objects (although you
    can still do lookups using None). This way, it is still possible to
    use the ``isnull`` lookup type correctly.
    """

    def __init__(self, *args, **kwargs):
        self.compress = kwargs.pop('compress', False)
        self.protocol = kwargs.pop('protocol', DEFAULT_PROTOCOL)
        super().__init__(*args, **kwargs)

    def get_default(self):
        """
        Returns the default value for this field.

        The default implementation on models.Field calls force_text
        on the default, which means you can't set arbitrary Python
        objects as the default. To fix this, we just return the value
        without calling force_text on it. Note that if you set a
        callable as a default, the field will still call it. It will
        *not* try to pickle and encode it.

        """
        if self.has_default():
            if callable(self.default):
                return self.default()
            return self.default
        # If the field doesn't have a default, then we punt to models.Field.
        return super().get_default()

    # def to_python(self, value):
    def from_db_value(self, value, *args):
        """
        B64decode and unpickle the object, optionally decompressing it.

        If an error is raised in de-pickling and we're sure the value is
        a definite pickle, the error is allowed to propagate. If we
        aren't sure if the value is a pickle or not, then we catch the
        error and return the original value instead.

        """
        if value is not None:
            try:
                value = dbsafe_decode(value, self.compress)
            except Exception:
                # If the value is a definite pickle; and an error is raised in
                # de-pickling it should be allowed to propogate.
                if isinstance(value, PickledObject):
                    raise
            else:
                if isinstance(value, _ObjectWrapper):
                    return value._obj
        return value

    def formfield(self, **kwargs):
        return PickledFormField(**kwargs)

    def pre_save(self, model_instance, add):
        value = super().pre_save(model_instance, add)
        return wrap_conflictual_object(value)

    def get_db_prep_value(self, value, connection=None, prepared=False):
        """
        Pickle and b64encode the object, optionally compressing it.

        The pickling protocol is specified explicitly (by default 2),
        rather than as -1 or HIGHEST_PROTOCOL, because we don't want the
        protocol to change over time. If it did, ``exact`` and ``in``
        lookups would likely fail, since pickle would now be generating
        a different string.

        """
        if value is not None and not isinstance(value, PickledObject):
            # We call force_text here explicitly, so that the encoded string
            # isn't rejected by the postgresql_psycopg2 backend. Alternatively,
            # we could have just registered PickledObject with the psycopg
            # marshaller (telling it to store it like it would a string), but
            # since both of these methods result in the same value being stored,
            # doing things this way is much easier.
            value = force_text(dbsafe_encode(value, self.compress, self.protocol))
        return value

    def value_to_string(self, obj):
        value = self._get_val_from_obj(obj)
        return self.get_db_prep_value(value)

    def get_internal_type(self):
        return 'TextField'

    def get_db_prep_lookup(self, lookup_type, value, connection=None, prepared=False):
        if lookup_type not in ['exact', 'in', 'isnull']:
            raise TypeError('Lookup type %s is not supported.' % lookup_type)
        # The Field model already calls get_db_prep_value before doing the
        # actual lookup, so all we need to do is limit the lookup types.
        return super().get_db_prep_lookup(
            lookup_type, value, connection=connection, prepared=prepared)
<|endoftext|>


<commit_before>from kolibri.content.models import ChannelMetadataCache, ContentNode, File
from rest_framework import serializers

class ChannelMetadataCacheSerializer(serializers.ModelSerializer):

    class Meta:
        model = ChannelMetadataCache
        fields = ('root_pk', 'id', 'name', 'description', 'author')

class FileSerializer(serializers.ModelSerializer):
    storage_url = serializers.SerializerMethodField()

    def get_storage_url(self, target_node):
        return target_node.get_url()

    class Meta:
        model = File
        fields = ('storage_url', 'id', 'priority', 'checksum', 'available', 'file_size', 'extension', 'preset', 'lang', 'supplementary', 'thumbnail')

class ContentNodeSerializer(serializers.ModelSerializer):
    parent = serializers.PrimaryKeyRelatedField(read_only=True)

    # Here we use a FileSerializer instead just the files reverse FK is because we want to get the computed field storage_url
    # In order to improve performance in production, we should implement a client side method to calculate the file url using
    # extension and checksum field along with the setting.STORAGE_ROOT, which can be passed to front end at template boostrapping.
    files = FileSerializer(many=True, read_only=True)
    ancestors = serializers.SerializerMethodField()
    thumbnail = serializers.SerializerMethodField()

    def __init__(self, *args, **kwargs):
        # Instantiate the superclass normally
        super(ContentNodeSerializer, self).__init__(*args, **kwargs)

        # enable dynamic fields specification!
        if 'request' in self.context and self.context['request'].GET.get('fields', None):
            fields = self.context['request'].GET['fields'].split(',')
            # Drop any fields that are not specified in the `fields` argument.
            allowed = set(fields)
            existing = set(self.fields.keys())
            for field_name in existing - allowed:
                self.fields.pop(field_name)

    def get_ancestors(self, target_node):
        """
        in descending order (root ancestor first, immediate parent last)
        """
        return target_node.get_ancestors().values('pk', 'title')

    def get_thumbnail(self, target_node):
        try:
            return target_node.files.get(thumbnail=True, available=True).get_url()
        except File.DoesNotExist:
            return None

    class Meta:
        model = ContentNode
        fields = (
            'pk', 'content_id', 'title', 'description', 'kind', 'available', 'tags', 'sort_order', 'license_owner',
            'license', 'files', 'ancestors', 'parent', 'thumbnail'
        )
<commit_msg>Just in case @jamalex has had one too many (thumbnails).<commit_after>from kolibri.content.models import ChannelMetadataCache, ContentNode, File
from rest_framework import serializers

class ChannelMetadataCacheSerializer(serializers.ModelSerializer):

    class Meta:
        model = ChannelMetadataCache
        fields = ('root_pk', 'id', 'name', 'description', 'author')

class FileSerializer(serializers.ModelSerializer):
    storage_url = serializers.SerializerMethodField()

    def get_storage_url(self, target_node):
        return target_node.get_url()

    class Meta:
        model = File
        fields = ('storage_url', 'id', 'priority', 'checksum', 'available', 'file_size', 'extension', 'preset', 'lang', 'supplementary', 'thumbnail')

class ContentNodeSerializer(serializers.ModelSerializer):
    parent = serializers.PrimaryKeyRelatedField(read_only=True)

    # Here we use a FileSerializer instead just the files reverse FK is because we want to get the computed field storage_url
    # In order to improve performance in production, we should implement a client side method to calculate the file url using
    # extension and checksum field along with the setting.STORAGE_ROOT, which can be passed to front end at template boostrapping.
    files = FileSerializer(many=True, read_only=True)
    ancestors = serializers.SerializerMethodField()
    thumbnail = serializers.SerializerMethodField()

    def __init__(self, *args, **kwargs):
        # Instantiate the superclass normally
        super(ContentNodeSerializer, self).__init__(*args, **kwargs)

        # enable dynamic fields specification!
        if 'request' in self.context and self.context['request'].GET.get('fields', None):
            fields = self.context['request'].GET['fields'].split(',')
            # Drop any fields that are not specified in the `fields` argument.
            allowed = set(fields)
            existing = set(self.fields.keys())
            for field_name in existing - allowed:
                self.fields.pop(field_name)

    def get_ancestors(self, target_node):
        """
        in descending order (root ancestor first, immediate parent last)
        """
        return target_node.get_ancestors().values('pk', 'title')

    def get_thumbnail(self, target_node):
        thumbnail_model = target_node.files.filter(thumbnail=True, available=True).first()
        return thumbnail_model.get_url() if thumbnail_model else None

    class Meta:
        model = ContentNode
        fields = (
            'pk', 'content_id', 'title', 'description', 'kind', 'available', 'tags', 'sort_order', 'license_owner',
            'license', 'files', 'ancestors', 'parent', 'thumbnail'
        )
<|endoftext|>


<commit_before>from django.conf import settings
from django.contrib.auth.decorators import login_required
from django.core.cache import cache
from django.core.urlresolvers import reverse_lazy
from django.http import HttpResponseRedirect
from django.views.generic.edit import FormView
import logging
import urllib.parse

from twentythree.forms import ChooseProfileForm
from twentythree.models import Token23

log = logging.getLogger('genoome.twentythree.views')

def token_required(f):
    def inner(request, *args, **kwargs):
        has_token = request.user.token23_set.exists()
        if not has_token:
            return HttpResponseRedirect(reverse_lazy('23andme:login'))
        return f(request, *args, **kwargs)
    return inner

@login_required(login_url=reverse_lazy('accounts:signin'))
def login23(request):
    # https://api.23andme.com/docs/authentication/
    url = 'https://api.23andme.com/authorize/?redirect_uri=%s&response_type=code&client_id=%s&scope=basic%%20genomes'
    url %= (
        urllib.parse.quote(settings.COMEBACK_URL23),
        settings.CLIENT_ID23,
    )
    return HttpResponseRedirect(url)

def comeback(request):
    try:
        code = request.GET['code']
    except KeyError:
        log.info('No code. Redirecting to 23andme_login(%s)', request.user)
        return HttpResponseRedirect(reverse_lazy('23andme:login'))
    try:
        token = Token.get_by_code(request.user, code)
    except Token23.ClientError:
        log.error('Failed token(%r). Redirecting to 23andme_login(%s)', code, request.user)
    return HttpResponseRedirect(reverse_lazy('23andme:profiles'))

class ChooseProfileView(FormView):
    form_class = ChooseProfileForm
    success_url = reverse_lazy('23andme:status')
    template_name = 'twentythree/choose_profile.html'

    def get(self, request, *args, **kwargs):
        self.get_token().get_profiles()
        genotyped_profiles = [p for p in profiles if p['genotyped']]
        if len(genotyped_profiles) == 1:
            # use this profile
            form = self.get_form()(self.get_form_kwargs())
            return self.form_valid(form)
        return super().get(request, *args, **kwargs)

    def get_form_kwargs(self):
        kwargs = self.get_form_kwargs()
        kwargs['profiles'] = self.get_token().get_profiles()
        kwargs['user'] = self.request.user
        return kwargs

    def form_valid(self, form):
        form.save()
        return super().form_valid(form)

profiles = login_required(token_required(ChooseProfileView.as_view()), login_url=reverse_lazy('accounts:signin'))

@login_required(login_url=reverse_lazy('accounts:signin'))
@token_required
def status(request):
    try:
        ctask = CeleryTask23.objects.filter(user=request.user).order_by('-pk')[0]
    except CeleryTask23.DoesNotExist:
        return HttpResponseRedirect(raverse_lazy('23andme:profiles'))
    if ctask.status == 'parsing':
        return HttpResponseRedirect(genomeurl)
    return render_to_response('twentythree/status.html', {'ctask': ctask})
<commit_msg>23andme wip<commit_after>from django.conf import settings
from django.contrib.auth.decorators import login_required
from django.core.cache import cache
from django.core.urlresolvers import reverse_lazy
from django.http import HttpResponseRedirect
from django.views.generic.edit import FormView
import logging
import urllib.parse

from twentythree.forms import ChooseProfileForm
from twentythree.models import Token23

log = logging.getLogger('genoome.twentythree.views')

def token_required(f):
    def inner(request, *args, **kwargs):
        has_token = request.user.token23_set.exists()
        if not has_token:
            return HttpResponseRedirect(reverse_lazy('23andme:login'))
        return f(request, *args, **kwargs)
    return inner

@login_required(login_url=reverse_lazy('accounts:signin'))
def login23(request):
    # https://api.23andme.com/docs/authentication/
    url = 'https://api.23andme.com/authorize/?redirect_uri=%s&response_type=code&client_id=%s&scope=basic%%20genomes'
    url %= (
        urllib.parse.quote(settings.COMEBACK_URL23),
        settings.CLIENT_ID23,
    )
    return HttpResponseRedirect(url)

def comeback(request):
    try:
        code = request.GET['code']
    except KeyError:
        log.info('No code. Redirecting to 23andme_login(%s)', request.user)
        return HttpResponseRedirect(reverse_lazy('23andme:login'))
    try:
        token = Token23.get_by_code(request.user, code)
    except Token23.ClientError:
        log.error('Failed token(%r). Redirecting to 23andme_login(%s)', code, request.user)
    return HttpResponseRedirect(reverse_lazy('23andme:profiles'))

class ChooseProfileView(FormView):
    form_class = ChooseProfileForm
    success_url = reverse_lazy('23andme:status')
    template_name = 'twentythree/choose_profile.html'

    def get(self, request, *args, **kwargs):
        self.get_token().get_profiles()
        genotyped_profiles = [p for p in profiles if p['genotyped']]
        if len(genotyped_profiles) == 1:
            # use this profile
            form = self.get_form()(self.get_form_kwargs())
            return self.form_valid(form)
        return super().get(request, *args, **kwargs)

    def get_form_kwargs(self):
        kwargs = self.get_form_kwargs()
        kwargs['profiles'] = self.get_token().get_profiles()
        kwargs['user'] = self.request.user
        return kwargs

    def form_valid(self, form):
        form.save()
        return super().form_valid(form)

profiles = login_required(token_required(ChooseProfileView.as_view()), login_url=reverse_lazy('accounts:signin'))

@login_required(login_url=reverse_lazy('accounts:signin'))
@token_required
def status(request):
    try:
        ctask = CeleryTask23.objects.filter(user=request.user).order_by('-pk')[0]
    except CeleryTask23.DoesNotExist:
        return HttpResponseRedirect(raverse_lazy('23andme:profiles'))
    if ctask.status == 'parsing':
        return HttpResponseRedirect(genomeurl)
    return render_to_response('twentythree/status.html', {'ctask': ctask})
<|endoftext|>


<commit_before>import types
import textwrap
from docopt import docopt
import inspect
import sys
import importlib
from cmd3.shell import command
import cloudmesh
from pprint import pprint
from cloudmesh.util.logger import LOGGER
from cloudmesh.cm_mongo import cm_mongo
from cloudmesh.config.cm_config import cm_config
from prettytable import PrettyTable

log = LOGGER(__file__)

class cm_shell_list:

    """opt_example class"""

    def activate_cm_shell_list(self):
        pass

    def _printList(self, parameter, fieldList, clouds):
        mesh = cloudmesh.mesh()

        x = PrettyTable()
        x.field_names = fieldList
        for field in fieldList:
            x.align[field] = "c" #center aligned

        try:
            for cloud in clouds:
                mesh.refresh(names=[cloud], types=[parameter])
                if cloud in mesh.clouds and mesh.clouds[cloud]:
                    params = mesh.clouds[cloud][parameter]
                    if len(params)>0:
                        print "\nCloud: ", cloud, "\n"
                        for key, value in params.iteritems():
                            # since value is a dictionary of multiple items,
                            # we add to table only fields from the fieldList
                            rowList = []
                            for field in fieldList:
                                rowList.append(value[field])
                            x.add_row(rowList)
                        print x.get_string(sortby=fieldList[0])
                        print "\n\n"
            return
        except:
            print "Unexpected error: ", sys.exc_info()[0], sys.exc_info()[1]

    @command
    def do_list(self, args, arguments):
        """
        Usage:
               list flavors [CLOUD]
               list servers [CLOUD]
               list images [CLOUD]

        Arguments:

                CLOUD    the name of the cloud

        Options:

           -v       verbose mode

        """
        mesh = cloudmesh.mesh()
        mongo = cm_mongo()
        config = cm_config()
        user = config.username()
        dbDict = self.mongoClass.db_defaults.find_one({'cm_user_id': user})
        #log.info(args)
        pprint(arguments)
        #log.info(arguments)
        all = False
        if not arguments["CLOUD"]:
            all = True
            if 'activeclouds' in dbDict and dbDict['activeclouds']:
                clouds = dbDict['activeclouds']
            else:
                clouds = [config.default_cloud]
        else:
            clouds = [arguments["CLOUD"]]

        if arguments["flavors"]:

            fieldList = ['id', 'name']
            self._printList('flavors', fieldList, clouds)

        if arguments["servers"]:

            fieldList = ["id", "name", "status"]
            self._printList('server', fieldList, clouds)

        if arguments["images"]:

            fieldList = ["id", "name"]
            self._printList('image', fieldList, clouds)

    @command
    def do_count(self, args, arguments):
        """
        Usage:
               count flavors [CLOUD...]
               count servers [CLOUD...]
               count images [CLOUD...]
               count [CLOUD...]

        Arguments:

                CLOUD    the name of the cloud

        Options:

           -v       verbose mode

        """
        log.info(args)

        log.info(arguments)

        if len(arguments["CLOUD"]) == 0:
            print "get all active clouds"
            all = True
            clouds = ['a', 'b']
        else:
            clouds = arguments['CLOUD']
        print clouds

        if arguments["flavors"] or all:
            log.info ("count flavors")
            for cloud in clouds:
                print "cloud: flavors", cloud, None

        if arguments["servers"] or all:
            log.info ("count servers")
            for cloud in clouds:
                print "cloud: servers", cloud, None

        if arguments["images"] or all:
            log.info ("list images")
            for cloud in clouds:
                print "cloud: images", cloud, None

def main():
    print "test correct"
    clouds = ['sierra_openstack_grizzly']
    fieldList = ["id", "name", "status"]

    ls = cm_shell_list()
    ls._printList('image', fieldList, clouds)

if __name__ == "__main__":
    main()

<commit_msg>CM Shell: List all clouds.<commit_after>import types
import textwrap
from docopt import docopt
import inspect
import sys
import importlib
from cmd3.shell import command
import cloudmesh
from pprint import pprint
from cloudmesh.util.logger import LOGGER
from cloudmesh.cm_mongo import cm_mongo
from cloudmesh.config.cm_config import cm_config
from prettytable import PrettyTable

log = LOGGER(__file__)

class cm_shell_list:

    """opt_example class"""

    def activate_cm_shell_list(self):
        pass

    def _printList(self, parameter, fieldList, clouds):
        mesh = cloudmesh.mesh()

        x = PrettyTable()
        x.field_names = fieldList
        for field in fieldList:
            x.align[field] = "c" #center aligned

        try:
            for cloud in clouds:
                mesh.refresh(names=[cloud], types=[parameter])
                if cloud in mesh.clouds and mesh.clouds[cloud]:
                    params = mesh.clouds[cloud][parameter]
                    if len(params)>0:
                        print "\nCloud: ", cloud, "\n"
                        for key, value in params.iteritems():
                            # since value is a dictionary of multiple items,
                            # we add to table only fields from the fieldList
                            rowList = []
                            for field in fieldList:
                                rowList.append(value[field])
                            x.add_row(rowList)
                        print x.get_string(sortby=fieldList[0])
                        print "\n\n"
            return
        except:
            print "Unexpected error: ", sys.exc_info()[0], sys.exc_info()[1]

    @command
    def do_list(self, args, arguments):
        """
        Usage:
               list flavors [CLOUD]
               list servers [CLOUD]
               list images [CLOUD]
               list

        Arguments:

                CLOUD    the name of the cloud

        Options:

           -v       verbose mode

        """
        mesh = cloudmesh.mesh()
        mongo = cm_mongo()
        config = cm_config()
        user = config.username()
        dbDict = self.mongoClass.db_defaults.find_one({'cm_user_id': user})
        #log.info(args)
        #pprint(arguments)
        #chk if no args provided, to output the list of clouds.
        print '\n\n'
        noArg = None
        for value in arguments.values():
            if value:
                noArg = False
                break
        #log.info(arguments)
        all = False
        if not arguments["CLOUD"]:
            all = True
            if 'activeclouds' in dbDict and dbDict['activeclouds']:
                clouds = dbDict['activeclouds']
            else:
                clouds = [config.default_cloud]
        else:
            clouds = [arguments["CLOUD"]]

        #no args provided, print the clouds.
        if not noArg:
            print "--------------------------------------------------------------------------------"
            print 'List of Clouds:'
            for cloud in clouds:
                print cloud
            print '\n'
            return

        if arguments["flavors"]:

            fieldList = ['id', 'name']
            self._printList('flavors', fieldList, clouds)

        if arguments["servers"]:

            fieldList = ["id", "name", "status"]
            self._printList('server', fieldList, clouds)

        if arguments["images"]:

            fieldList = ["id", "name"]
            self._printList('image', fieldList, clouds)

    @command
    def do_count(self, args, arguments):
        """
        Usage:
               count flavors [CLOUD...]
               count servers [CLOUD...]
               count images [CLOUD...]
               count [CLOUD...]

        Arguments:

                CLOUD    the name of the cloud

        Options:

           -v       verbose mode

        """
        log.info(args)

        log.info(arguments)

        if len(arguments["CLOUD"]) == 0:
            print "get all active clouds"
            all = True
            clouds = ['a', 'b']
        else:
            clouds = arguments['CLOUD']
        print clouds

        if arguments["flavors"] or all:
            log.info ("count flavors")
            for cloud in clouds:
                print "cloud: flavors", cloud, None

        if arguments["servers"] or all:
            log.info ("count servers")
            for cloud in clouds:
                print "cloud: servers", cloud, None

        if arguments["images"] or all:
            log.info ("list images")
            for cloud in clouds:
                print "cloud: images", cloud, None

def main():
    print "test correct"
    clouds = ['sierra_openstack_grizzly']
    fieldList = ["id", "name", "status"]

    ls = cm_shell_list()
    ls._printList('image', fieldList, clouds)

if __name__ == "__main__":
    main()

<|endoftext|>


<commit_before>import logging

from leaguebot.services import screeps, redis_data, redis_queue

def get_battles(since_tick=None, interval=None):
    client = screeps.get_client()
    return client.battles(sinceTick=since_tick, interval=interval)

def get_nukes():
    client = screeps.get_client()
    return client.nukes()['nukes']

def check_and_queue_battles_once():
    last_grabbed_tick = redis_data.get_latest_fetched_tick()
    if last_grabbed_tick:
        last_grabbed_tick = int(last_grabbed_tick)
    if last_grabbed_tick:
        battles = get_battles(since_tick=last_grabbed_tick)
    else:
        battles = get_battles(interval=2000)
    if not battles:
        logging.warning("Something went wrong accessing the battles API.")
        return

    new_latest_tick = int(battles['time'])

    redis_queue.push_battles_for_processing(
        new_latest_tick,
        ((obj['_id'], obj['lastPvpTime']) for obj in battles['rooms'])
    )
<commit_msg>Only check back 500 ticks on first run, rather than 2000 ticks back<commit_after>import logging

from leaguebot.services import screeps, redis_data, redis_queue

def get_battles(since_tick=None, interval=None):
    client = screeps.get_client()
    return client.battles(sinceTick=since_tick, interval=interval)

def get_nukes():
    client = screeps.get_client()
    return client.nukes()['nukes']

def check_and_queue_battles_once():
    last_grabbed_tick = redis_data.get_latest_fetched_tick()
    if last_grabbed_tick:
        last_grabbed_tick = int(last_grabbed_tick)
    if last_grabbed_tick:
        battles = get_battles(since_tick=last_grabbed_tick)
    else:
        battles = get_battles(interval=500)
    if not battles:
        logging.warning("Something went wrong accessing the battles API.")
        return

    new_latest_tick = int(battles['time'])

    redis_queue.push_battles_for_processing(
        new_latest_tick,
        ((obj['_id'], obj['lastPvpTime']) for obj in battles['rooms'])
    )
<|endoftext|>


<commit_before>#!/usr/bin/env python3
import os
import sys
from rq import Connection, Worker
import django
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "djing.settings")
from agent import NasFailedResult, NasNetworkError
from django.core.exceptions import ValidationError

"""
  Заустить этот скрипт как демон, он соединяет redis и django
"""
if __name__ == '__main__':
    try:
        django.setup()
        with Connection():
            qs = sys.argv[1:] or ['default']
            w = Worker(qs)
            w.work()
    except (NasNetworkError, NasFailedResult) as e:
        print('NAS:', e)
    except (ValidationError, ValueError) as e:
        print('ERROR:', e)
<commit_msg>Изменил права на запуск<commit_after>#!/usr/bin/env python3
import os
import sys
from rq import Connection, Worker
import django
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "djing.settings")
from agent import NasFailedResult, NasNetworkError
from django.core.exceptions import ValidationError

"""
  Заустить этот скрипт как демон, он соединяет redis и django
"""
if __name__ == '__main__':
    try:
        django.setup()
        with Connection():
            qs = sys.argv[1:] or ['default']
            w = Worker(qs)
            w.work()
    except (NasNetworkError, NasFailedResult) as e:
        print('NAS:', e)
    except (ValidationError, ValueError) as e:
        print('ERROR:', e)
<|endoftext|>


<commit_before>try:
    from collections import OrderedDict
except ImportError:
    from ordereddict import OrderedDict

top_menu_options = OrderedDict([
    ("houdini", {
        "menu_name": "Houdini",
        "menu_view": "hou_reports",
        "menu_options": OrderedDict([
            ("overview", "Overview"),
            ("uptime", "Session Information"),
            ("crashes", "Crashes"),
            ("node_usage", "Node Usage"),
            ("versions_and_builds", "Versions and builds"),
        ]),
    }),
    ("licensing", {
        "menu_name": "Downloads & Licensing",
        "menu_view": "hou_licenses",
        "menu_options": OrderedDict([
            ("downloads", "Houdini Downloads"),
            ("apprentice_activations", "Apprentice Activations"),
            ("apprentice_hd", "Apprentice HD"),
        ]),
    }),
    ("surveys", {
        "menu_name": "Surveys",
        "menu_view": "hou_surveys",
        "menu_options": OrderedDict([
            ("sidefx_labs", "labs.sidefx.com survey"),
            ("apprentice_followup","Apprentice survey"),
        ]),
    }),
    ("sidefx.com", {
        "menu_name": "Website",
        "menu_view": "hou_forum",
        "menu_options": OrderedDict([
            ("login_registration", "Login & Registration"),
        ]),
    }),
    ("orbolt", {
        "menu_name": "Orbolt",
        "menu_view": "hou_orbolt",
        "menu_options": OrderedDict([
        ]),
    }),
])

<commit_msg>Commenting out orbolt menu since it was repeated<commit_after>try:
    from collections import OrderedDict
except ImportError:
    from ordereddict import OrderedDict

top_menu_options = OrderedDict([
    ("houdini", {
        "menu_name": "Houdini",
        "menu_view": "hou_reports",
        "menu_options": OrderedDict([
            ("overview", "Overview"),
            ("uptime", "Session Information"),
            ("crashes", "Crashes"),
            ("node_usage", "Node Usage"),
            ("versions_and_builds", "Versions and builds"),
        ]),
    }),
    ("licensing", {
        "menu_name": "Downloads & Licensing",
        "menu_view": "hou_licenses",
        "menu_options": OrderedDict([
            ("downloads", "Houdini Downloads"),
            ("apprentice_activations", "Apprentice Activations"),
            ("apprentice_hd", "Apprentice HD"),
        ]),
    }),
    ("surveys", {
        "menu_name": "Surveys",
        "menu_view": "hou_surveys",
        "menu_options": OrderedDict([
            ("sidefx_labs", "labs.sidefx.com survey"),
            ("apprentice_followup","Apprentice survey"),
        ]),
    }),
    ("sidefx.com", {
        "menu_name": "Website",
        "menu_view": "hou_forum",
        "menu_options": OrderedDict([
            ("login_registration", "Login & Registration"),
        ]),
    }),
#
#    ("orbolt", {
#        "menu_name": "Orbolt",
#        "menu_view": "hou_orbolt",
#        "menu_options": OrderedDict([
#        ]),
#    }),
])

<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
# © 2015 Elico corp (www.elico-corp.com)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).

from openerp import models, api, fields, tools

class TimesheetReport(models.Model):
    """
        Timesheet Activities Report.
    """
    _name = "timesheet.activity.report"
    _auto = False # Database table should not be created, use init() instead
    _description = "Timesheet Activities Report"
    _rec_name = 'activity_name' # Field used for the Name

    id = fields.Integer('Id', readonly=True)
    activity_type = fields.Selection(
        [
            ('task', 'Task'),
            ('issue', 'Issue'),
            ('timesheet', 'Timesheet'),
        ], 'Type', readonly=True,
        help="Type is used to separate Tasks, Issues and " \
            "Timesheets input directly")
    description = fields.Char('Description', readonly=True)
    hours = fields.Float(
        'Time spent', digits=(16, 2), readonly=True,
        help="Time spent on timesheet")
    user_id = fields.Many2one('res.users', 'User', readonly=True)
    product_id = fields.Many2one('product.product', 'Product', readonly=True)
    date = fields.Date('Date', readonly=True)
    project_id = fields.Many2one('project.project', 'Project', readonly=True)
    project_state = fields.Char('State', readonly=True, help="Project State")
    activity_stage_id = fields.Many2one(
        'project.task.type', 'Stage',
        readonly=True, help="Activity Stage")
    account_id = fields.Many2one(
            'account.analytic.account', 'Analytic account', readonly=True)
    activity_id = fields.Char(
            'Activity id', readonly=True, help="Task id or Issue id")
    activity_name = fields.Char(
            'Activity name', readonly=True, help="Task name or Issue name")
    categ_id = fields.Many2one(
            'project.category.main', 'Task Cat.',
            readonly=True, help="Task Category")
    br_id = fields.Many2one(
            'business.requirement', 'Bus. requ.',
            readonly=True, help="Business requirement")
    partner_id = fields.Many2one(
            'res.partner', 'Customer', readonly=True)
    project_categ_id = fields.Many2one(
            'project.project.category',
            'Project Cat.', readonly=True, help="Project Category")

    def init(self, cr):
        """
            Timesheet Activities Report.
        """
        tools.drop_view_if_exists(cr, 'timesheet_activity_report')
        cr.execute("""
            CREATE OR REPLACE VIEW timesheet_activity_report AS (
                SELECT
                    ts.id,
                    -- Check if the timesheet is linked to a task
                    -- or an issue
                    CASE WHEN tw.id IS NOT NULL THEN 'task'
                    ELSE (
                        CASE WHEN i.id IS NOT NULL THEN 'issue'
                        -- Timesheet created in the timesheet
                        -- activities panel
                        ELSE (
                            CASE WHEN ts.id IS NOT NULL THEN 'timesheet'
                            -- No timesheet attached to this
                            -- task/project/BR
                            ELSE NULL
                            END
                        )
                        END
                    )
                    END AS activity_type,
                    -- Description from the task first
                    -- because the one in the timesheet
                    -- is wrong when it's linked to a task
                    COALESCE(tw.name, al.name) AS description,
                    al.unit_amount AS hours,
                    al.user_id,
                    al.product_id,
                    al.date,
                    p.id AS project_id,
                    p.state AS project_state,
                    COALESCE(t.stage_id, i.stage_id) AS activity_stage_id,
                    COALESCE(a.id, al.account_id) AS account_id,
                    COALESCE(t.id, i.id) AS activity_id,
                    COALESCE(t.name, i.name) AS activity_name,
                    t.categ_id,
                    b.id AS br_id,
                    a.partner_id,
                    p.project_categ_id
                FROM
                    hr_analytic_timesheet ts
                    INNER JOIN account_analytic_line al
                        ON al.id = ts.line_id
                    -- Link with the task
                    LEFT OUTER JOIN project_task_work tw
                        ON tw.hr_analytic_timesheet_id = ts.id
                    LEFT OUTER JOIN project_task t ON t.id = tw.task_id
                    -- Link with the issue
                    LEFT OUTER JOIN project_issue i ON i.id = ts.issue_id
                    -- Link with the project
                    LEFT OUTER JOIN project_project p
                        ON p.id = COALESCE(t.project_id, i.project_id)
                    -- Link with the analytic account
                    LEFT OUTER JOIN account_analytic_account a
                        ON a.id = p.analytic_account_id
                    -- Link with the BR
                    LEFT OUTER JOIN business_requirement b
                        ON b.linked_project = p.id
            )""")
<commit_msg>Fix Travis<commit_after># -*- coding: utf-8 -*-
# © 2015 Elico corp (www.elico-corp.com)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).

from openerp import models, fields, tools

class TimesheetReport(models.Model):
    """
        Timesheet Activities Report.
    """
    _name = "timesheet.activity.report"
    # Database table should not be created, use init() instead
    _auto = False
    _description = "Timesheet Activities Report"
    # Field used for the Name
    _rec_name = 'activity_name'

    id = fields.Integer('Id', readonly=True)
    activity_type = fields.Selection(
        [
            ('task', 'Task'),
            ('issue', 'Issue'),
            ('timesheet', 'Timesheet'),
        ], 'Type', readonly=True,
        help="Type is used to separate Tasks, Issues and "
             "Timesheets input directly")
    description = fields.Char('Description', readonly=True)
    hours = fields.Float(
        'Time spent', digits=(16, 2), readonly=True,
        help="Time spent on timesheet")
    user_id = fields.Many2one('res.users', 'User', readonly=True)
    product_id = fields.Many2one('product.product', 'Product', readonly=True)
    date = fields.Date('Date', readonly=True)
    project_id = fields.Many2one('project.project', 'Project', readonly=True)
    project_state = fields.Char('State', readonly=True, help="Project State")
    activity_stage_id = fields.Many2one(
        'project.task.type', 'Stage',
        readonly=True, help="Activity Stage")
    account_id = fields.Many2one(
        'account.analytic.account', 'Analytic account', readonly=True)
    activity_id = fields.Char(
        'Activity id', readonly=True, help="Task id or Issue id")
    activity_name = fields.Char(
        'Activity name', readonly=True, help="Task name or Issue name")
    categ_id = fields.Many2one(
        'project.category.main', 'Task Cat.',
        readonly=True, help="Task Category")
    br_id = fields.Many2one(
        'business.requirement', 'Bus. requ.',
        readonly=True, help="Business requirement")
    partner_id = fields.Many2one(
        'res.partner', 'Customer', readonly=True)
    project_categ_id = fields.Many2one(
        'project.project.category',
        'Project Cat.', readonly=True, help="Project Category")

    def init(self, cr):
        """
            Timesheet Activities Report.
        """
        tools.drop_view_if_exists(cr, 'timesheet_activity_report')
        cr.execute("""
            CREATE OR REPLACE VIEW timesheet_activity_report AS (
                SELECT
                    ts.id,
                    -- Check if the timesheet is linked to a task
                    -- or an issue
                    CASE WHEN tw.id IS NOT NULL THEN 'task'
                    ELSE (
                        CASE WHEN i.id IS NOT NULL THEN 'issue'
                        -- Timesheet created in the timesheet
                        -- activities panel
                        ELSE (
                            CASE WHEN ts.id IS NOT NULL THEN 'timesheet'
                            -- No timesheet attached to this
                            -- task/project/BR
                            ELSE NULL
                            END
                        )
                        END
                    )
                    END AS activity_type,
                    -- Description from the task first
                    -- because the one in the timesheet
                    -- is wrong when it's linked to a task
                    COALESCE(tw.name, al.name) AS description,
                    al.unit_amount AS hours,
                    al.user_id,
                    al.product_id,
                    al.date,
                    p.id AS project_id,
                    p.state AS project_state,
                    COALESCE(t.stage_id, i.stage_id) AS activity_stage_id,
                    COALESCE(a.id, al.account_id) AS account_id,
                    COALESCE(t.id, i.id) AS activity_id,
                    COALESCE(t.name, i.name) AS activity_name,
                    t.categ_id,
                    b.id AS br_id,
                    a.partner_id,
                    p.project_categ_id
                FROM
                    hr_analytic_timesheet ts
                    INNER JOIN account_analytic_line al
                        ON al.id = ts.line_id
                    -- Link with the task
                    LEFT OUTER JOIN project_task_work tw
                        ON tw.hr_analytic_timesheet_id = ts.id
                    LEFT OUTER JOIN project_task t ON t.id = tw.task_id
                    -- Link with the issue
                    LEFT OUTER JOIN project_issue i ON i.id = ts.issue_id
                    -- Link with the project
                    LEFT OUTER JOIN project_project p
                        ON p.id = COALESCE(t.project_id, i.project_id)
                    -- Link with the analytic account
                    LEFT OUTER JOIN account_analytic_account a
                        ON a.id = p.analytic_account_id
                    -- Link with the BR
                    LEFT OUTER JOIN business_requirement b
                        ON b.linked_project = p.id
            )""")
<|endoftext|>