<commit_before># -*- coding: utf-8 -*-
""" IntelMQ Dataplane Parser """

from intelmq.lib.bot import ParserBot
from intelmq.lib.message import Event

class DataplaneParserBot(ParserBot):

    CATEGORY = {
        'sipquery': {
            'classification.type': 'brute-force',
            'protocol.application': 'sip',
            'event_description.text': 'Address has been seen initiating a SIP OPTIONS query to a remote host. '
                                      'The source report lists hosts that are suspicious of more than just port '
                                      'scanning. The host may be SIP server cataloging or conducting various forms '
                                      'of telephony abuse.',
        },
        'sipinvitation': {
            'classification.type': 'brute-force',
            'protocol.application': 'sip',
            'event_description.text': 'Address has been seen initiating a SIP INVITE operation to a remote host. '
                                      'The source report lists hosts that are suspicious of more than just port '
                                      'scanning. The host may be SIP client cataloging or conducting various forms '
                                      'of telephony abuse.',
        },
        'sipregistration': {
            'classification.type': 'brute-force',
            'protocol.application': 'sip',
            'event_description.text': 'Address has been seen initiating a SIP REGISTER operation to a remote host. '
                                      'The source report lists hosts that are suspicious of more than just port '
                                      'scanning. The host may be SIP client cataloging or conducting various forms '
                                      'of telephony abuse.',
        },
        'sshclient': {
            'classification.type': 'scanner',
            'protocol.application': 'ssh',
            'event_description.text': 'Address has been seen initiating an SSH connection to a remote host. The source '
                                      'report lists hosts that are suspicious of more than just port scanning.  '
                                      'The host may be SSH server cataloging or conducting authentication attack '
                                      'attempts.',
        },
        'sshpwauth': {
            'classification.type': 'brute-force',
            'protocol.application': 'ssh',
            'event_description.text': 'Address has been seen attempting to remotely login to a host using SSH password '
                                      'authentication. The source report lists hosts that are highly suspicious and '
                                      'are likely conducting malicious SSH password authentication attacks.',
        }
    }

    def parse_line(self, line, report):
        if line.startswith('#') or len(line) == 0:
            self.tempdata.append(line)

        else:
            value = line.split('|')
            event = Event(report)
            event.add('time.source', value[3].strip() + '+00:00')
            if value[0].strip() != 'NA':
                event.add('source.asn', value[0].strip())
            if value[1].strip() != 'NA':
                event.add('source.as_name', value[1].split()[0])
            event.add('source.ip', value[2].strip())

            if value[4].strip() in DataplaneParserBot.CATEGORY:
                event.update(DataplaneParserBot.CATEGORY[value[4].strip()])
            else:
                raise ValueError('Unknown data feed %r.' % value[4].strip())

            event.add('raw', line)
            yield event

BOT = DataplaneParserBot
<commit_msg>Dataplane parser refactoring<commit_after># -*- coding: utf-8 -*-
""" IntelMQ Dataplane Parser """

from intelmq.lib.bot import ParserBot
from intelmq.lib.message import Event

class DataplaneParserBot(ParserBot):

    CATEGORY = {
        'sipquery': {
            'classification.type': 'brute-force',
            'protocol.application': 'sip',
            'event_description.text': 'Address has been seen initiating a SIP OPTIONS query to a remote host. '
                                      'The source report lists hosts that are suspicious of more than just port '
                                      'scanning. The host may be SIP server cataloging or conducting various forms '
                                      'of telephony abuse.',
        },
        'sipinvitation': {
            'classification.type': 'brute-force',
            'protocol.application': 'sip',
            'event_description.text': 'Address has been seen initiating a SIP INVITE operation to a remote host. '
                                      'The source report lists hosts that are suspicious of more than just port '
                                      'scanning. The host may be SIP client cataloging or conducting various forms '
                                      'of telephony abuse.',
        },
        'sipregistration': {
            'classification.type': 'brute-force',
            'protocol.application': 'sip',
            'event_description.text': 'Address has been seen initiating a SIP REGISTER operation to a remote host. '
                                      'The source report lists hosts that are suspicious of more than just port '
                                      'scanning. The host may be SIP client cataloging or conducting various forms '
                                      'of telephony abuse.',
        },
        'sshclient': {
            'classification.type': 'scanner',
            'protocol.application': 'ssh',
            'event_description.text': 'Address has been seen initiating an SSH connection to a remote host. The source '
                                      'report lists hosts that are suspicious of more than just port scanning.  '
                                      'The host may be SSH server cataloging or conducting authentication attack '
                                      'attempts.',
        },
        'sshpwauth': {
            'classification.type': 'brute-force',
            'protocol.application': 'ssh',
            'event_description.text': 'Address has been seen attempting to remotely login to a host using SSH password '
                                      'authentication. The source report lists hosts that are highly suspicious and '
                                      'are likely conducting malicious SSH password authentication attacks.',
        }
    }

    def parse_line(self, line, report):
        file_format = [
            ('source.asn', lambda x: x if x != 'NA' else None),
            ('source.as_name', lambda x: x.split()[0] if x != 'NA' else None),
            ('source.ip', lambda x: x),
            ('time.source', lambda x: x + '+00:00'),
        ]

        if line.startswith('#') or len(line) == 0:
            self.tempdata.append(line)
        else:
            event = Event(report)

            line_contents = line.split('|')
            if len(line_contents) != len(file_format) + 1:
                raise ValueError('Incorrect format for feed {}, found line: "{}"', event.get('feed.url'), line)

            if line_contents[-1].strip() in self.CATEGORY:
                event.update(self.CATEGORY[line_contents[-1].strip()])
            else:
                raise ValueError('Unknown data feed {}.'.format(line_contents[-1].strip()))

            for field, setter in zip(line_contents, file_format):
                event.add(setter[0], setter[1](field.strip()))

            event.add('raw', line)
            yield event

BOT = DataplaneParserBot
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
"""
Analysis utils: Helper functions for the result analysis ipython notebooks.

"""

from collections import defaultdict
import pprint
import copy
import json
import time

from featureforge.experimentation.stats_manager import StatsManager
from featureforge.experimentation.utils import DictNormalizer

def myplot(grouping, xkey, ykey, color="Paired", dot="o"):
    pylab.figure(figsize=(13, 13))
    pylab.xlabel(xkey)
    pylab.ylabel(ykey)
    colors = pylab.get_cmap(color)(numpy.linspace(0, 1, len(grouping)))
    for i, (name, xs) in enumerate(sorted(grouping.items())):
        name = name[:100]
        X = [x["results"][xkey] for x in xs]
        Y = [x["results"][ykey] for x in xs]
        X, Y = zip(*sorted(zip(X, Y)))
        pylab.plot(X, Y, dot, color=colors[i], alpha=.99, label=name)
    pylab.legend(loc=2)
    pylab.show()

def pprint_solution(d, delfields=None):
    if not delfields:
        delfields = []
    else:
        delfields = delfields.split()
    delfields.extend("results features database_hash data_shuffle_seed config_version input_file_md5 marshalled_key git_info".split())
    delfields.extend("_id booked_at experiment_status train_percentage".split())
    d = copy.deepcopy(d)
    for name in delfields:
        if name in d:
            del d[name]
    pprint.pprint(d)

def group_by_strategy(xs):
    groups = defaultdict(list)
    normalizer = DictNormalizer()
    delnames = "results marshalled_key data_shuffle_seed train_percentage".split()
    for point in xs:
        d = copy.deepcopy(point)
        for name in delnames:
            del d[name]
        # "booked_at" and "_id" unhashable by the normalizer:
        if "booked_at" in d:
            del d["booked_at"]
        if "_id" in d:
            del d["_id"]
        d = normalizer(d)
        key = json.dumps(d, sort_keys=True)
        groups[key].append(point)
    return dict(groups)

def average_stats(xs):
    groups = defaultdict(list)
    normalizer = DictNormalizer()
    for point in xs:
        d = copy.deepcopy(point)
        del d["results"]
        del d["marshalled_key"]
        del d["data_shuffle_seed"]
        # "booked_at" and "_id" unhashable by the normalizer:
        if "booked_at" in d:
            del d["booked_at"]
        if "_id" in d:
            del d["_id"]
        d = normalizer(d)
        key = json.dumps(d, sort_keys=True)
        groups[key].append(point)
    xs = []
    for ys in groups.values():
        if len(ys) < 10:
            continue
        x = copy.deepcopy(ys[0])
        for name in ["f1", "precision", "recall", "accuracy"]:
            values = [y["results"][name] for y in ys]
            avg = sum(values) / len(values)
            var = sum((y - avg) ** 2 for y in values) / (len(values) - 1)
            del x["results"][name]
            x["results"][name + "_avg"] = avg
            x["results"][name + "_var"] = var
        xs.append(x)
    return xs

def rday(r):
    t = r[u'results'][u'start_time']
    t = time.gmtime(t)
    return (t.tm_year, t.tm_mon, t.tm_mday)

def rdatasize(r):
    return r[u'results'][u'train_size'] + r[u'results'][u'test_size']

def rkey(r):
    return (r[u'config_version'],
            r[u'git_info'],
            r[u"input_file_md5"],
            tuple(r[u"database_hash"]))
#            rday(r))

# francolq's:

# print the results for a specific strategy, ordered by train_percentage:
def pprint_strategy_result(l):
    print '%\ttp\tP\tR\tF1'
    for p in sorted(l, key=lambda p: p[u'train_percentage']):
        print '{}\t{}\t{:.2}\t{:.2}\t{:.2}'.format(p[u'train_percentage'], p[u'results'][u'true_positives'], p[u'results'][u'precision_avg'], p[u'results'][u'recall_avg'], p[u'results'][u'f1_avg'])
<commit_msg>Missing imports.<commit_after># -*- coding: utf-8 -*-
"""
Analysis utils: Helper functions for the result analysis ipython notebooks.

"""

from collections import defaultdict
import pprint
import copy
import json
import time
import pylab
import numpy

from featureforge.experimentation.stats_manager import StatsManager
from featureforge.experimentation.utils import DictNormalizer

def myplot(grouping, xkey, ykey, color="Paired", dot="o"):
    pylab.figure(figsize=(13, 13))
    pylab.xlabel(xkey)
    pylab.ylabel(ykey)
    colors = pylab.get_cmap(color)(numpy.linspace(0, 1, len(grouping)))
    for i, (name, xs) in enumerate(sorted(grouping.items())):
        name = name[:100]
        X = [x["results"][xkey] for x in xs]
        Y = [x["results"][ykey] for x in xs]
        X, Y = zip(*sorted(zip(X, Y)))
        pylab.plot(X, Y, dot, color=colors[i], alpha=.99, label=name)
    pylab.legend(loc=2)
    pylab.show()

def pprint_solution(d, delfields=None):
    if not delfields:
        delfields = []
    else:
        delfields = delfields.split()
    delfields.extend("results features database_hash data_shuffle_seed config_version input_file_md5 marshalled_key git_info".split())
    delfields.extend("_id booked_at experiment_status train_percentage".split())
    d = copy.deepcopy(d)
    for name in delfields:
        if name in d:
            del d[name]
    pprint.pprint(d)

def group_by_strategy(xs):
    groups = defaultdict(list)
    normalizer = DictNormalizer()
    delnames = "results marshalled_key data_shuffle_seed train_percentage".split()
    for point in xs:
        d = copy.deepcopy(point)
        for name in delnames:
            del d[name]
        # "booked_at" and "_id" unhashable by the normalizer:
        if "booked_at" in d:
            del d["booked_at"]
        if "_id" in d:
            del d["_id"]
        d = normalizer(d)
        key = json.dumps(d, sort_keys=True)
        groups[key].append(point)
    return dict(groups)

def average_stats(xs):
    groups = defaultdict(list)
    normalizer = DictNormalizer()
    for point in xs:
        d = copy.deepcopy(point)
        del d["results"]
        del d["marshalled_key"]
        del d["data_shuffle_seed"]
        # "booked_at" and "_id" unhashable by the normalizer:
        if "booked_at" in d:
            del d["booked_at"]
        if "_id" in d:
            del d["_id"]
        d = normalizer(d)
        key = json.dumps(d, sort_keys=True)
        groups[key].append(point)
    xs = []
    for ys in groups.values():
        if len(ys) < 10:
            continue
        x = copy.deepcopy(ys[0])
        for name in ["f1", "precision", "recall", "accuracy"]:
            values = [y["results"][name] for y in ys]
            avg = sum(values) / len(values)
            var = sum((y - avg) ** 2 for y in values) / (len(values) - 1)
            del x["results"][name]
            x["results"][name + "_avg"] = avg
            x["results"][name + "_var"] = var
        xs.append(x)
    return xs

def rday(r):
    t = r[u'results'][u'start_time']
    t = time.gmtime(t)
    return (t.tm_year, t.tm_mon, t.tm_mday)

def rdatasize(r):
    return r[u'results'][u'train_size'] + r[u'results'][u'test_size']

def rkey(r):
    return (r[u'config_version'],
            r[u'git_info'],
            r[u"input_file_md5"],
            tuple(r[u"database_hash"]))
#            rday(r))

# francolq's:

# print the results for a specific strategy, ordered by train_percentage:
def pprint_strategy_result(l):
    print '%\ttp\tP\tR\tF1'
    for p in sorted(l, key=lambda p: p[u'train_percentage']):
        print '{}\t{}\t{:.2}\t{:.2}\t{:.2}'.format(p[u'train_percentage'], p[u'results'][u'true_positives'], p[u'results'][u'precision_avg'], p[u'results'][u'recall_avg'], p[u'results'][u'f1_avg'])
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
###############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2001-2015 Micronaet S.r.l. (<http://www.micronaet.it>)
#
# This program 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.
#
# This program 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
###############################################################################

import os
import sys
import logging
import openerp
import openerp.netsvc as netsvc
import openerp.addons.decimal_precision as dp
from openerp.osv import fields, osv, expression, orm
from datetime import datetime, timedelta
from dateutil.relativedelta import relativedelta
from openerp import SUPERUSER_ID
from openerp import tools
from openerp.tools.translate import _
from openerp.tools.float_utils import float_round as round
from openerp.tools import (DEFAULT_SERVER_DATE_FORMAT,
    DEFAULT_SERVER_DATETIME_FORMAT, DATETIME_FORMATS_MAP, float_compare)

_logger = logging.getLogger(__name__)

class account_analytic_account(orm.Model):
    ''' Add extra info for account
    '''
    _inherit = 'account.analytic.account'

    # Utility: extract Excel account intervent block:
    def extract_excel_status(self, cr, uid, month, year, account,
            context=None):
        ''' Extract XLS status for intervent for this month and for
            this account analitic
        '''
        # Pool used:
        ts_pool = self.pool.get('hr.analytic.timesheet')

        # ---------------------------------------------------------------------
        # Excel setup:
        # ---------------------------------------------------------------------
        WS_ids = {} # For extract all account
        WS_row = {} # Position row
        WS_name = _('Fatturazione extra')
        WS_header = [
            'ID', 'Cliente', 'Data',
            'Numero', 'Utente', 'Oggetto', 'Descrizione',
            'Fatturazione', 'H tot.', 'H fatt.', # da inserire
            ]
        WS_header_width = [
            1, 30, 20,
            10, 20, 30, 50,
            20, 10, 10,
            ]

        # ---------------------------------------------------------------------
        # Date filter for intervent selection:
        # ---------------------------------------------------------------------
        # Start or end month:
        from_date = '%s-%s-01' % (year, month)
        date_dt = datetime.strptime(from_date, DEFAULT_SERVER_DATE_FORMAT)
        date_dt += relativedelta(months=1)
        to_date = '%s01' % date_dt.strftime(DEFAULT_SERVER_DATE_FORMAT)[:8]

        domain = [
            ('date_start', '>=', from_date),
            ('date_start', '<', to_date),
            ]
        if account:
            domain.append(
                ('account_id', '=', account.id)
                )

        ts_ids = ts_pool.search(cr, uid, domain, context=context)
        for intervent in sorted(
                ts_pool.browse(cr, uid, ts_ids, context=context),
                key=lambda x: (x.date_start, x.user_id.name)
                ):
            this_account = intervent.account_id
            if this_account in WS_ids:
                excel_pool = WS_ids[this_account]
            else:
                # Create pool for every Excel file:
                WS_ids[this_account] = self.pool.get('excel.writer')
                excel_pool = WS_ids[this_account]
                excel_pool.create_worksheet(WS_name)
                excel_pool.write_xls_line(WS_name, 0, WS_header)
                excel_pool.column_width(WS_name, WS_header_width)
                WS_row[this_account] = 0
            WS_row[this_account] += 1

            excel_pool.write_xls_line(WS_name, WS_row[this_account], [
                intervent.id,
                intervent.intervent_partner_id.name,
                intervent.date_start,
                intervent.ref,
                intervent.user_id.name,
                intervent.name,
                intervent.to_invoice.name,
                intervent.intervention,
                intervent.unit_amount,
                '', # da inserire
                ])
        if account:
            return WS_ids[account].return_attachment(
                cr, uid, 'Interventi da valutare',
                'intervent.xlsx', version='7.0', context=context)

        # else: all will be saved in folder:
        config_pool = self.pool.get('ir.config_parameter')
        config_ids = config_pool.search(cr, uid, [
            ('key', '=', 'intervention_export_root_folder')], context=context)
        if config_ids:
            config_browse = config_pool.browse(cr, uid, config_ids,
                context=context)[0]
            root_folder = config_browse.value
        else:
            raise osv.except_osv(
                _('Parameter error'),
                _('Not found: intervention_export_root_folder parameter!'),
                )
        month_folder = os.path.expanduser(
            os.path.join(root_folder, '%s_%s' % (
                year, month)))
        os.system('mkdir -p %s' % month_folder)
        for account, excel_pool in WS_ids.iteritems():
            excel_pool.save_file_as(os.path.join(
                month_folder,
                account.name,
                ))
        return True

    def onchange_cost_parameter(self, cr, uid, ids, hour_cost, total_amount,
            total_hours, field='hour_cost', context=None):
        ''' Calculate 2 value depend on change element
        '''
        if field == 'total_amount':
            if total_hours:
                hour_cost = total_amount / total_hours
            elif hour_cost:
                total_hours = total_amount / hour_cost
            else: # hour_cost = 0
                total_hours = 0
        elif field == 'total_hours':
            if hour_cost:
                total_amount = hour_cost * total_hours
            else: # no hour_cost
                if total_hours:
                    hour_cost = total_amount / total_hours
                else:
                    hour_cost = 0
        else: # 'hour_cost':
            if total_hours:
                total_amount = total_hours * hour_cost
            else:
                if hour_cost:
                    total_hours = total_amount / hour_cost
                else:
                    total_hours = 0
        return {
            'value': {
                'total_amount': total_amount,
                'total_hours': total_hours,
                'hour_cost': hour_cost,
                },
            }

    # -------------------------------------------------------------------------
    # Function fields:
    # -------------------------------------------------------------------------
    def _get_done_hour_total(self, cr, uid, ids, fields, args, context=None):
        ''' Fields function for calculate
        '''
        res = dict.fromkeys(ids, 0)

        ts_pool = self.pool.get('hr.analytic.timesheet')
        ts_ids = ts_pool.search(cr, uid, [
            ('account_id', 'in', ids),
            ], context=context)

        # Calculate intervent marked hours:
        for ts in ts_pool.browse(cr, uid, ts_ids, context=context):
            invoiced_hour = ts_pool.get_total_h_2_invoice(ts, no_factor=True)
            account_id = ts.account_id.id
            res[account_id] += invoiced_hour

        # Remove invoiced hours:
        for account in self.browse(cr, uid, ids, context=context):
            for invoice in account.invoice_ids:
                res[account.id] -= invoice.hour_removed
        return res

    _columns = {
        # Amount:
        'total_amount': fields.float('Total amount',
            digits=(16, 2),
            help='Total amount of contract'),
        'hour_cost': fields.float('Hour cost',
            digits=(16, 2),
            help='Hour cost (total / number of hours)'),
        'hour_cost_customer': fields.related(
            'partner_id', 'hour_cost', type='float', digits=(16, 2),
            string='Customer hour cost',
            help='Remember starndar customer hour cost'),

        'hour_done': fields.function(
            _get_done_hour_total, method=True,
            type='float', string='Done hour', store=False),

        # Date period:
        'from_date': fields.date('From'),
        'to_date': fields.date('To'),
        }

class account_analytic_account_distribution(orm.Model):
    ''' Distribution of hour
    '''
    _name = 'account.analytic.account.distribution'
    _description = 'User distribution for contract'
    _rec_name = 'user_id'

    def _get_total_hours(self, cr, uid, ids, fields, args, context=None):
        ''' Fields function for calculate
        '''
        res = {}
        for dist in self.browse(cr, uid, ids, context=context):
            total_hours = dist.account_id.total_hours
            if total_hours:
                res[dist.id] = dist.percentual * total_hours / 100.0
            else:
                res[dist.id] = 0
        return res

    _columns = {
        'user_id': fields.many2one('res.users', 'Users', required=True),
        'account_id': fields.many2one(
            'account.analytic.account', 'Account'),
        'percentual': fields.float('% of hours', required=True,
            digits=(8, 2), help='Percentual on total hour'),
        'hour': fields.function(
            _get_total_hours, method=True,
            type='float', string='Tot. H.', store=False, readonly=True),
        }

class account_analytic_account_invoice(orm.Model):
    ''' Invoice for account
    '''
    _name = 'account.analytic.account.invoice'
    _description = 'Invoice extra for contract'
    _rec_name = 'name'

    def extract_excel_status(self, cr, uid, ids, context=None):
        ''' Extract XLS status for intervent for this month and for
            this account analitic
        '''
        assert len(ids) == 1, 'Works only with one record a time'

        # Read data from invoice:
        current_proxy = self.browse(cr, uid, ids, context=context)[0]
        date = current_proxy.date

        account_pool = self.pool.get('account.analytic.account')
        return account_pool.extract_excel_status(cr, uid,
            date[5:7], date[:4],
            current_proxy.account_id, # browse
            context=context)

    _columns = {
        'date': fields.date('Date', required=True),
        'name': fields.char('Invoice ref.', size=40, required=True),
        'hour_removed': fields.float(
            'Hour covered', digits=(16, 2), required=True,
            help='Number of hours coveder (removed from contract)'),
        'hour': fields.float(
            'Hour invoiced', digits=(16, 2), required=True,
            help='Number of hours invoiced (sometimes is not all)'),
        'hour_cost': fields.float('Hour cost', digits=(16, 2),
            help='Number of hours'),
        'total_amount': fields.float('Total amount', digits=(16, 2)),

        'account_id': fields.many2one('account.analytic.account', 'Account',
            required=True),
        'invoice_id': fields.many2one('account.invoice', 'Invoice'),
        #'date_invoice': fields.related(
        #    'invoice_id', 'date_invoice', 'Invoice date', type='date'),
        #'note': fields.text('Note'),
        }

class account_analytic_account(orm.Model):
    ''' Add *many relation
    '''
    _inherit = 'account.analytic.account'

    _columns = {
        'distribution_ids': fields.one2many(
            'account.analytic.account.distribution', 'account_id',
            'Distribution'),
        'invoice_ids': fields.one2many(
            'account.analytic.account.invoice', 'account_id',
            'Invoice'),
        }

class account_invoice(orm.Model):
    ''' Add *many relation
    '''
    _inherit = 'account.invoice'

    _columns = {
        'analytic_invoice_ids': fields.one2many(
            'account.analytic.account.invoice', 'invoice_id',
            'Analytic Invoice'),
        }
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
<commit_msg>generic bugfix (fast message)<commit_after># -*- coding: utf-8 -*-
###############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2001-2015 Micronaet S.r.l. (<http://www.micronaet.it>)
#
# This program 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.
#
# This program 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
###############################################################################

import os
import sys
import logging
import openerp
import openerp.netsvc as netsvc
import openerp.addons.decimal_precision as dp
from openerp.osv import fields, osv, expression, orm
from datetime import datetime, timedelta
from dateutil.relativedelta import relativedelta
from openerp import SUPERUSER_ID
from openerp import tools
from openerp.tools.translate import _
from openerp.tools.float_utils import float_round as round
from openerp.tools import (DEFAULT_SERVER_DATE_FORMAT,
    DEFAULT_SERVER_DATETIME_FORMAT, DATETIME_FORMATS_MAP, float_compare)

_logger = logging.getLogger(__name__)

class account_analytic_account(orm.Model):
    ''' Add extra info for account
    '''
    _inherit = 'account.analytic.account'

    # Utility: extract Excel account intervent block:
    def extract_excel_status(self, cr, uid, month, year, account,
            context=None):
        ''' Extract XLS status for intervent for this month and for
            this account analitic
        '''
        # Pool used:
        import pdb; pdb.set_trace()
        ts_pool = self.pool.get('hr.analytic.timesheet')

        # ---------------------------------------------------------------------
        # Excel setup:
        # ---------------------------------------------------------------------
        WS_ids = {} # For extract all account
        WS_row = {} # Position row
        WS_name = _('Fatturazione extra')
        WS_header = [
            'ID', 'Cliente', 'Data',
            'Numero', 'Utente', 'Oggetto', 'Descrizione',
            'Fatturazione', 'H tot.', 'H fatt.', # da inserire
            ]
        WS_header_width = [
            1, 30, 20,
            10, 20, 30, 50,
            20, 10, 10,
            ]

        # ---------------------------------------------------------------------
        # Date filter for intervent selection:
        # ---------------------------------------------------------------------
        # Start or end month:
        from_date = '%s-%s-01' % (year, month)
        date_dt = datetime.strptime(from_date, DEFAULT_SERVER_DATE_FORMAT)
        date_dt += relativedelta(months=1)
        to_date = '%s01' % date_dt.strftime(DEFAULT_SERVER_DATE_FORMAT)[:8]

        domain = [
            ('date_start', '>=', from_date),
            ('date_start', '<', to_date),
            ]
        if account:
            domain.append(
                ('account_id', '=', account.id)
                )

        ts_ids = ts_pool.search(cr, uid, domain, context=context)
        for intervent in sorted(
                ts_pool.browse(cr, uid, ts_ids, context=context),
                key=lambda x: (x.date_start, x.user_id.name)
                ):
            this_account = intervent.account_id
            if this_account in WS_ids:
                excel_pool = WS_ids[this_account]
            else:
                # Create pool for every Excel file:
                WS_ids[this_account] = self.pool.get('excel.writer')

                # Add header once:
                excel_pool = WS_ids[this_account]
                excel_pool.create_worksheet(WS_name)
                excel_pool.write_xls_line(WS_name, 0, WS_header)
                excel_pool.column_width(WS_name, WS_header_width)
                WS_row[this_account] = 0
            WS_row[this_account] += 1

            excel_pool.write_xls_line(WS_name, WS_row[this_account], [
                intervent.id,
                intervent.intervent_partner_id.name,
                intervent.date_start,
                intervent.ref,
                intervent.user_id.name,
                intervent.name,
                intervent.to_invoice.name,
                intervent.intervention,
                intervent.unit_amount,
                '', # da inserire
                ])
        if account:
            return WS_ids[account].return_attachment(
                cr, uid, 'Interventi da valutare',
                'intervent.xlsx', version='7.0', context=context)

        # else: all will be saved in folder:
        config_pool = self.pool.get('ir.config_parameter')
        config_ids = config_pool.search(cr, uid, [
            ('key', '=', 'intervention_export_root_folder')], context=context)
        if config_ids:
            config_browse = config_pool.browse(cr, uid, config_ids,
                context=context)[0]
            root_folder = config_browse.value
        else:
            raise osv.except_osv(
                _('Parameter error'),
                _('Not found: intervention_export_root_folder parameter!'),
                )
        month_folder = os.path.expanduser(
            os.path.join(root_folder, '%s_%s' % (
                year, month)))
        os.system('mkdir -p %s' % month_folder)
        for account, excel_pool in WS_ids.iteritems():
            excel_pool.save_file_as(os.path.join(
                month_folder,
                account.name,
                ))
        return True

    def onchange_cost_parameter(self, cr, uid, ids, hour_cost, total_amount,
            total_hours, field='hour_cost', context=None):
        ''' Calculate 2 value depend on change element
        '''
        if field == 'total_amount':
            if total_hours:
                hour_cost = total_amount / total_hours
            elif hour_cost:
                total_hours = total_amount / hour_cost
            else: # hour_cost = 0
                total_hours = 0
        elif field == 'total_hours':
            if hour_cost:
                total_amount = hour_cost * total_hours
            else: # no hour_cost
                if total_hours:
                    hour_cost = total_amount / total_hours
                else:
                    hour_cost = 0
        else: # 'hour_cost':
            if total_hours:
                total_amount = total_hours * hour_cost
            else:
                if hour_cost:
                    total_hours = total_amount / hour_cost
                else:
                    total_hours = 0
        return {
            'value': {
                'total_amount': total_amount,
                'total_hours': total_hours,
                'hour_cost': hour_cost,
                },
            }

    # -------------------------------------------------------------------------
    # Function fields:
    # -------------------------------------------------------------------------
    def _get_done_hour_total(self, cr, uid, ids, fields, args, context=None):
        ''' Fields function for calculate
        '''
        res = dict.fromkeys(ids, 0)

        ts_pool = self.pool.get('hr.analytic.timesheet')
        ts_ids = ts_pool.search(cr, uid, [
            ('account_id', 'in', ids),
            ], context=context)

        # Calculate intervent marked hours:
        for ts in ts_pool.browse(cr, uid, ts_ids, context=context):
            invoiced_hour = ts_pool.get_total_h_2_invoice(ts, no_factor=True)
            account_id = ts.account_id.id
            res[account_id] += invoiced_hour

        # Remove invoiced hours:
        for account in self.browse(cr, uid, ids, context=context):
            for invoice in account.invoice_ids:
                res[account.id] -= invoice.hour_removed
        return res

    _columns = {
        # Amount:
        'total_amount': fields.float('Total amount',
            digits=(16, 2),
            help='Total amount of contract'),
        'hour_cost': fields.float('Hour cost',
            digits=(16, 2),
            help='Hour cost (total / number of hours)'),
        'hour_cost_customer': fields.related(
            'partner_id', 'hour_cost', type='float', digits=(16, 2),
            string='Customer hour cost',
            help='Remember starndar customer hour cost'),

        'hour_done': fields.function(
            _get_done_hour_total, method=True,
            type='float', string='Done hour', store=False),

        # Date period:
        'from_date': fields.date('From'),
        'to_date': fields.date('To'),
        }

class account_analytic_account_distribution(orm.Model):
    ''' Distribution of hour
    '''
    _name = 'account.analytic.account.distribution'
    _description = 'User distribution for contract'
    _rec_name = 'user_id'

    def _get_total_hours(self, cr, uid, ids, fields, args, context=None):
        ''' Fields function for calculate
        '''
        res = {}
        for dist in self.browse(cr, uid, ids, context=context):
            total_hours = dist.account_id.total_hours
            if total_hours:
                res[dist.id] = dist.percentual * total_hours / 100.0
            else:
                res[dist.id] = 0
        return res

    _columns = {
        'user_id': fields.many2one('res.users', 'Users', required=True),
        'account_id': fields.many2one(
            'account.analytic.account', 'Account'),
        'percentual': fields.float('% of hours', required=True,
            digits=(8, 2), help='Percentual on total hour'),
        'hour': fields.function(
            _get_total_hours, method=True,
            type='float', string='Tot. H.', store=False, readonly=True),
        }

class account_analytic_account_invoice(orm.Model):
    ''' Invoice for account
    '''
    _name = 'account.analytic.account.invoice'
    _description = 'Invoice extra for contract'
    _rec_name = 'name'

    def extract_excel_status(self, cr, uid, ids, context=None):
        ''' Extract XLS status for intervent for this month and for
            this account analitic
        '''
        assert len(ids) == 1, 'Works only with one record a time'

        # Read data from invoice:
        current_proxy = self.browse(cr, uid, ids, context=context)[0]
        date = current_proxy.date

        account_pool = self.pool.get('account.analytic.account')
        return account_pool.extract_excel_status(cr, uid,
            date[5:7], date[:4],
            current_proxy.account_id, # browse
            context=context)

    _columns = {
        'date': fields.date('Date', required=True),
        'name': fields.char('Invoice ref.', size=40, required=True),
        'hour_removed': fields.float(
            'Hour covered', digits=(16, 2), required=True,
            help='Number of hours coveder (removed from contract)'),
        'hour': fields.float(
            'Hour invoiced', digits=(16, 2), required=True,
            help='Number of hours invoiced (sometimes is not all)'),
        'hour_cost': fields.float('Hour cost', digits=(16, 2),
            help='Number of hours'),
        'total_amount': fields.float('Total amount', digits=(16, 2)),

        'account_id': fields.many2one('account.analytic.account', 'Account',
            required=True),
        'invoice_id': fields.many2one('account.invoice', 'Invoice'),
        #'date_invoice': fields.related(
        #    'invoice_id', 'date_invoice', 'Invoice date', type='date'),
        #'note': fields.text('Note'),
        }

class account_analytic_account(orm.Model):
    ''' Add *many relation
    '''
    _inherit = 'account.analytic.account'

    _columns = {
        'distribution_ids': fields.one2many(
            'account.analytic.account.distribution', 'account_id',
            'Distribution'),
        'invoice_ids': fields.one2many(
            'account.analytic.account.invoice', 'account_id',
            'Invoice'),
        }

class account_invoice(orm.Model):
    ''' Add *many relation
    '''
    _inherit = 'account.invoice'

    _columns = {
        'analytic_invoice_ids': fields.one2many(
            'account.analytic.account.invoice', 'invoice_id',
            'Analytic Invoice'),
        }
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
<|endoftext|>


<commit_before># coding: utf-8

"""
BpmApi.py
Copyright 2015 SmartBear Software

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
"""

from __future__ import absolute_import

import sys
import os

# python 2 and python 3 compatibility library
from six import iteritems

from ..configuration import Configuration
from ..api_client import ApiClient

class BpmApi(object):
    """
    NOTE: This class is auto generated by the swagger code generator program.
    Do not edit the class manually.
    Ref: https://github.com/swagger-api/swagger-codegen
    """

    def __init__(self, api_client=None):
        config = Configuration()
        if api_client:
            self.api_client = api_client
        else:
            if not config.api_client:
                config.api_client = ApiClient()
            self.api_client = config.api_client

    def get_bpm_task_by_id(self, task_id, **kwargs):
        """
        Get single (recently) active BPM task.

        This method makes a synchronous HTTP request by default. To make an
        asynchronous HTTP request, please define a `callback` function
        to be invoked when receiving the response.
        >>> def callback_function(response):
        >>>     pprint(response)
        >>>
        >>> thread = api.get_bpm_task_by_id(task_id, callback=callback_function)

        :param callback function: The callback function
            for asynchronous request. (optional)
        :param int task_id: BPM task ID (required)
        :return: BpmTaskRestSingleton
                 If the method is called asynchronously,
                 returns the request thread.
        """
        # verify the required parameter 'task_id' is set
        if task_id is None:
            raise ValueError("Missing the required parameter `task_id` when calling `get_bpm_task_by_id`")

        all_params = ['task_id']
        all_params.append('callback')

        params = locals()
        for key, val in iteritems(params['kwargs']):
            if key not in all_params:
                raise TypeError(
                    "Got an unexpected keyword argument '%s'"
                    " to method get_bpm_task_by_id" % key
                )
            params[key] = val
        del params['kwargs']

        resource_path = '/bpm/tasks/{taskId}'.replace('{format}', 'json')
        method = 'GET'

        path_params = {}
        if 'task_id' in params:
            path_params['taskId'] = params['task_id']

        query_params = {}

        header_params = {}

        form_params = {}
        files = {}

        body_params = None

        # HTTP header `Accept`
        header_params['Accept'] = self.api_client.\
            select_header_accept(['application/json'])
        if not header_params['Accept']:
            del header_params['Accept']

        # HTTP header `Content-Type`
        header_params['Content-Type'] = self.api_client.\
            select_header_content_type(['application/json'])

        # Authentication setting
        auth_settings = []

        response = self.api_client.call_api(resource_path, method,
                                            path_params,
                                            query_params,
                                            header_params,
                                            body=body_params,
                                            post_params=form_params,
                                            files=files,
                                            response_type='BpmTaskRestSingleton',
                                            auth_settings=auth_settings,
                                            callback=params.get('callback'))
        return response

    def get_bpm_tasks(self, **kwargs):
        """
        List of (recently) active BPM tasks.

        This method makes a synchronous HTTP request by default. To make an
        asynchronous HTTP request, please define a `callback` function
        to be invoked when receiving the response.
        >>> def callback_function(response):
        >>>     pprint(response)
        >>>
        >>> thread = api.get_bpm_tasks(callback=callback_function)

        :param callback function: The callback function
            for asynchronous request. (optional)
        :param int page_index: Page Index
        :param int page_size: Pagination size
        :return: BpmTaskRestPage
                 If the method is called asynchronously,
                 returns the request thread.
        """

        all_params = ['page_index', 'page_size']
        all_params.append('callback')

        params = locals()
        for key, val in iteritems(params['kwargs']):
            if key not in all_params:
                raise TypeError(
                    "Got an unexpected keyword argument '%s'"
                    " to method get_bpm_tasks" % key
                )
            params[key] = val
        del params['kwargs']

        resource_path = '/bpm/tasks'.replace('{format}', 'json')
        method = 'GET'

        path_params = {}

        query_params = {}
        if 'page_index' in params:
            query_params['pageIndex'] = params['page_index']
        if 'page_size' in params:
            query_params['pageSize'] = params['page_size']

        header_params = {}

        form_params = {}
        files = {}

        body_params = None

        # HTTP header `Accept`
        header_params['Accept'] = self.api_client.\
            select_header_accept(['application/json'])
        if not header_params['Accept']:
            del header_params['Accept']

        # HTTP header `Content-Type`
        header_params['Content-Type'] = self.api_client.\
            select_header_content_type(['application/json'])

        # Authentication setting
        auth_settings = []

        response = self.api_client.call_api(resource_path, method,
                                            path_params,
                                            query_params,
                                            header_params,
                                            body=body_params,
                                            post_params=form_params,
                                            files=files,
                                            response_type='BpmTaskRestPage',
                                            auth_settings=auth_settings,
                                            callback=params.get('callback'))
        return response

    def notify_task(self, task_id, **kwargs):
        """
        Notify PNC about a BPM task event. Accepts polymorphic JSON {\"eventType\": \"string\"} based on \"eventType\" field.

        This method makes a synchronous HTTP request by default. To make an
        asynchronous HTTP request, please define a `callback` function
        to be invoked when receiving the response.
        >>> def callback_function(response):
        >>>     pprint(response)
        >>>
        >>> thread = api.notify_task(task_id, callback=callback_function)

        :param callback function: The callback function
            for asynchronous request. (optional)
        :param int task_id: BPM task ID (required)
        :return: None
                 If the method is called asynchronously,
                 returns the request thread.
        """
        # verify the required parameter 'task_id' is set
        if task_id is None:
            raise ValueError("Missing the required parameter `task_id` when calling `notify_task`")

        all_params = ['task_id']
        all_params.append('callback')

        params = locals()
        for key, val in iteritems(params['kwargs']):
            if key not in all_params:
                raise TypeError(
                    "Got an unexpected keyword argument '%s'"
                    " to method notify_task" % key
                )
            params[key] = val
        del params['kwargs']

        resource_path = '/bpm/tasks/{taskId}/notify'.replace('{format}', 'json')
        method = 'POST'

        path_params = {}
        if 'task_id' in params:
            path_params['taskId'] = params['task_id']

        query_params = {}

        header_params = {}

        form_params = {}
        files = {}

        body_params = None

        # HTTP header `Accept`
        header_params['Accept'] = self.api_client.\
            select_header_accept(['application/json'])
        if not header_params['Accept']:
            del header_params['Accept']

        # HTTP header `Content-Type`
        header_params['Content-Type'] = self.api_client.\
            select_header_content_type(['application/json'])

        # Authentication setting
        auth_settings = []

        response = self.api_client.call_api(resource_path, method,
                                            path_params,
                                            query_params,
                                            header_params,
                                            body=body_params,
                                            post_params=form_params,
                                            files=files,
                                            response_type=None,
                                            auth_settings=auth_settings,
                                            callback=params.get('callback'))
        return response

    def start_bc_creation_task(self, body, **kwargs):
        """
        Start BC creation task.

        This method makes a synchronous HTTP request by default. To make an
        asynchronous HTTP request, please define a `callback` function
        to be invoked when receiving the response.
        >>> def callback_function(response):
        >>>     pprint(response)
        >>>
        >>> thread = api.start_bc_creation_task(body, callback=callback_function)

        :param callback function: The callback function
            for asynchronous request. (optional)
        :param BpmBuildConfigurationCreationRest body: Task parameters. (required)
        :return: None
                 If the method is called asynchronously,
                 returns the request thread.
        """
        # verify the required parameter 'body' is set
        if body is None:
            raise ValueError("Missing the required parameter `body` when calling `start_bc_creation_task`")

        all_params = ['body']
        all_params.append('callback')

        params = locals()
        for key, val in iteritems(params['kwargs']):
            if key not in all_params:
                raise TypeError(
                    "Got an unexpected keyword argument '%s'"
                    " to method start_bc_creation_task" % key
                )
            params[key] = val
        del params['kwargs']

        resource_path = '/bpm/tasks/start-build-configuration-creation'.replace('{format}', 'json')
        method = 'POST'

        path_params = {}

        query_params = {}

        header_params = {}

        form_params = {}
        files = {}

        body_params = None
        if 'body' in params:
            body_params = params['body']

        # HTTP header `Accept`
        header_params['Accept'] = self.api_client.\
            select_header_accept(['application/json'])
        if not header_params['Accept']:
            del header_params['Accept']

        # HTTP header `Content-Type`
        header_params['Content-Type'] = self.api_client.\
            select_header_content_type(['application/json'])

        # Authentication setting
        auth_settings = []

        response = self.api_client.call_api(resource_path, method,
                                            path_params,
                                            query_params,
                                            header_params,
                                            body=body_params,
                                            post_params=form_params,
                                            files=files,
                                            response_type=None,
                                            auth_settings=auth_settings,
                                            callback=params.get('callback'))
        return response
<commit_msg>reapply swagger returntype fix for start_bc_creation_task<commit_after># coding: utf-8

"""
BpmApi.py
Copyright 2015 SmartBear Software

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
"""

from __future__ import absolute_import

import sys
import os

# python 2 and python 3 compatibility library
from six import iteritems

from ..configuration import Configuration
from ..api_client import ApiClient

class BpmApi(object):
    """
    NOTE: This class is auto generated by the swagger code generator program.
    Do not edit the class manually.
    Ref: https://github.com/swagger-api/swagger-codegen
    """

    def __init__(self, api_client=None):
        config = Configuration()
        if api_client:
            self.api_client = api_client
        else:
            if not config.api_client:
                config.api_client = ApiClient()
            self.api_client = config.api_client

    def get_bpm_task_by_id(self, task_id, **kwargs):
        """
        Get single (recently) active BPM task.

        This method makes a synchronous HTTP request by default. To make an
        asynchronous HTTP request, please define a `callback` function
        to be invoked when receiving the response.
        >>> def callback_function(response):
        >>>     pprint(response)
        >>>
        >>> thread = api.get_bpm_task_by_id(task_id, callback=callback_function)

        :param callback function: The callback function
            for asynchronous request. (optional)
        :param int task_id: BPM task ID (required)
        :return: BpmTaskRestSingleton
                 If the method is called asynchronously,
                 returns the request thread.
        """
        # verify the required parameter 'task_id' is set
        if task_id is None:
            raise ValueError("Missing the required parameter `task_id` when calling `get_bpm_task_by_id`")

        all_params = ['task_id']
        all_params.append('callback')

        params = locals()
        for key, val in iteritems(params['kwargs']):
            if key not in all_params:
                raise TypeError(
                    "Got an unexpected keyword argument '%s'"
                    " to method get_bpm_task_by_id" % key
                )
            params[key] = val
        del params['kwargs']

        resource_path = '/bpm/tasks/{taskId}'.replace('{format}', 'json')
        method = 'GET'

        path_params = {}
        if 'task_id' in params:
            path_params['taskId'] = params['task_id']

        query_params = {}

        header_params = {}

        form_params = {}
        files = {}

        body_params = None

        # HTTP header `Accept`
        header_params['Accept'] = self.api_client.\
            select_header_accept(['application/json'])
        if not header_params['Accept']:
            del header_params['Accept']

        # HTTP header `Content-Type`
        header_params['Content-Type'] = self.api_client.\
            select_header_content_type(['application/json'])

        # Authentication setting
        auth_settings = []

        response = self.api_client.call_api(resource_path, method,
                                            path_params,
                                            query_params,
                                            header_params,
                                            body=body_params,
                                            post_params=form_params,
                                            files=files,
                                            response_type='BpmTaskRestSingleton',
                                            auth_settings=auth_settings,
                                            callback=params.get('callback'))
        return response

    def get_bpm_tasks(self, **kwargs):
        """
        List of (recently) active BPM tasks.

        This method makes a synchronous HTTP request by default. To make an
        asynchronous HTTP request, please define a `callback` function
        to be invoked when receiving the response.
        >>> def callback_function(response):
        >>>     pprint(response)
        >>>
        >>> thread = api.get_bpm_tasks(callback=callback_function)

        :param callback function: The callback function
            for asynchronous request. (optional)
        :param int page_index: Page Index
        :param int page_size: Pagination size
        :return: BpmTaskRestPage
                 If the method is called asynchronously,
                 returns the request thread.
        """

        all_params = ['page_index', 'page_size']
        all_params.append('callback')

        params = locals()
        for key, val in iteritems(params['kwargs']):
            if key not in all_params:
                raise TypeError(
                    "Got an unexpected keyword argument '%s'"
                    " to method get_bpm_tasks" % key
                )
            params[key] = val
        del params['kwargs']

        resource_path = '/bpm/tasks'.replace('{format}', 'json')
        method = 'GET'

        path_params = {}

        query_params = {}
        if 'page_index' in params:
            query_params['pageIndex'] = params['page_index']
        if 'page_size' in params:
            query_params['pageSize'] = params['page_size']

        header_params = {}

        form_params = {}
        files = {}

        body_params = None

        # HTTP header `Accept`
        header_params['Accept'] = self.api_client.\
            select_header_accept(['application/json'])
        if not header_params['Accept']:
            del header_params['Accept']

        # HTTP header `Content-Type`
        header_params['Content-Type'] = self.api_client.\
            select_header_content_type(['application/json'])

        # Authentication setting
        auth_settings = []

        response = self.api_client.call_api(resource_path, method,
                                            path_params,
                                            query_params,
                                            header_params,
                                            body=body_params,
                                            post_params=form_params,
                                            files=files,
                                            response_type='BpmTaskRestPage',
                                            auth_settings=auth_settings,
                                            callback=params.get('callback'))
        return response

    def notify_task(self, task_id, **kwargs):
        """
        Notify PNC about a BPM task event. Accepts polymorphic JSON {\"eventType\": \"string\"} based on \"eventType\" field.

        This method makes a synchronous HTTP request by default. To make an
        asynchronous HTTP request, please define a `callback` function
        to be invoked when receiving the response.
        >>> def callback_function(response):
        >>>     pprint(response)
        >>>
        >>> thread = api.notify_task(task_id, callback=callback_function)

        :param callback function: The callback function
            for asynchronous request. (optional)
        :param int task_id: BPM task ID (required)
        :return: None
                 If the method is called asynchronously,
                 returns the request thread.
        """
        # verify the required parameter 'task_id' is set
        if task_id is None:
            raise ValueError("Missing the required parameter `task_id` when calling `notify_task`")

        all_params = ['task_id']
        all_params.append('callback')

        params = locals()
        for key, val in iteritems(params['kwargs']):
            if key not in all_params:
                raise TypeError(
                    "Got an unexpected keyword argument '%s'"
                    " to method notify_task" % key
                )
            params[key] = val
        del params['kwargs']

        resource_path = '/bpm/tasks/{taskId}/notify'.replace('{format}', 'json')
        method = 'POST'

        path_params = {}
        if 'task_id' in params:
            path_params['taskId'] = params['task_id']

        query_params = {}

        header_params = {}

        form_params = {}
        files = {}

        body_params = None

        # HTTP header `Accept`
        header_params['Accept'] = self.api_client.\
            select_header_accept(['application/json'])
        if not header_params['Accept']:
            del header_params['Accept']

        # HTTP header `Content-Type`
        header_params['Content-Type'] = self.api_client.\
            select_header_content_type(['application/json'])

        # Authentication setting
        auth_settings = []

        response = self.api_client.call_api(resource_path, method,
                                            path_params,
                                            query_params,
                                            header_params,
                                            body=body_params,
                                            post_params=form_params,
                                            files=files,
                                            response_type=None,
                                            auth_settings=auth_settings,
                                            callback=params.get('callback'))
        return response

    def start_bc_creation_task(self, body, **kwargs):
        """
        Start BC creation task.

        This method makes a synchronous HTTP request by default. To make an
        asynchronous HTTP request, please define a `callback` function
        to be invoked when receiving the response.
        >>> def callback_function(response):
        >>>     pprint(response)
        >>>
        >>> thread = api.start_bc_creation_task(body, callback=callback_function)

        :param callback function: The callback function
            for asynchronous request. (optional)
        :param BpmBuildConfigurationCreationRest body: Task parameters. (required)
        :return: None
                 If the method is called asynchronously,
                 returns the request thread.
        """
        # verify the required parameter 'body' is set
        if body is None:
            raise ValueError("Missing the required parameter `body` when calling `start_bc_creation_task`")

        all_params = ['body']
        all_params.append('callback')

        params = locals()
        for key, val in iteritems(params['kwargs']):
            if key not in all_params:
                raise TypeError(
                    "Got an unexpected keyword argument '%s'"
                    " to method start_bc_creation_task" % key
                )
            params[key] = val
        del params['kwargs']

        resource_path = '/bpm/tasks/start-build-configuration-creation'.replace('{format}', 'json')
        method = 'POST'

        path_params = {}

        query_params = {}

        header_params = {}

        form_params = {}
        files = {}

        body_params = None
        if 'body' in params:
            body_params = params['body']

        # HTTP header `Accept`
        header_params['Accept'] = self.api_client.\
            select_header_accept(['application/json'])
        if not header_params['Accept']:
            del header_params['Accept']

        # HTTP header `Content-Type`
        header_params['Content-Type'] = self.api_client.\
            select_header_content_type(['application/json'])

        # Authentication setting
        auth_settings = []

        response = self.api_client.call_api(resource_path, method,
                                            path_params,
                                            query_params,
                                            header_params,
                                            body=body_params,
                                            post_params=form_params,
                                            files=files,
                                            response_type=int,
                                            auth_settings=auth_settings,
                                            callback=params.get('callback'))
        return response
<|endoftext|>


<commit_before><commit_msg>Add build links on the Active Releases page.<commit_after><|endoftext|>


<commit_before>from django.conf import settings
from django.db import models
from django.db.models import Q
from proso.django.models import ModelDiffMixin
from proso_models.models import Item, ItemRelation, Answer
from django.db.models.signals import pre_save, post_save, pre_delete
from django.dispatch import receiver
from proso.django.util import disable_for_loaddata
import logging

LOGGER = logging.getLogger('django.request')

class TermManager(models.Manager):

    def prepare_related(self):
        return self

class Term(models.Model, ModelDiffMixin):
    identifier = models.SlugField()
    item = models.ForeignKey(Item, null=True, default=None, related_name="flashcard_terms")

    lang = models.CharField(max_length=2)
    name = models.TextField()

    objects = TermManager()

    def to_json(self, nested=False):
        return {
            "id": self.pk,
            "identifier": self.identifier,
            "item_id": self.item_id,
            "object_type": "fc_term",
            "lang": self.lang,
            "name": self.name,
        }

    def __str__(self):
        return "{0.lang} - {0.name}".format(self)

class ContextManager(models.Manager):

    def prepare_related(self):
        return self

class Context(models.Model, ModelDiffMixin):
    identifier = models.SlugField()
    item = models.ForeignKey(Item, null=True, default=None, related_name="flashcard_contexts")

    lang = models.CharField(max_length=2)
    name = models.TextField(null=True, blank=True)
    content = models.TextField(null=True, blank=True)

    objects = ContextManager()

    def to_json(self, nested=False, with_content=True):
        json = {
            "id": self.pk,
            "identifier": self.identifier,
            "item_id": self.item_id,
            "object_type": "fc_context",
            "lang": self.lang,
            "name": self.name,
        }
        if with_content:
            json['content'] = self.content
        return json

    def __str__(self):
        return "{0.lang} - {0.name}".format(self)

class FlashcardManager(models.Manager):

    def prepare_related(self):
        return self.select_related(Flashcard.related_term(), Flashcard.related_context())

class Flashcard(models.Model, ModelDiffMixin):
    identifier = models.SlugField()
    item = models.ForeignKey(Item, null=True, default=None, related_name="flashcards")

    lang = models.CharField(max_length=2)
    term = models.ForeignKey(Term, related_name="flashcards")
    term_secondary = models.ForeignKey(Term, related_name="flashcards_as_secondary", null=True, blank=True, default=None)
    context = models.ForeignKey(Context, related_name="flashcards")
    description = models.TextField(null=True, blank=True)
    active = models.BooleanField(default=True)

    objects = FlashcardManager()

    def to_json(self, nested=False, contexts=True):
        data = {
            "id": self.pk,
            "identifier": self.identifier,
            "item_id": self.item_id,
            "object_type": "fc_flashcard",
            "active": self.active,
            "lang": self.lang,
            "term": self.get_term().to_json(nested=True),
        }
        if hasattr(self, "options"):
            data["options"] = [o.to_json(nested=True) for o in sorted(self.options, key=lambda f: f.term.name)]
        if hasattr(self, 'practice_meta'):
            data['practice_meta'] = self.practice_meta
        if not nested and contexts:
            data["context"] = self.get_context().to_json(nested=True)
        else:
            data["context_id"] = self.context_id
        if self.term_secondary is not None:
            data["term_secondary"] = self.get_term_secondary().to_json(nested=True)
        if self.description is not None:
            data['description'] = self.description
        return data

    def get_term(self):
        extension = settings.PROSO_FLASHCARDS.get("term_extension", None)
        if extension is None:
            return self.term
        else:
            return getattr(self.term, extension.__name__.lower())

    def get_term_secondary(self):
        extension = settings.PROSO_FLASHCARDS.get("term_extension", None)
        if extension is None:
            return self.term_secondary
        else:
            return None if self.term_secondary is None else getattr(self.term_secondary, extension.__name__.lower())

    def get_context(self):
        extension = settings.PROSO_FLASHCARDS.get("context_extension", None)
        if extension is None:
            return self.context
        else:
            return getattr(self.context, extension.__name__.lower())

    @staticmethod
    def related_term():
        extension = settings.PROSO_FLASHCARDS.get("term_extension", None)
        if extension is None:
            return "term"
        else:
            return "term__{}".format(extension.__name__.lower())

    @staticmethod
    def related_term_secondary():
        extension = settings.PROSO_FLASHCARDS.get("term_extension", None)
        if extension is None:
            return "term_secondary"
        else:
            return "term_secondary__{}".format(extension.__name__.lower())

    @staticmethod
    def related_context():
        extension = settings.PROSO_FLASHCARDS.get("context_extension", None)
        if extension is None:
            return "context"
        else:
            return "context__{}".format(extension.__name__.lower())

    def __str__(self):
        return "{0.term} - {0.context}".format(self)

class Category(models.Model, ModelDiffMixin):
    class Meta:
        verbose_name_plural = "categories"

    identifier = models.SlugField()
    item = models.ForeignKey(Item, null=True, default=None, related_name="flashcard_categories")
    lang = models.CharField(max_length=2)
    name = models.TextField()
    type = models.CharField(max_length=50, null=True, blank=True)

    def to_json(self, nested=False):
        return {
            "id": self.pk,
            "identifier": self.identifier,
            "item_id": self.item_id,
            "object_type": "fc_category",
            "lang": self.lang,
            "name": self.name,
            "type": self.type,
        }

    def __str__(self):
        return "{0.lang} - {0.name}".format(self)

class FlashcardAnswerManager(models.Manager):

    def prepare_related(self):
        return self.prefetch_related('options__{}'.format(Flashcard.related_term()))

    def from_json(self, json_object, practice_context, user_id, object_class=None):
        if object_class is None:
            object_class = FlashcardAnswer
        json_object = dict(json_object)
        flashcard_ids = set()
        flashcard_ids.add(json_object['flashcard_id'])
        if json_object.get('flashcard_answered_id') is not None:
            flashcard_ids.add(json_object['flashcard_answered_id'])
        if 'option_ids' in json_object:
            option_ids = set(json_object['option_ids'])
            if len(option_ids) < 1:
                raise Exception('If option_ids is given, it has to contain at least 1 items!')
            flashcard_ids |= option_ids
            if json_object['flashcard_id'] in option_ids:
                raise Exception('Option ids can not contain main flashcard id!')
            json_object['guess'] = 1.0 / (len(option_ids) + 1)
        else:
            json_object['guess'] = 0
        flashcards = {fc.id: fc for fc in Flashcard.objects.filter(pk__in=flashcard_ids)}
        if len(flashcard_ids) != len(flashcards):
            raise Exception("Invalid flashcard id (asked, answered or as option)")
        json_object['item_id'] = flashcards[json_object['flashcard_id']].item_id
        json_object['item_asked_id'] = flashcards[json_object['flashcard_id']].item_id
        json_object['item_answered_id'] = flashcards[json_object.get('flashcard_answered_id')].item_id if json_object.get('flashcard_answered_id') is not None else None
        json_object['lang'] = flashcards[json_object['flashcard_id']].lang
        answer = Answer.objects.from_json(json_object, practice_context, user_id, object_class=object_class)
        if 'option_ids' in json_object:
            for option_id in set(json_object['option_ids']):
                answer.options.add(flashcards[option_id])
        answer.save()
        return answer

class FlashcardAnswer(Answer):
    FROM_TERM = "t2d"
    FROM_DESCRIPTION = "d2t"
    FROM_TERM_TO_TERM_SECONDARY = 't2t2'
    FROM_TERM_SECONDARY_TO_TERM = 't22t'

    options = models.ManyToManyField(Flashcard, related_name="answers_with_this_as_option")

    objects = FlashcardAnswerManager()

    def to_json(self, nested=False):
        json = Answer.to_json(self)
        json['object_type'] = "fc_answer"
        if not nested:
            json["options"] = [flashcard.to_json(nested=True) for flashcard in self.options.all()]
        return json

PROSO_MODELS_TO_EXPORT = [Category, Flashcard, FlashcardAnswer,
                          settings.PROSO_FLASHCARDS.get("context_extension", Context),
                          settings.PROSO_FLASHCARDS.get("term_extension", Term)]

PROSO_CUSTOM_EXPORT = {
    'answer': '''
        SELECT
            proso_models_answer.*
        FROM proso_models_answer
        INNER JOIN proso_flashcards_flashcardanswer
            ON proso_models_answer.id = answer_ptr_id
    ''',
    'context': '''
        SELECT
            proso_flashcards_context.id,
            proso_flashcards_flashcard.item_id AS item_id,
            proso_flashcards_term.name AS term_name,
            proso_flashcards_context.name AS context_name,
            proso_flashcards_flashcard.lang AS language
        FROM proso_flashcards_flashcard
        INNER JOIN proso_flashcards_term ON term_id = proso_flashcards_term.id
        INNER JOIN proso_flashcards_context ON context_id = proso_flashcards_context.id
        WHERE proso_flashcards_term.lang = proso_flashcards_context.lang
    '''
}

def _get_value_without_negation(value):
    if isinstance(value, int):
        return abs(value), value < 0
    else:
        if value is not None and value.startswith('-'):
            return value[1:], True
        else:
            return value, False

def _create_q(column, value):
    value, negation = _get_value_without_negation(value)
    q = Q(**{column: value})
    return ~q if negation else q

@receiver(pre_save, sender=Term)
@receiver(pre_save, sender=Context)
@receiver(pre_save, sender=Flashcard)
@receiver(pre_save, sender=Category)
@disable_for_loaddata
def create_items(sender, instance, **kwargs):
    """
    When one of the defined objects is created, initialize also its item.
    """
    if instance.item_id is None and instance.item is None:
        item = Item()
        if hasattr(instance, 'active'):
            item.active = getattr(instance, 'active')
        item.save()
        instance.item = item

@receiver(pre_save, sender=Term)
@receiver(pre_save, sender=Context)
@receiver(pre_save, sender=Flashcard)
@receiver(pre_save, sender=Category)
@disable_for_loaddata
def change_activity(sender, instance, **kwargs):
    if 'active' in instance.diff:
        instance.item.active = instance.active
        instance.item.save()

@receiver(post_save, sender=Flashcard)
@disable_for_loaddata
def add_parent(sender, instance, **kwargs):
    """
    When a flashcard is created, create also an item relation.
    """
    if not kwargs['created']:
        return
    for att in ['term', 'term_secondary', 'context']:
        if getattr(instance, att) is None:
            continue
        parent = getattr(instance, att).item_id
        child = instance.item_id
        ItemRelation.objects.get_or_create(
            parent_id=parent,
            child_id=child,
            visible=True,
        )

@receiver(pre_save, sender=Flashcard)
@disable_for_loaddata
def change_parent(sender, instance, **kwargs):
    """
    When the given flashcard has changed. Look at term and context and change
    the corresponding item relation.
    """
    if instance.id is None:
        return
    if len({'term', 'term_id'} & set(instance.changed_fields)) != 0:
        diff = instance.diff
        parent = diff['term'][0] if 'term' in diff else diff['term_id'][0]
        parent_id = parent.item_id if isinstance(parent, Term) else Term.objects.get(pk=parent).item_id
        child_id = instance.item_id
        ItemRelation.objects.filter(parent_id=parent_id, child_id=child_id).delete()
        ItemRelation.objects.create(parent_id=instance.term.item_id, child_id=child_id, visible=True)
    if len({'term_secondary', 'term_secondary_id'} & set(instance.changed_fields)) != 0:
        diff = instance.diff
        child_id = instance.item_id
        parent = diff['term_secondary'][0] if 'term_secondary' in diff else diff['term_secondary_id'][0]
        if parent is not None:
            parent_id = parent.item_id if isinstance(parent, Term) else Term.objects.get(pk=parent).item_id
            ItemRelation.objects.filter(parent_id=parent_id, child_id=child_id).delete()
        if instance.term_secondary is not None or instance.term_secondary_id is not None:
            ItemRelation.objects.create(parent_id=instance.term_secondary.item_id, child_id=child_id, visible=True)
    if len({'context', 'context_id'} & set(instance.changed_fields)) != 0:
        diff = instance.diff
        parent = diff['context'][0] if 'context' in diff else diff['context_id'][0]
        parent_id = parent.item_id if isinstance(parent, Context) else Context.objects.get(pk=parent).item_id
        child_id = instance.item_id
        ItemRelation.objects.filter(parent_id=parent_id, child_id=child_id).delete()
        ItemRelation.objects.create(parent_id=instance.context.item_id, child_id=child_id, visible=True)

@receiver(pre_delete, sender=Flashcard)
@disable_for_loaddata
def delete_parent(sender, instance, **kwargs):
    """
    When the given flashcard is deleted, delete also the corresponding item
    relations.
    """
    ItemRelation.objects.filter(child_id=instance.item_id).delete()
<commit_msg>rename question directins in case of secondary terms<commit_after>from django.conf import settings
from django.db import models
from django.db.models import Q
from proso.django.models import ModelDiffMixin
from proso_models.models import Item, ItemRelation, Answer
from django.db.models.signals import pre_save, post_save, pre_delete
from django.dispatch import receiver
from proso.django.util import disable_for_loaddata
import logging

LOGGER = logging.getLogger('django.request')

class TermManager(models.Manager):

    def prepare_related(self):
        return self

class Term(models.Model, ModelDiffMixin):
    identifier = models.SlugField()
    item = models.ForeignKey(Item, null=True, default=None, related_name="flashcard_terms")

    lang = models.CharField(max_length=2)
    name = models.TextField()

    objects = TermManager()

    def to_json(self, nested=False):
        return {
            "id": self.pk,
            "identifier": self.identifier,
            "item_id": self.item_id,
            "object_type": "fc_term",
            "lang": self.lang,
            "name": self.name,
        }

    def __str__(self):
        return "{0.lang} - {0.name}".format(self)

class ContextManager(models.Manager):

    def prepare_related(self):
        return self

class Context(models.Model, ModelDiffMixin):
    identifier = models.SlugField()
    item = models.ForeignKey(Item, null=True, default=None, related_name="flashcard_contexts")

    lang = models.CharField(max_length=2)
    name = models.TextField(null=True, blank=True)
    content = models.TextField(null=True, blank=True)

    objects = ContextManager()

    def to_json(self, nested=False, with_content=True):
        json = {
            "id": self.pk,
            "identifier": self.identifier,
            "item_id": self.item_id,
            "object_type": "fc_context",
            "lang": self.lang,
            "name": self.name,
        }
        if with_content:
            json['content'] = self.content
        return json

    def __str__(self):
        return "{0.lang} - {0.name}".format(self)

class FlashcardManager(models.Manager):

    def prepare_related(self):
        return self.select_related(Flashcard.related_term(), Flashcard.related_context())

class Flashcard(models.Model, ModelDiffMixin):
    identifier = models.SlugField()
    item = models.ForeignKey(Item, null=True, default=None, related_name="flashcards")

    lang = models.CharField(max_length=2)
    term = models.ForeignKey(Term, related_name="flashcards")
    term_secondary = models.ForeignKey(Term, related_name="flashcards_as_secondary", null=True, blank=True, default=None)
    context = models.ForeignKey(Context, related_name="flashcards")
    description = models.TextField(null=True, blank=True)
    active = models.BooleanField(default=True)

    objects = FlashcardManager()

    def to_json(self, nested=False, contexts=True):
        data = {
            "id": self.pk,
            "identifier": self.identifier,
            "item_id": self.item_id,
            "object_type": "fc_flashcard",
            "active": self.active,
            "lang": self.lang,
            "term": self.get_term().to_json(nested=True),
        }
        if hasattr(self, "options"):
            data["options"] = [o.to_json(nested=True) for o in sorted(self.options, key=lambda f: f.term.name)]
        if hasattr(self, 'practice_meta'):
            data['practice_meta'] = self.practice_meta
        if not nested and contexts:
            data["context"] = self.get_context().to_json(nested=True)
        else:
            data["context_id"] = self.context_id
        if self.term_secondary is not None:
            data["term_secondary"] = self.get_term_secondary().to_json(nested=True)
        if self.description is not None:
            data['description'] = self.description
        return data

    def get_term(self):
        extension = settings.PROSO_FLASHCARDS.get("term_extension", None)
        if extension is None:
            return self.term
        else:
            return getattr(self.term, extension.__name__.lower())

    def get_term_secondary(self):
        extension = settings.PROSO_FLASHCARDS.get("term_extension", None)
        if extension is None:
            return self.term_secondary
        else:
            return None if self.term_secondary is None else getattr(self.term_secondary, extension.__name__.lower())

    def get_context(self):
        extension = settings.PROSO_FLASHCARDS.get("context_extension", None)
        if extension is None:
            return self.context
        else:
            return getattr(self.context, extension.__name__.lower())

    @staticmethod
    def related_term():
        extension = settings.PROSO_FLASHCARDS.get("term_extension", None)
        if extension is None:
            return "term"
        else:
            return "term__{}".format(extension.__name__.lower())

    @staticmethod
    def related_term_secondary():
        extension = settings.PROSO_FLASHCARDS.get("term_extension", None)
        if extension is None:
            return "term_secondary"
        else:
            return "term_secondary__{}".format(extension.__name__.lower())

    @staticmethod
    def related_context():
        extension = settings.PROSO_FLASHCARDS.get("context_extension", None)
        if extension is None:
            return "context"
        else:
            return "context__{}".format(extension.__name__.lower())

    def __str__(self):
        return "{0.term} - {0.context}".format(self)

class Category(models.Model, ModelDiffMixin):
    class Meta:
        verbose_name_plural = "categories"

    identifier = models.SlugField()
    item = models.ForeignKey(Item, null=True, default=None, related_name="flashcard_categories")
    lang = models.CharField(max_length=2)
    name = models.TextField()
    type = models.CharField(max_length=50, null=True, blank=True)

    def to_json(self, nested=False):
        return {
            "id": self.pk,
            "identifier": self.identifier,
            "item_id": self.item_id,
            "object_type": "fc_category",
            "lang": self.lang,
            "name": self.name,
            "type": self.type,
        }

    def __str__(self):
        return "{0.lang} - {0.name}".format(self)

class FlashcardAnswerManager(models.Manager):

    def prepare_related(self):
        return self.prefetch_related('options__{}'.format(Flashcard.related_term()))

    def from_json(self, json_object, practice_context, user_id, object_class=None):
        if object_class is None:
            object_class = FlashcardAnswer
        json_object = dict(json_object)
        flashcard_ids = set()
        flashcard_ids.add(json_object['flashcard_id'])
        if json_object.get('flashcard_answered_id') is not None:
            flashcard_ids.add(json_object['flashcard_answered_id'])
        if 'option_ids' in json_object:
            option_ids = set(json_object['option_ids'])
            if len(option_ids) < 1:
                raise Exception('If option_ids is given, it has to contain at least 1 items!')
            flashcard_ids |= option_ids
            if json_object['flashcard_id'] in option_ids:
                raise Exception('Option ids can not contain main flashcard id!')
            json_object['guess'] = 1.0 / (len(option_ids) + 1)
        else:
            json_object['guess'] = 0
        flashcards = {fc.id: fc for fc in Flashcard.objects.filter(pk__in=flashcard_ids)}
        if len(flashcard_ids) != len(flashcards):
            raise Exception("Invalid flashcard id (asked, answered or as option)")
        json_object['item_id'] = flashcards[json_object['flashcard_id']].item_id
        json_object['item_asked_id'] = flashcards[json_object['flashcard_id']].item_id
        json_object['item_answered_id'] = flashcards[json_object.get('flashcard_answered_id')].item_id if json_object.get('flashcard_answered_id') is not None else None
        json_object['lang'] = flashcards[json_object['flashcard_id']].lang
        answer = Answer.objects.from_json(json_object, practice_context, user_id, object_class=object_class)
        if 'option_ids' in json_object:
            for option_id in set(json_object['option_ids']):
                answer.options.add(flashcards[option_id])
        answer.save()
        return answer

class FlashcardAnswer(Answer):
    FROM_TERM = "t2d"
    FROM_DESCRIPTION = "d2t"
    FROM_TERM_TO_TERM_SECONDARY = 't2ts'
    FROM_TERM_SECONDARY_TO_TERM = 'ts2t'

    options = models.ManyToManyField(Flashcard, related_name="answers_with_this_as_option")

    objects = FlashcardAnswerManager()

    def to_json(self, nested=False):
        json = Answer.to_json(self)
        json['object_type'] = "fc_answer"
        if not nested:
            json["options"] = [flashcard.to_json(nested=True) for flashcard in self.options.all()]
        return json

PROSO_MODELS_TO_EXPORT = [Category, Flashcard, FlashcardAnswer,
                          settings.PROSO_FLASHCARDS.get("context_extension", Context),
                          settings.PROSO_FLASHCARDS.get("term_extension", Term)]

PROSO_CUSTOM_EXPORT = {
    'answer': '''
        SELECT
            proso_models_answer.*
        FROM proso_models_answer
        INNER JOIN proso_flashcards_flashcardanswer
            ON proso_models_answer.id = answer_ptr_id
    ''',
    'context': '''
        SELECT
            proso_flashcards_context.id,
            proso_flashcards_flashcard.item_id AS item_id,
            proso_flashcards_term.name AS term_name,
            proso_flashcards_context.name AS context_name,
            proso_flashcards_flashcard.lang AS language
        FROM proso_flashcards_flashcard
        INNER JOIN proso_flashcards_term ON term_id = proso_flashcards_term.id
        INNER JOIN proso_flashcards_context ON context_id = proso_flashcards_context.id
        WHERE proso_flashcards_term.lang = proso_flashcards_context.lang
    '''
}

def _get_value_without_negation(value):
    if isinstance(value, int):
        return abs(value), value < 0
    else:
        if value is not None and value.startswith('-'):
            return value[1:], True
        else:
            return value, False

def _create_q(column, value):
    value, negation = _get_value_without_negation(value)
    q = Q(**{column: value})
    return ~q if negation else q

@receiver(pre_save, sender=Term)
@receiver(pre_save, sender=Context)
@receiver(pre_save, sender=Flashcard)
@receiver(pre_save, sender=Category)
@disable_for_loaddata
def create_items(sender, instance, **kwargs):
    """
    When one of the defined objects is created, initialize also its item.
    """
    if instance.item_id is None and instance.item is None:
        item = Item()
        if hasattr(instance, 'active'):
            item.active = getattr(instance, 'active')
        item.save()
        instance.item = item

@receiver(pre_save, sender=Term)
@receiver(pre_save, sender=Context)
@receiver(pre_save, sender=Flashcard)
@receiver(pre_save, sender=Category)
@disable_for_loaddata
def change_activity(sender, instance, **kwargs):
    if 'active' in instance.diff:
        instance.item.active = instance.active
        instance.item.save()

@receiver(post_save, sender=Flashcard)
@disable_for_loaddata
def add_parent(sender, instance, **kwargs):
    """
    When a flashcard is created, create also an item relation.
    """
    if not kwargs['created']:
        return
    for att in ['term', 'term_secondary', 'context']:
        if getattr(instance, att) is None:
            continue
        parent = getattr(instance, att).item_id
        child = instance.item_id
        ItemRelation.objects.get_or_create(
            parent_id=parent,
            child_id=child,
            visible=True,
        )

@receiver(pre_save, sender=Flashcard)
@disable_for_loaddata
def change_parent(sender, instance, **kwargs):
    """
    When the given flashcard has changed. Look at term and context and change
    the corresponding item relation.
    """
    if instance.id is None:
        return
    if len({'term', 'term_id'} & set(instance.changed_fields)) != 0:
        diff = instance.diff
        parent = diff['term'][0] if 'term' in diff else diff['term_id'][0]
        parent_id = parent.item_id if isinstance(parent, Term) else Term.objects.get(pk=parent).item_id
        child_id = instance.item_id
        ItemRelation.objects.filter(parent_id=parent_id, child_id=child_id).delete()
        ItemRelation.objects.create(parent_id=instance.term.item_id, child_id=child_id, visible=True)
    if len({'term_secondary', 'term_secondary_id'} & set(instance.changed_fields)) != 0:
        diff = instance.diff
        child_id = instance.item_id
        parent = diff['term_secondary'][0] if 'term_secondary' in diff else diff['term_secondary_id'][0]
        if parent is not None:
            parent_id = parent.item_id if isinstance(parent, Term) else Term.objects.get(pk=parent).item_id
            ItemRelation.objects.filter(parent_id=parent_id, child_id=child_id).delete()
        if instance.term_secondary is not None or instance.term_secondary_id is not None:
            ItemRelation.objects.create(parent_id=instance.term_secondary.item_id, child_id=child_id, visible=True)
    if len({'context', 'context_id'} & set(instance.changed_fields)) != 0:
        diff = instance.diff
        parent = diff['context'][0] if 'context' in diff else diff['context_id'][0]
        parent_id = parent.item_id if isinstance(parent, Context) else Context.objects.get(pk=parent).item_id
        child_id = instance.item_id
        ItemRelation.objects.filter(parent_id=parent_id, child_id=child_id).delete()
        ItemRelation.objects.create(parent_id=instance.context.item_id, child_id=child_id, visible=True)

@receiver(pre_delete, sender=Flashcard)
@disable_for_loaddata
def delete_parent(sender, instance, **kwargs):
    """
    When the given flashcard is deleted, delete also the corresponding item
    relations.
    """
    ItemRelation.objects.filter(child_id=instance.item_id).delete()
<|endoftext|>


<commit_before># init for netCDF4. package
# Docstring comes from extension module _netCDF4.
from ._netCDF4 import *
# Need explicit imports for names beginning with underscores
from ._netCDF4 import __doc__
from ._netCDF4 import (__version__, __netcdf4libversion__, __hdf5libversion__,
                       __has_rename_grp__, __has_nc_inq_path__,
                       __has_nc_inq_format_extended__, __has_nc_open_mem__,
                       __has_nc_create_mem__, __has_cdf5_format__,
                       __has_parallel4_support__, __has_pnetcdf_support__,
                       __has_quantization_support__, __has_zstandard_support__,
                       __has_bzip2_support__, __has_blosc_support__, __has_szip_support__)
import os
__all__ =\
['Dataset','Variable','Dimension','Group','MFDataset','MFTime','CompoundType','VLType','date2num','num2date','date2index','stringtochar','chartostring','stringtoarr','getlibversion','EnumType','get_chunk_cache','set_chunk_cache']
# if HDF5_PLUGIN_PATH not set, point to package path if libh5noop.so exists there
if 'HDF5_PLUGIN_PATH' not in os.environ and os.path.exists(os.path.join(__path__[0],'lib__nczhdf5filters.so')):
    print('setting HDF5_PLUGIN_PATH to %s' % __path__[0])
    os.environ['HDF5_PLUGIN_PATH']=__path__[0]
<commit_msg>check for dylib extension on plugins<commit_after># init for netCDF4. package
# Docstring comes from extension module _netCDF4.
from ._netCDF4 import *
# Need explicit imports for names beginning with underscores
from ._netCDF4 import __doc__
from ._netCDF4 import (__version__, __netcdf4libversion__, __hdf5libversion__,
                       __has_rename_grp__, __has_nc_inq_path__,
                       __has_nc_inq_format_extended__, __has_nc_open_mem__,
                       __has_nc_create_mem__, __has_cdf5_format__,
                       __has_parallel4_support__, __has_pnetcdf_support__,
                       __has_quantization_support__, __has_zstandard_support__,
                       __has_bzip2_support__, __has_blosc_support__, __has_szip_support__)
import os
__all__ =\
['Dataset','Variable','Dimension','Group','MFDataset','MFTime','CompoundType','VLType','date2num','num2date','date2index','stringtochar','chartostring','stringtoarr','getlibversion','EnumType','get_chunk_cache','set_chunk_cache']
# if HDF5_PLUGIN_PATH not set, point to package path if plugins live there
if 'HDF5_PLUGIN_PATH' not in os.environ and\
    (os.path.exists(os.path.join(__path__[0],'lib__nczhdf5filters.so')) or\
    os.path.exists(os.path.join(__path__[0],'lib__nczhdf5filters.dylib'))):
    os.environ['HDF5_PLUGIN_PATH']=__path__[0]
<|endoftext|>