<commit_before># Copyright 2014 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
# pylint: disable=protected-access

import datetime
import functools
import os
import inspect
import types
import warnings

def Cache(obj):
  """Decorator for caching read-only properties.

  Example usage (always returns the same Foo instance):
    @Cache
    def CreateFoo():
      return Foo()

  If CreateFoo() accepts parameters, a separate cached value is maintained
  for each unique parameter combination.

  Cached methods maintain their cache for the lifetime of the /instance/, while
  cached functions maintain their cache for the lifetime of the /module/.
  """
  @functools.wraps(obj)
  def Cacher(*args, **kwargs):
    cacher = args[0] if inspect.getargspec(obj).args[:1] == ['self'] else obj
    cacher.__cache = cacher.__cache if hasattr(cacher, '__cache') else {}
    key = str(obj) + str(args) + str(kwargs)
    if key not in cacher.__cache:
      cacher.__cache[key] = obj(*args, **kwargs)
    return cacher.__cache[key]
  return Cacher

class Deprecated(object):

  def __init__(self, year, month, day, extra_guidance=''):
    self._date_of_support_removal = datetime.date(year, month, day)
    self._extra_guidance = extra_guidance

  def _DisplayWarningMessage(self, target):
    target_str = ''
    if isinstance(target, types.FunctionType):
      target_str = 'Function %s' % target.__name__
    else:
      target_str = 'Class %s' % target.__name__
    warnings.warn('%s is deprecated. It will no longer be supported on %s. '
                  'Please remove it or switch to an alternative before '
                  'that time. %s\n'
                  % (target_str,
                     self._date_of_support_removal.strftime('%B %d, %Y'),
                     self._extra_guidance),
                  stacklevel=self._ComputeStackLevel())

  def _ComputeStackLevel(self):
    this_file, _ = os.path.splitext(__file__)
    frame = inspect.currentframe()
    i = 0
    while True:
      filename = frame.f_code.co_filename
      if not filename.startswith(this_file):
        return i
      frame = frame.f_back
      i += 1

  def __call__(self, target):
    if isinstance(target, types.FunctionType):
      @functools.wraps(target)
      def wrapper(*args, **kwargs):
        self._DisplayWarningMessage(target)
        return target(*args, **kwargs)
      return wrapper
    elif inspect.isclass(target):
      original_ctor = target.__init__

      # We have to handle case original_ctor is object.__init__ separately
      # since object.__init__ does not have __module__ defined, which
      # cause functools.wraps() to raise exception.
      if original_ctor == object.__init__:
        def new_ctor(*args, **kwargs):
          self._DisplayWarningMessage(target)
          return original_ctor(*args, **kwargs)
      else:
        @functools.wraps(original_ctor)
        def new_ctor(*args, **kwargs):
          self._DisplayWarningMessage(target)
          return original_ctor(*args, **kwargs)

      target.__init__ = new_ctor
      return target
    else:
      raise TypeError('@Deprecated is only applicable to functions or classes')

def Disabled(*args):
  """Decorator for disabling tests/benchmarks.

  If args are given, the test will be disabled if ANY of the args match the
  browser type, OS name or OS version:
    @Disabled('canary')        # Disabled for canary browsers
    @Disabled('win')           # Disabled on Windows.
    @Disabled('win', 'linux')  # Disabled on both Windows and Linux.
    @Disabled('mavericks')     # Disabled on Mac Mavericks (10.9) only.
    @Disabled('all')  # Unconditionally disabled.
  """
  def _Disabled(func):
    if not hasattr(func, '_disabled_strings'):
      func._disabled_strings = set()
    func._disabled_strings.update(disabled_strings)
    return func
  assert args, (
      "@Disabled(...) requires arguments. Use @Disabled('all') if you want to "
      'unconditionally disable the test.')
  assert not callable(args[0]), 'Please use @Disabled(..).'
  disabled_strings = list(args)
  for disabled_string in disabled_strings:
    # TODO(tonyg): Validate that these strings are recognized.
    assert isinstance(disabled_string, str), '@Disabled accepts a list of strs'
  return _Disabled

def Enabled(*args):
  """Decorator for enabling tests/benchmarks.

  The test will be enabled if ANY of the args match the browser type, OS name
  or OS version:
    @Enabled('canary')        # Enabled only for canary browsers
    @Enabled('win')           # Enabled only on Windows.
    @Enabled('win', 'linux')  # Enabled only on Windows or Linux.
    @Enabled('mavericks')     # Enabled only on Mac Mavericks (10.9).
  """
  def _Enabled(func):
    if not hasattr(func, '_enabled_strings'):
      func._enabled_strings = set()
    func._enabled_strings.update(enabled_strings)
    return func
  assert args, '@Enabled(..) requires arguments'
  assert not callable(args[0]), 'Please use @Enabled(..).'
  enabled_strings = list(args)
  for enabled_string in enabled_strings:
    # TODO(tonyg): Validate that these strings are recognized.
    assert isinstance(enabled_string, str), '@Enabled accepts a list of strs'
  return _Enabled

# TODO(dpranke): Remove if we don't need this.
def Isolated(*args):
  """Decorator for noting that tests must be run in isolation.

  The test will be run by itself (not concurrently with any other tests)
  if ANY of the args match the browser type, OS name, or OS version."""
  def _Isolated(func):
    if not isinstance(func, types.FunctionType):
      func._isolated_strings = isolated_strings
      return func
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
      func(*args, **kwargs)
    wrapper._isolated_strings = isolated_strings
    return wrapper
  if len(args) == 1 and callable(args[0]):
    isolated_strings = []
    return _Isolated(args[0])
  isolated_strings = list(args)
  for isolated_string in isolated_strings:
    # TODO(tonyg): Validate that these strings are recognized.
    assert isinstance(isolated_string, str), 'Isolated accepts a list of strs'
  return _Isolated

# TODO(nednguyen): Remove this and have call site just use ShouldSkip directly.
def IsEnabled(test, possible_browser):
  """Returns True iff |test| is enabled given the |possible_browser|.

  Use to respect the @Enabled / @Disabled decorators.

  Args:
    test: A function or class that may contain _disabled_strings and/or
          _enabled_strings attributes.
    possible_browser: A PossibleBrowser to check whether |test| may run against.
  """
  should_skip, msg = ShouldSkip(test, possible_browser)
  return (not should_skip, msg)

def ShouldSkip(test, possible_browser):
  """Returns whether the test should be skipped and the reason for it."""
  platform_attributes = [a.lower() for a in [
      possible_browser.browser_type,
      possible_browser.platform.GetOSName(),
      possible_browser.platform.GetOSVersionName(),
      ]]
  if possible_browser.supports_tab_control:
    platform_attributes.append('has tabs')
  # Match browser type filters against "content-shell" and "mandoline" prefixes.
  if 'content-shell' in possible_browser.browser_type:
    platform_attributes.append('content-shell')
  if 'mandoline' in possible_browser.browser_type:
    platform_attributes.append('mandoline')

  if hasattr(test, '__name__'):
    name = test.__name__
  elif hasattr(test, '__class__'):
    name = test.__class__.__name__
  else:
    name = str(test)

  skip = 'Skipping %s (%s) because' % (name, str(test))
  running = 'You are running %r.' % platform_attributes

  if hasattr(test, '_disabled_strings'):
    if 'all' in test._disabled_strings:
      return (True, '%s it is unconditionally disabled.' % skip)
    if set(test._disabled_strings) & set(platform_attributes):
      return (True, '%s it is disabled for %s. %s' %
                      (skip, ' and '.join(test._disabled_strings), running))

  if hasattr(test, '_enabled_strings'):
    if 'all' in test._enabled_strings:
      return False, None  # No arguments to @Enabled means always enable.
    if not set(test._enabled_strings) & set(platform_attributes):
      return (True, '%s it is only enabled for %s. %s' %
                      (skip, ' or '.join(test._enabled_strings), running))

  return False, None

def ShouldBeIsolated(test, possible_browser):
  platform_attributes = [a.lower() for a in [
      possible_browser.browser_type,
      possible_browser.platform.GetOSName(),
      possible_browser.platform.GetOSVersionName(),
      ]]
  if possible_browser.supports_tab_control:
    platform_attributes.append('has tabs')

  if hasattr(test, '_isolated_strings'):
    isolated_strings = test._isolated_strings
    if not isolated_strings:
      return True # No arguments to @Isolated means always isolate.
    for isolated_string in isolated_strings:
      if isolated_string in platform_attributes:
        return True
    return False
  return False
<commit_msg>Extract construction of platform attributes in telemetry/decorators.py.<commit_after># Copyright 2014 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
# pylint: disable=protected-access

import datetime
import functools
import os
import inspect
import types
import warnings

def Cache(obj):
  """Decorator for caching read-only properties.

  Example usage (always returns the same Foo instance):
    @Cache
    def CreateFoo():
      return Foo()

  If CreateFoo() accepts parameters, a separate cached value is maintained
  for each unique parameter combination.

  Cached methods maintain their cache for the lifetime of the /instance/, while
  cached functions maintain their cache for the lifetime of the /module/.
  """
  @functools.wraps(obj)
  def Cacher(*args, **kwargs):
    cacher = args[0] if inspect.getargspec(obj).args[:1] == ['self'] else obj
    cacher.__cache = cacher.__cache if hasattr(cacher, '__cache') else {}
    key = str(obj) + str(args) + str(kwargs)
    if key not in cacher.__cache:
      cacher.__cache[key] = obj(*args, **kwargs)
    return cacher.__cache[key]
  return Cacher

class Deprecated(object):

  def __init__(self, year, month, day, extra_guidance=''):
    self._date_of_support_removal = datetime.date(year, month, day)
    self._extra_guidance = extra_guidance

  def _DisplayWarningMessage(self, target):
    target_str = ''
    if isinstance(target, types.FunctionType):
      target_str = 'Function %s' % target.__name__
    else:
      target_str = 'Class %s' % target.__name__
    warnings.warn('%s is deprecated. It will no longer be supported on %s. '
                  'Please remove it or switch to an alternative before '
                  'that time. %s\n'
                  % (target_str,
                     self._date_of_support_removal.strftime('%B %d, %Y'),
                     self._extra_guidance),
                  stacklevel=self._ComputeStackLevel())

  def _ComputeStackLevel(self):
    this_file, _ = os.path.splitext(__file__)
    frame = inspect.currentframe()
    i = 0
    while True:
      filename = frame.f_code.co_filename
      if not filename.startswith(this_file):
        return i
      frame = frame.f_back
      i += 1

  def __call__(self, target):
    if isinstance(target, types.FunctionType):
      @functools.wraps(target)
      def wrapper(*args, **kwargs):
        self._DisplayWarningMessage(target)
        return target(*args, **kwargs)
      return wrapper
    elif inspect.isclass(target):
      original_ctor = target.__init__

      # We have to handle case original_ctor is object.__init__ separately
      # since object.__init__ does not have __module__ defined, which
      # cause functools.wraps() to raise exception.
      if original_ctor == object.__init__:
        def new_ctor(*args, **kwargs):
          self._DisplayWarningMessage(target)
          return original_ctor(*args, **kwargs)
      else:
        @functools.wraps(original_ctor)
        def new_ctor(*args, **kwargs):
          self._DisplayWarningMessage(target)
          return original_ctor(*args, **kwargs)

      target.__init__ = new_ctor
      return target
    else:
      raise TypeError('@Deprecated is only applicable to functions or classes')

def Disabled(*args):
  """Decorator for disabling tests/benchmarks.

  If args are given, the test will be disabled if ANY of the args match the
  browser type, OS name or OS version:
    @Disabled('canary')        # Disabled for canary browsers
    @Disabled('win')           # Disabled on Windows.
    @Disabled('win', 'linux')  # Disabled on both Windows and Linux.
    @Disabled('mavericks')     # Disabled on Mac Mavericks (10.9) only.
    @Disabled('all')  # Unconditionally disabled.
  """
  def _Disabled(func):
    if not hasattr(func, '_disabled_strings'):
      func._disabled_strings = set()
    func._disabled_strings.update(disabled_strings)
    return func
  assert args, (
      "@Disabled(...) requires arguments. Use @Disabled('all') if you want to "
      'unconditionally disable the test.')
  assert not callable(args[0]), 'Please use @Disabled(..).'
  disabled_strings = list(args)
  for disabled_string in disabled_strings:
    # TODO(tonyg): Validate that these strings are recognized.
    assert isinstance(disabled_string, str), '@Disabled accepts a list of strs'
  return _Disabled

def Enabled(*args):
  """Decorator for enabling tests/benchmarks.

  The test will be enabled if ANY of the args match the browser type, OS name
  or OS version:
    @Enabled('canary')        # Enabled only for canary browsers
    @Enabled('win')           # Enabled only on Windows.
    @Enabled('win', 'linux')  # Enabled only on Windows or Linux.
    @Enabled('mavericks')     # Enabled only on Mac Mavericks (10.9).
  """
  def _Enabled(func):
    if not hasattr(func, '_enabled_strings'):
      func._enabled_strings = set()
    func._enabled_strings.update(enabled_strings)
    return func
  assert args, '@Enabled(..) requires arguments'
  assert not callable(args[0]), 'Please use @Enabled(..).'
  enabled_strings = list(args)
  for enabled_string in enabled_strings:
    # TODO(tonyg): Validate that these strings are recognized.
    assert isinstance(enabled_string, str), '@Enabled accepts a list of strs'
  return _Enabled

# TODO(dpranke): Remove if we don't need this.
def Isolated(*args):
  """Decorator for noting that tests must be run in isolation.

  The test will be run by itself (not concurrently with any other tests)
  if ANY of the args match the browser type, OS name, or OS version."""
  def _Isolated(func):
    if not isinstance(func, types.FunctionType):
      func._isolated_strings = isolated_strings
      return func
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
      func(*args, **kwargs)
    wrapper._isolated_strings = isolated_strings
    return wrapper
  if len(args) == 1 and callable(args[0]):
    isolated_strings = []
    return _Isolated(args[0])
  isolated_strings = list(args)
  for isolated_string in isolated_strings:
    # TODO(tonyg): Validate that these strings are recognized.
    assert isinstance(isolated_string, str), 'Isolated accepts a list of strs'
  return _Isolated

# TODO(nednguyen): Remove this and have call site just use ShouldSkip directly.
def IsEnabled(test, possible_browser):
  """Returns True iff |test| is enabled given the |possible_browser|.

  Use to respect the @Enabled / @Disabled decorators.

  Args:
    test: A function or class that may contain _disabled_strings and/or
          _enabled_strings attributes.
    possible_browser: A PossibleBrowser to check whether |test| may run against.
  """
  should_skip, msg = ShouldSkip(test, possible_browser)
  return (not should_skip, msg)

def ShouldSkip(test, possible_browser):
  """Returns whether the test should be skipped and the reason for it."""
  platform_attributes = _PlatformAttributes(possible_browser)

  if hasattr(test, '__name__'):
    name = test.__name__
  elif hasattr(test, '__class__'):
    name = test.__class__.__name__
  else:
    name = str(test)

  skip = 'Skipping %s (%s) because' % (name, str(test))
  running = 'You are running %r.' % platform_attributes

  if hasattr(test, '_disabled_strings'):
    if 'all' in test._disabled_strings:
      return (True, '%s it is unconditionally disabled.' % skip)
    if set(test._disabled_strings) & set(platform_attributes):
      return (True, '%s it is disabled for %s. %s' %
                      (skip, ' and '.join(test._disabled_strings), running))

  if hasattr(test, '_enabled_strings'):
    if 'all' in test._enabled_strings:
      return False, None  # No arguments to @Enabled means always enable.
    if not set(test._enabled_strings) & set(platform_attributes):
      return (True, '%s it is only enabled for %s. %s' %
                      (skip, ' or '.join(test._enabled_strings), running))

  return False, None

def ShouldBeIsolated(test, possible_browser):
  platform_attributes = _PlatformAttributes(possible_browser)
  if hasattr(test, '_isolated_strings'):
    isolated_strings = test._isolated_strings
    if not isolated_strings:
      return True # No arguments to @Isolated means always isolate.
    for isolated_string in isolated_strings:
      if isolated_string in platform_attributes:
        return True
    return False
  return False

def _PlatformAttributes(possible_browser):
  """Returns a list of platform attribute strings."""
  attributes = [a.lower() for a in [
      possible_browser.browser_type,
      possible_browser.platform.GetOSName(),
      possible_browser.platform.GetOSVersionName(),
  ]]
  if possible_browser.supports_tab_control:
    attributes.append('has tabs')
  if 'content-shell' in possible_browser.browser_type:
    attributes.append('content-shell')
  if 'mandoline' in possible_browser.browser_type:
    attributes.append('mandoline')
  return attributes
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
###############################################################################
#
#    Copyright (C) 2001-2014 Micronaet SRL (<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, api
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 ResCompany(orm.Model):
    """ Model name: ResCompany
    """
    _inherit = 'res.company'

    # Procedure:
    def export_product_status_for_inventory(self, cr, uid, ids, context=None):
        ''' Export inventory data from order and picking
        '''
        # Output file:
        log_file = '/home/administrator/photo/output/C01_inventory.csv'
        f_log = open(log_file, 'w')

        # Pool used:
        product_pool = self.pool.get('product.product')
        line_pool = self.pool.get('purchase.order.line')
        move_pool = self.pool.get('stock.move')

        # Database:
        move_db = {}
        line_db = {}

        # Search only product in cat. stat: C01:
        _logger.warning('Start select product')
        product_ids = product_pool.search(cr, uid, [
            ('statistic_category', '=', 'C01'),
            ], context=context)
        _logger.warning('End select, total: %s' % len(product_ids))

        # Check purchase line for price:
        _logger.warning('Start select product')
        line_ids = line_pool.search(cr, uid, [
            ('product_id', 'in', product_ids),
            ('order_id.state', 'not in', ('draft', 'cancel')),
            ], context=context)
        _logger.warning('End select purchase, total: %s' % len(line_ids))
        for line in line_pool.browse(cr, uid, line_ids, context=context):
            if line.product_id.id not in line_db:
                line_db[line.product_id.id] = []
            line_db[line.product_id.id].append(line)

        # Check stock move for sold in year
        _logger.warning('Start select product')
        move_ids = move_pool.search(cr, uid, [
            ('product_id', 'in', product_ids),
            ('state', '=', 'done'),
            ], context=context)
        _logger.warning('End select move, total: %s' % len(move_ids))
        for move in move_pool.browse(cr, uid, move_ids, context=context):
            if move.product_id.id not in move_db:
                move_db[move.product_id.id] = []
            move_db[move.product_id.id].append(move)

        # Create database list for product:
        _logger.warning('Start export product')
        f_log.write('Codice|INV|Costo azienda|Prezzo diff.|Movimentato 2016|OF|MM\n')

        for product in product_pool.browse(
                cr, uid, product_ids, context=context):
            # OF status
            of_status = ''
            price_unit = 0.0
            price_difference = False
            for line in line_db.get(product.id, []):
                if not price_unit:
                    price_unit = line.price_unit
                elif price_unit != line.price_unit:
                    price_difference = True

                of_status += '[ %s doc. %s ]' % (
                    line.price_unit,
                    line.order_id.date_order[:10],
                    )

            # MM status
            mm_status = ''
            move_date = '' # TODO
            moved = False
            for line in move_db.get(product.id, []):
                if line.picking_id.name.startswith('WH/IN/00005'):
                    continue

                if not line.picking_id.name.startswith('WH/IN'):
                    continue
                if line.create_date > '2016-01-01':
                    moved = True
                mm_status += '[ %s doc. %s ]' % (
                    line.create_date[:10],
                    line.picking_id.name, # date_done
                    )

            f_log.write('%s|%s|%s|%s|%s|%s|%s\n' % (
                product.default_code,
                product.mx_start_qty,
                product.company_cost,
                'X' if price_difference else '',
                'X' if moved else '',
                of_status,
                mm_status,
                ))

        f_log.close()
        _logger.warning('End export product')
        return True

    def save_cost_in_cost_method(self, cr, uid, ids, context=None):
        ''' Migrate 3 cost from old part in new cost management
        '''
        # Log operation
        log_file = '/home/administrator/photo/output/indoor_cost_migration.csv'
        f_log = open(log_file, 'w')
        _logger.warning('Start migrate cost log on: %s' % log_file)

        product_pool = self.pool.get('product.product')
        product_ids = product_pool.search(cr, uid, [
            ('statistic_category', 'in', (
                'I01', 'I02', 'I03', 'I04', 'I05', 'I06')),
            ], context=context)

        f_log.write(
            'Codice|INV|Cat. Stat.|Costo fornitore|Azienda Da|A|Cliente Da|A\n')
        res = {}
        for product in product_pool.browse(
                cr, uid, product_ids, context=context):
            f_log.write(
                '%s|%s|%s|%s|%s|%s|%s\n' % (
                    product.default_code,
                    product.statistic_category,
                    product.standard_price,
                    product.cost_in_stock,
                    product.company_cost,
                    product.cost_for_sale,
                    product.customer_cost,
                ))
            res[product.id] = {
                'company_cost': product.cost_in_stock,
                'customer_cost': product.cost_for_sale,
                }

        for product_id, data in res.iteritems:
            product_pool.write(cr, uid, product_id, data, context=context)
        _logger.info('Migrated data!')
        return True

<commit_msg>bugfix<commit_after># -*- coding: utf-8 -*-
###############################################################################
#
#    Copyright (C) 2001-2014 Micronaet SRL (<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, api
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 ResCompany(orm.Model):
    """ Model name: ResCompany
    """
    _inherit = 'res.company'

    # Procedure:
    def export_product_status_for_inventory(self, cr, uid, ids, context=None):
        ''' Export inventory data from order and picking
        '''
        # Output file:
        log_file = '/home/administrator/photo/output/C01_inventory.csv'
        f_log = open(log_file, 'w')

        # Pool used:
        product_pool = self.pool.get('product.product')
        line_pool = self.pool.get('purchase.order.line')
        move_pool = self.pool.get('stock.move')

        # Database:
        move_db = {}
        line_db = {}

        # Search only product in cat. stat: C01:
        _logger.warning('Start select product')
        product_ids = product_pool.search(cr, uid, [
            ('statistic_category', '=', 'C01'),
            ], context=context)
        _logger.warning('End select, total: %s' % len(product_ids))

        # Check purchase line for price:
        _logger.warning('Start select product')
        line_ids = line_pool.search(cr, uid, [
            ('product_id', 'in', product_ids),
            ('order_id.state', 'not in', ('draft', 'cancel')),
            ], context=context)
        _logger.warning('End select purchase, total: %s' % len(line_ids))
        for line in line_pool.browse(cr, uid, line_ids, context=context):
            if line.product_id.id not in line_db:
                line_db[line.product_id.id] = []
            line_db[line.product_id.id].append(line)

        # Check stock move for sold in year
        _logger.warning('Start select product')
        move_ids = move_pool.search(cr, uid, [
            ('product_id', 'in', product_ids),
            ('state', '=', 'done'),
            ], context=context)
        _logger.warning('End select move, total: %s' % len(move_ids))
        for move in move_pool.browse(cr, uid, move_ids, context=context):
            if move.product_id.id not in move_db:
                move_db[move.product_id.id] = []
            move_db[move.product_id.id].append(move)

        # Create database list for product:
        _logger.warning('Start export product')
        f_log.write('Codice|INV|Costo azienda|Prezzo diff.|Movimentato 2016|OF|MM\n')

        for product in product_pool.browse(
                cr, uid, product_ids, context=context):
            # OF status
            of_status = ''
            price_unit = 0.0
            price_difference = False
            for line in line_db.get(product.id, []):
                if not price_unit:
                    price_unit = line.price_unit
                elif price_unit != line.price_unit:
                    price_difference = True

                of_status += '[ %s doc. %s ]' % (
                    line.price_unit,
                    line.order_id.date_order[:10],
                    )

            # MM status
            mm_status = ''
            move_date = '' # TODO
            moved = False
            for line in move_db.get(product.id, []):
                if line.picking_id.name.startswith('WH/IN/00005'):
                    continue

                if not line.picking_id.name.startswith('WH/IN'):
                    continue
                if line.create_date > '2016-01-01':
                    moved = True
                mm_status += '[ %s doc. %s ]' % (
                    line.create_date[:10],
                    line.picking_id.name, # date_done
                    )

            f_log.write('%s|%s|%s|%s|%s|%s|%s\n' % (
                product.default_code,
                product.mx_start_qty,
                product.company_cost,
                'X' if price_difference else '',
                'X' if moved else '',
                of_status,
                mm_status,
                ))

        f_log.close()
        _logger.warning('End export product')
        return True

    def save_cost_in_cost_method(self, cr, uid, ids, context=None):
        ''' Migrate 3 cost from old part in new cost management
        '''
        # Log operation
        log_file = '/home/administrator/photo/output/indoor_cost_migration.csv'
        f_log = open(log_file, 'w')
        _logger.warning('Start migrate cost log on: %s' % log_file)

        product_pool = self.pool.get('product.product')
        product_ids = product_pool.search(cr, uid, [
            ('statistic_category', 'in', (
                'I01', 'I02', 'I03', 'I04', 'I05', 'I06')),
            ], context=context)

        f_log.write(
            'Codice|INV|Cat. Stat.|Costo fornitore|Azienda Da|A|Cliente Da|A\n')
        res = {}
        for product in product_pool.browse(
                cr, uid, product_ids, context=context):
            f_log.write(
                '%s|%s|%s|%s|%s|%s|%s\n' % (
                    product.default_code,
                    product.statistic_category,
                    product.standard_price,
                    product.cost_in_stock,
                    product.company_cost,
                    product.cost_for_sale,
                    product.customer_cost,
                ))
            res[product.id] = {
                'company_cost': product.cost_in_stock,
                'customer_cost': product.cost_for_sale,
                }

        for product_id, data in res.iteritems():
            product_pool.write(cr, uid, product_id, data, context=context)
        _logger.info('Migrated data!')
        return True

<|endoftext|>


<commit_before>'''
    XXXENV is a list of dicts, ordered by scopeXXX

    ENV contains a pointer to memory

    In memory, an env consists of a list:
        [ <frame>, <pointer to enclosing env> ],
    where a frame is a dictionary

    TODO:
        * clean up mem imports
            * consolidate read/write functions?
'''

from reg import fetch, assign, ENV, VAL, UNEV, ARGL
from prim import PRIMITIVES
from mem import *

UNBOUND = 'UNBOUND'

def read_env_from_memory():
    "read env from memory"
    address = fetch(ENV)
    return read_from_address(address), address

def write_env_to_new_memory(env):
    "write env to memory at the earliest available address"
    address = write_to_free_address(env)
    assign(ENV, address)

def lookup(reg):
    "return the value bound to var (in reg) in current env"
    var = fetch(reg)

    if var in PRIMITIVES:
        return var

    env, _ = read_env_from_memory()

    while env:
        print(env)
        frame, enclosure = env
        if var in frame:
            return frame[var]
        else:
            env = read_from_address(enclosure)

    return UNBOUND

def define_var():
    "bind var (in UNEV) to val (in VAL) in the most recent frame"
    var, val = fetch(UNEV), fetch(VAL)
    env, address = read_env_from_memory()
    frame, enclosure = env
    frame[var] = val
    write_to_address([frame, enclosure], address)

def set_var():
    "bind the most recent occurence of var (in UNEV) to val (in VAL)"
    var, val = fetch(UNEV), fetch(VAL)
    env, address = read_env_from_memory()

    while env:
        frame, enclosure = env
        if var in frame:
            frame[var] = val
            write_to_address([frame, enclosure], address)
        else:
            env = enclosure

    # raise exception? return dummy val?

def extend_env():
    env_pointer = fetch(ENV)
    params = fetch(UNEV)
    args = fetch(ARGL)

    new_frame = dict(zip(params, args))

    ext_env = [new_frame, env_pointer]

    write_env_to_new_memory(ext_env)

# initialization

def initial_env():
    return [{}, None]

def initialize_env():
    write_to_address(initial_env(), ROOT)

def load_global_env():
    assign(ENV, ROOT)<commit_msg>Fix lookup bug<commit_after>'''
    XXXENV is a list of dicts, ordered by scopeXXX

    ENV contains a pointer to memory

    In memory, an env consists of a list:
        [ <frame>, <pointer to enclosing env> ],
    where a frame is a dictionary

    TODO:
        * clean up mem imports
            * consolidate read/write functions?
'''

from reg import fetch, assign, ENV, VAL, UNEV, ARGL
from prim import PRIMITIVES
from mem import *

UNBOUND = 'UNBOUND'

def read_env_from_memory():
    "read env from memory"
    address = fetch(ENV)
    return read_from_address(address), address

def write_env_to_new_memory(env):
    "write env to memory at the earliest available address"
    address = write_to_free_address(env)
    assign(ENV, address)

def lookup(reg):
    "return the value bound to var (in reg) in current env"
    var = fetch(reg)

    if var in PRIMITIVES:
        return var

    env, _ = read_env_from_memory()

    while env:
        print(env)
        frame, enclosure = env
        if var in frame:
            return frame[var]
        else:
            env = read_from_address(enclosure) if enclosure else None

    return UNBOUND

def define_var():
    "bind var (in UNEV) to val (in VAL) in the most recent frame"
    var, val = fetch(UNEV), fetch(VAL)
    env, address = read_env_from_memory()
    frame, enclosure = env
    frame[var] = val
    write_to_address([frame, enclosure], address)

def set_var():
    "bind the most recent occurence of var (in UNEV) to val (in VAL)"
    var, val = fetch(UNEV), fetch(VAL)
    env, address = read_env_from_memory()

    while env:
        frame, enclosure = env
        if var in frame:
            frame[var] = val
            write_to_address([frame, enclosure], address)
        else:
            env = enclosure

    # raise exception? return dummy val?

def extend_env():
    env_pointer = fetch(ENV)
    params = fetch(UNEV)
    args = fetch(ARGL)

    new_frame = dict(zip(params, args))

    ext_env = [new_frame, env_pointer]

    write_env_to_new_memory(ext_env)

# initialization

def initial_env():
    return [{}, None]

def initialize_env():
    write_to_address(initial_env(), ROOT)

def load_global_env():
    assign(ENV, ROOT)<|endoftext|>


<commit_before>from __future__ import division
import numpy as np
np.seterr(divide='ignore') # these warnings are usually harmless for this code
from matplotlib import pyplot as plt
import matplotlib
matplotlib.rcParams['font.size'] = 8

import pyhsmm
from pyhsmm.util.text import progprint_xrange

print \
'''
This demo shows how HDP-HMMs can fail when the underlying data has state
persistence without some kind of temporal regularization (in the form of a
sticky bias or duration modeling): without setting the number of states to be
the correct number a priori, lots of extra states can be intsantiated.

BUT the effect is much more relevant on real data (when the data doesn't exactly
fit the model). Maybe this demo should use multinomial emissions...
'''

#####################
#  data generation  #
#####################

# Set parameters
N = 4
T = 1000
obs_dim = 2

obs_hypparams = {'mu_0':np.zeros(obs_dim),
                'sigma_0':np.eye(obs_dim),
                'kappa_0':0.3,
                'nu_0':obs_dim+5}

dur_hypparams = {'alpha_0':2*30,
                 'beta_0':2}

true_obs_distns = [pyhsmm.distributions.Gaussian(**obs_hypparams) for state in xrange(N)]
true_dur_distns = [pyhsmm.distributions.PoissonDuration(**dur_hypparams) for state in range(N)]

truemodel = pyhsmm.models.HSMM(alpha=6.,gamma=6.,init_state_concentration=6.,
                              obs_distns=true_obs_distns,
                              dur_distns=true_dur_distns)

data, labels = truemodel.generate(T)

plt.figure()
truemodel.plot()
plt.gcf().suptitle('True model')

#########################
#  posterior inference  #
#########################

# Set the weak limit truncation level
Nmax = 25

### HDP-HMM without the sticky bias

obs_distns = [pyhsmm.distributions.Gaussian(**obs_hypparams) for state in xrange(Nmax)]
posteriormodel = pyhsmm.models.HMMEigen(alpha=6.,gamma=6.,init_state_concentration=6.,
                                   obs_distns=obs_distns)
posteriormodel.add_data(data)

for idx in progprint_xrange(100):
    posteriormodel.resample_model()

plt.figure()
posteriormodel.plot()
plt.gcf().suptitle('HDP-HMM sampled model after 100 iterations')

### Sticky-HDP-HMM

obs_distns = [pyhsmm.distributions.Gaussian(**obs_hypparams) for state in xrange(Nmax)]
posteriormodel = pyhsmm.models.StickyHMMEigen(kappa=50.,alpha=6.,gamma=6.,init_state_concentration=6.,
                                   obs_distns=obs_distns)
posteriormodel.add_data(data)

for idx in progprint_xrange(100):
    posteriormodel.resample_model()

plt.figure()
posteriormodel.plot()
plt.gcf().suptitle('Sticky HDP-HMM sampled model after 100 iterations')

plt.show()
<commit_msg>whitespace<commit_after>from __future__ import division
import numpy as np
np.seterr(divide='ignore') # these warnings are usually harmless for this code
from matplotlib import pyplot as plt
import matplotlib
matplotlib.rcParams['font.size'] = 8

import pyhsmm
from pyhsmm.util.text import progprint_xrange

print \
'''
This demo shows how HDP-HMMs can fail when the underlying data has state
persistence without some kind of temporal regularization (in the form of a
sticky bias or duration modeling): without setting the number of states to be
the correct number a priori, lots of extra states can be intsantiated.

BUT the effect is much more relevant on real data (when the data doesn't exactly
fit the model). Maybe this demo should use multinomial emissions...
'''

#####################
#  data generation  #
#####################

# Set parameters
N = 4
T = 1000
obs_dim = 2

obs_hypparams = {'mu_0':np.zeros(obs_dim),
                'sigma_0':np.eye(obs_dim),
                'kappa_0':0.3,
                'nu_0':obs_dim+5}

dur_hypparams = {'alpha_0':2*30,
                 'beta_0':2}

true_obs_distns = [pyhsmm.distributions.Gaussian(**obs_hypparams) for state in xrange(N)]
true_dur_distns = [pyhsmm.distributions.PoissonDuration(**dur_hypparams) for state in range(N)]

truemodel = pyhsmm.models.HSMM(alpha=6.,gamma=6.,init_state_concentration=6.,
                              obs_distns=true_obs_distns,
                              dur_distns=true_dur_distns)

data, labels = truemodel.generate(T)

plt.figure()
truemodel.plot()
plt.gcf().suptitle('True model')

#########################
#  posterior inference  #
#########################

# Set the weak limit truncation level
Nmax = 25

### HDP-HMM without the sticky bias

obs_distns = [pyhsmm.distributions.Gaussian(**obs_hypparams) for state in xrange(Nmax)]
posteriormodel = pyhsmm.models.HMMEigen(alpha=6.,gamma=6.,init_state_concentration=6.,
                                   obs_distns=obs_distns)
posteriormodel.add_data(data)

for idx in progprint_xrange(100):
    posteriormodel.resample_model()

plt.figure()
posteriormodel.plot()
plt.gcf().suptitle('HDP-HMM sampled model after 100 iterations')

### Sticky-HDP-HMM

obs_distns = [pyhsmm.distributions.Gaussian(**obs_hypparams) for state in xrange(Nmax)]
posteriormodel = pyhsmm.models.StickyHMMEigen(kappa=50.,alpha=6.,gamma=6.,init_state_concentration=6.,
                                   obs_distns=obs_distns)
posteriormodel.add_data(data)

for idx in progprint_xrange(100):
    posteriormodel.resample_model()

plt.figure()
posteriormodel.plot()
plt.gcf().suptitle('Sticky HDP-HMM sampled model after 100 iterations')

plt.show()

<|endoftext|>


<commit_before>from django.core.urlresolvers import reverse
from django.contrib.auth.decorators import login_required, permission_required
from django.http import HttpResponse, HttpResponseRedirect, HttpResponseForbidden, Http404
from bawebauth.forms import DeviceForm
from bawebauth.models import Device, Usage
from django.template import RequestContext
from django.shortcuts import render_to_response, get_object_or_404
from django.contrib import messages

def show_home(request):
    return render_to_response('show_home.html', context_instance=RequestContext(request))

@login_required
def show_dashboard(request):
    devices = Device.objects.all().filter(user=request.user).order_by('name')

    template_values = {
        'devices': devices,
    }

    return render_to_response('show_dashboard.html', template_values, context_instance=RequestContext(request))

@login_required
def show_device(request, device_id):
    devices = Device.objects.all().filter(user=request.user).order_by('name')
    device = get_object_or_404(Device, user=request.user, id=device_id)

    template_values = {
        'devices': devices,
        'device': device,
    }

    return render_to_response('show_dashboard.html', template_values, context_instance=RequestContext(request))

@login_required
def edit_device(request, device_id):
    devices = Device.objects.all().filter(user=request.user).order_by('name')
    device = get_object_or_404(Device, user=request.user, id=device_id)
    edit_form = DeviceForm(instance=device, data=request.POST if request.method == 'POST' else None)

    if edit_form.is_valid():
        device = edit_form.save(commit=False)
        device.user = request.user
        device.save()
        return HttpResponseRedirect(reverse('bawebauth.views.show_device', kwargs={'device_id': device.id}))

    template_values = {
        'devices': devices,
        'device': device,
        'device_edit_form': edit_form,
    }

    return render_to_response('show_dashboard.html', template_values, context_instance=RequestContext(request))

@login_required
def switch_device(request, device_id):
    devices = Device.objects.all().filter(user=request.user).order_by('name')
    device = get_object_or_404(Device, user=request.user, id=device_id)
    device.enabled = not(device.enabled)
    device.active = True
    device.save()

    messages.success(request, 'Switched device "%s" %s!' % (device, 'on' if device.enabled else 'off'))

    template_values = {
        'devices': devices,
        'device': device,
    }

    return render_to_response('show_dashboard.html', template_values, context_instance=RequestContext(request))

@login_required
def delete_device(request, device_id):
    devices = Device.objects.all().filter(user=request.user).order_by('name')
    device = get_object_or_404(Device, user=request.user, id=device_id)
    device.delete()

    messages.success(request, 'Deleted device "%s" from your Dashboard!' % device)

    template_values = {
        'devices': devices,
    }

    return render_to_response('show_dashboard.html', template_values, context_instance=RequestContext(request))

@login_required
def delete_device_ask(request, device_id):
    devices = Device.objects.all().filter(user=request.user).order_by('name')
    device = get_object_or_404(Device, user=request.user, id=device_id)

    button = 'Yes' % reverse('bawebauth.views.delete_device', kwargs={'device_id': device.id})
    messages.warning(request, '%sDo you want to delete device "%s"?' % (button, device))

    template_values = {
        'devices': devices,
    }

    return render_to_response('show_dashboard.html', template_values, context_instance=RequestContext(request))
<commit_msg>Redirect after switch<commit_after>from django.core.urlresolvers import reverse
from django.contrib.auth.decorators import login_required, permission_required
from django.http import HttpResponse, HttpResponseRedirect, HttpResponseForbidden, Http404
from bawebauth.forms import DeviceForm
from bawebauth.models import Device, Usage
from django.template import RequestContext
from django.shortcuts import render_to_response, get_object_or_404
from django.contrib import messages

def show_home(request):
    return render_to_response('show_home.html', context_instance=RequestContext(request))

@login_required
def show_dashboard(request):
    devices = Device.objects.all().filter(user=request.user).order_by('name')

    template_values = {
        'devices': devices,
    }

    return render_to_response('show_dashboard.html', template_values, context_instance=RequestContext(request))

@login_required
def show_device(request, device_id):
    devices = Device.objects.all().filter(user=request.user).order_by('name')
    device = get_object_or_404(Device, user=request.user, id=device_id)

    template_values = {
        'devices': devices,
        'device': device,
    }

    return render_to_response('show_dashboard.html', template_values, context_instance=RequestContext(request))

@login_required
def edit_device(request, device_id):
    devices = Device.objects.all().filter(user=request.user).order_by('name')
    device = get_object_or_404(Device, user=request.user, id=device_id)
    edit_form = DeviceForm(instance=device, data=request.POST if request.method == 'POST' else None)

    if edit_form.is_valid():
        device = edit_form.save(commit=False)
        device.user = request.user
        device.save()
        return HttpResponseRedirect(reverse('bawebauth.views.show_device', kwargs={'device_id': device.id}))

    template_values = {
        'devices': devices,
        'device': device,
        'device_edit_form': edit_form,
    }

    return render_to_response('show_dashboard.html', template_values, context_instance=RequestContext(request))

@login_required
def switch_device(request, device_id):
    devices = Device.objects.all().filter(user=request.user).order_by('name')
    device = get_object_or_404(Device, user=request.user, id=device_id)
    device.enabled = not(device.enabled)
    device.active = True
    device.save()

    messages.success(request, 'Switched device "%s" %s!' % (device, 'on' if device.enabled else 'off'))

    return HttpResponseRedirect(reverse('bawebauth.views.show_device', kwargs={'device_id': device.id}))

@login_required
def delete_device(request, device_id):
    devices = Device.objects.all().filter(user=request.user).order_by('name')
    device = get_object_or_404(Device, user=request.user, id=device_id)
    device.delete()

    messages.success(request, 'Deleted device "%s" from your Dashboard!' % device)

    template_values = {
        'devices': devices,
    }

    return render_to_response('show_dashboard.html', template_values, context_instance=RequestContext(request))

@login_required
def delete_device_ask(request, device_id):
    devices = Device.objects.all().filter(user=request.user).order_by('name')
    device = get_object_or_404(Device, user=request.user, id=device_id)

    button = 'Yes' % reverse('bawebauth.views.delete_device', kwargs={'device_id': device.id})
    messages.warning(request, '%sDo you want to delete device "%s"?' % (button, device))

    template_values = {
        'devices': devices,
    }

    return render_to_response('show_dashboard.html', template_values, context_instance=RequestContext(request))
<|endoftext|>


<commit_before>"""Provide summary details on damage filtering for MultiQC.
"""
import os

from bcbio import utils
from bcbio.distributed.transaction import file_transaction
from bcbio.pipeline import datadict as dd
from bcbio.provenance import do

def run(bam_file, data, out_dir):
    out = {}
    if data.get("vrn_file"):
        out_file = os.path.join(utils.safe_makedir(out_dir),
                                "%s-damage.yaml" % (dd.get_sample_name(data)))
        if not utils.file_exists(out_file):
            with file_transaction(data, out_file) as tx_out_file:
                cmd = ["dkfzbiasfilter_summarize.py", "--sample=%s" % dd.get_sample_name(data),
                       "--outfile=%s" % tx_out_file]
                do.run(cmd, "Summarize damage filtering")
        if utils.file_exists(out_file):
            out["base"] = out_file
    return out
<commit_msg>QC: correctly pass VCF file to damage summary<commit_after>"""Provide summary details on damage filtering for MultiQC.
"""
import os

from bcbio import utils
from bcbio.distributed.transaction import file_transaction
from bcbio.pipeline import datadict as dd
from bcbio.provenance import do

def run(bam_file, data, out_dir):
    out = {}
    if data.get("vrn_file"):
        out_file = os.path.join(utils.safe_makedir(out_dir),
                                "%s-damage.yaml" % (dd.get_sample_name(data)))
        if not utils.file_exists(out_file):
            with file_transaction(data, out_file) as tx_out_file:
                cmd = ["dkfzbiasfilter_summarize.py", "--sample=%s" % dd.get_sample_name(data),
                       "--outfile=%s" % tx_out_file, data["vrn_file"]]
                do.run(cmd, "Summarize damage filtering")
        if utils.file_exists(out_file):
            out["base"] = out_file
    return out
<|endoftext|>


<commit_before>from .bcdata import bcdc_package_show
from .bcdata import get_data
from .bcdata import get_features
from .bcdata import get_count
from .bcdata import list_tables
from .bcdata import validate_name
from .bcdata import define_request

import logging

__version__ = "0.3.0"

BCDC_API_URL = "https://catalogue.data.gov.bc.ca/api/3/action/"
WFS_URL = "https://openmaps.gov.bc.ca/geo/pub/wfs"
OWS_URL = "http://openmaps.gov.bc.ca/geo/ows"

def configure_logging():
    logger = logging.getLogger()
    formatter = logging.Formatter("%(asctime)s %(name)-12s %(levelname)-8s %(message)s")
    logger.setLevel(logging.INFO)

    streamhandler = logging.StreamHandler()
    streamhandler.setFormatter(formatter)
    streamhandler.setLevel(logging.INFO)
    logger.addHandler(streamhandler)
<commit_msg>bump version<commit_after>from .bcdata import bcdc_package_show
from .bcdata import get_data
from .bcdata import get_features
from .bcdata import get_count
from .bcdata import list_tables
from .bcdata import validate_name
from .bcdata import define_request

import logging

__version__ = "0.3.1dev"

BCDC_API_URL = "https://catalogue.data.gov.bc.ca/api/3/action/"
WFS_URL = "https://openmaps.gov.bc.ca/geo/pub/wfs"
OWS_URL = "http://openmaps.gov.bc.ca/geo/ows"

def configure_logging():
    logger = logging.getLogger()
    formatter = logging.Formatter("%(asctime)s %(name)-12s %(levelname)-8s %(message)s")
    logger.setLevel(logging.INFO)

    streamhandler = logging.StreamHandler()
    streamhandler.setFormatter(formatter)
    streamhandler.setLevel(logging.INFO)
    logger.addHandler(streamhandler)
<|endoftext|>


<commit_before>from collections import Counter
from datetime import datetime
import enumerations
import pytz
import re

# the tzinfo (a.k.a. timezone) defaults to UTC
# you can override this by setting bdates.timezone to what you wish
global tzinfo
tzinfo = pytz.UTC

month_to_number = {
                 "Jan": 1,
                 "January": 1,
                 "Feb": 2,
                 "Febuary": 2,
                 "February": 2,
                 "Mar": 3,
                 "March": 3,
                 "Apr": 4,
                 "April": 4,
                 "May": 5,
                 "Jun": 6,
                 "June": 6,
                 "Jul": 7,
                 "July": 7,
                 "Aug": 8,
                 "August": 8,
                 "Sep": 9,
                 "Sept": 9,
                 "September": 9,
                 "Oct": 10,
                 "October": 10,
                 "Nov": 11,
                 "November": 11,
                 "Dec": 12,
                 "December": 12
}

def generate_patterns():
    global patterns
    patterns = {}

    # iterate through the names of the variables in the enumerations
    for key in dir(enumerations):

        # ignore inherited methods that come with most python modules
        # also ignore short variables of 1 length
        if not key.startswith("__") and len(key) > 1:
            pattern = "(?P<" + key + ">" + "|".join(getattr(enumerations, key)) + ")"

            # check to see if pattern is in unicode
            # if it's not convert it
            if isinstance(pattern, str):
                pattern = pattern.decode("utf-8")

            patterns[key] = pattern

    #merge months as regular name, abbreviation and number all together
    patterns['day'] = u'(?P<day_of_the_month>' + patterns['days_of_the_month_as_numbers'] + u'|' + patterns['days_of_the_month_as_ordinal'] + ')(?!\d{2,4})'

    #merge months as regular name, abbreviation and number all together
    # makes sure that it doesn't pull out 3 as the month in January 23, 2015
    patterns['month'] = u'(?<!\d)(?P<month>' + patterns['months_verbose'] + u'|' + patterns['months_abbreviated'] + u'|' + patterns['months_as_numbers'] + u')'

    # matches the year as two digits or four
    # tried to match the four digits first
    # (?!, \d{2,4}) makes sure it doesn't pick out 23 as the year in January 23, 2015
    patterns['year'] = u'(?P<year>\d{4}|\d{2})(?!, \d{2,4})'

    # spaces or punctuation separatings days, months and years
    # blank space, comma, dash, period, backslash
    # todo: write code for forward slash, an escape character
    patterns['punctuation'] = u"(?: |,|-|\.|\/){1,2}"

global patterns
generate_patterns()

def get_date_from_match_group(match):
    #print "starting get_date_from_match_group with ", match
    #print dir(match)
    #print match.group(0)
    #print match.groupdict()
    month = match.group("month")
    if month.isdigit():
        month = int(month)
    else:
        month = month_to_number[month]

    try:
        day = int(match.group("day_of_the_month"))
    except Exception as e:
        #print "exception is", e
        day = 1
    try:
        return datetime(int(match.group("year")), month, day, tzinfo=tzinfo)
    except Exception as e:
        print e

def extract_dates(text):
    global patterns

    # convert to unicode if the text is in a bytestring
    # we conver to unicode because it is easier to work with
    # and it handles text in foreign languages much better
    if isinstance(text, str):
        text = text.decode('utf-8')

    dates = []

    matches = []

    # add day month year to matches
    for match in re.finditer(re.compile(u"(?P<date>" + "(" + patterns['day'] + patterns['punctuation'] + ")?" + patterns['month'] + patterns['punctuation'] + patterns['year'] + u")", re.MULTILINE|re.IGNORECASE), text):
        date = get_date_from_match_group(match)
        if date:
            dates.append(date)

    # add month day year to matches
    for match in re.finditer(re.compile(u"(?P<date>" + patterns['month'] + patterns['punctuation'] + patterns['day'] + patterns['punctuation'] + patterns['year'] + u")", re.MULTILINE|re.IGNORECASE), text):
        date = get_date_from_match_group(match)
        if date:
            dates.append(date)

    # add year month day to matches
    # to make sure don't match 23 May 2015 as May 2, 2023
    for match in re.finditer(re.compile(u"(?P<date>" + patterns['year'] + patterns['punctuation'] + patterns['month'] + patterns['punctuation'] + patterns['day'] + u")", re.MULTILINE|re.IGNORECASE), text):
        date = get_date_from_match_group(match)
        if date:
            dates.append(date)

    # sorts the dates by the number of times they appear in the text
    dates = [date for date, freq in Counter(dates).most_common()]

    return dates
<commit_msg>fixed month_to_number lookup where month isn't capitalized/titled for some reason<commit_after>from collections import Counter
from datetime import datetime
import enumerations
import pytz
import re

# the tzinfo (a.k.a. timezone) defaults to UTC
# you can override this by setting bdates.timezone to what you wish
global tzinfo
tzinfo = pytz.UTC

month_to_number = {
                 "Jan": 1,
                 "January": 1,
                 "Feb": 2,
                 "Febuary": 2,
                 "February": 2,
                 "Mar": 3,
                 "March": 3,
                 "Apr": 4,
                 "April": 4,
                 "May": 5,
                 "Jun": 6,
                 "June": 6,
                 "Jul": 7,
                 "July": 7,
                 "Aug": 8,
                 "August": 8,
                 "Sep": 9,
                 "Sept": 9,
                 "September": 9,
                 "Oct": 10,
                 "October": 10,
                 "Nov": 11,
                 "November": 11,
                 "Dec": 12,
                 "December": 12
}

def generate_patterns():
    global patterns
    patterns = {}

    # iterate through the names of the variables in the enumerations
    for key in dir(enumerations):

        # ignore inherited methods that come with most python modules
        # also ignore short variables of 1 length
        if not key.startswith("__") and len(key) > 1:
            pattern = "(?P<" + key + ">" + "|".join(getattr(enumerations, key)) + ")"

            # check to see if pattern is in unicode
            # if it's not convert it
            if isinstance(pattern, str):
                pattern = pattern.decode("utf-8")

            patterns[key] = pattern

    #merge months as regular name, abbreviation and number all together
    patterns['day'] = u'(?P<day_of_the_month>' + patterns['days_of_the_month_as_numbers'] + u'|' + patterns['days_of_the_month_as_ordinal'] + ')(?!\d{2,4})'

    #merge months as regular name, abbreviation and number all together
    # makes sure that it doesn't pull out 3 as the month in January 23, 2015
    patterns['month'] = u'(?<!\d)(?P<month>' + patterns['months_verbose'] + u'|' + patterns['months_abbreviated'] + u'|' + patterns['months_as_numbers'] + u')'

    # matches the year as two digits or four
    # tried to match the four digits first
    # (?!, \d{2,4}) makes sure it doesn't pick out 23 as the year in January 23, 2015
    patterns['year'] = u'(?P<year>\d{4}|\d{2})(?!, \d{2,4})'

    # spaces or punctuation separatings days, months and years
    # blank space, comma, dash, period, backslash
    # todo: write code for forward slash, an escape character
    patterns['punctuation'] = u"(?: |,|-|\.|\/){1,2}"

global patterns
generate_patterns()

def get_date_from_match_group(match):
    #print "starting get_date_from_match_group with ", match
    #print dir(match)
    #print match.group(0)
    #print match.groupdict()
    month = match.group("month")
    if month.isdigit():
        month = int(month)
    else:
        month = month_to_number[month.title()]

    try:
        day = int(match.group("day_of_the_month"))
    except Exception as e:
        #print "exception is", e
        day = 1
    try:
        return datetime(int(match.group("year")), month, day, tzinfo=tzinfo)
    except Exception as e:
        print e

def extract_dates(text):
    global patterns

    # convert to unicode if the text is in a bytestring
    # we conver to unicode because it is easier to work with
    # and it handles text in foreign languages much better
    if isinstance(text, str):
        text = text.decode('utf-8')

    dates = []

    matches = []

    # add day month year to matches
    for match in re.finditer(re.compile(u"(?P<date>" + "(" + patterns['day'] + patterns['punctuation'] + ")?" + patterns['month'] + patterns['punctuation'] + patterns['year'] + u")", re.MULTILINE|re.IGNORECASE), text):
        date = get_date_from_match_group(match)
        if date:
            dates.append(date)

    # add month day year to matches
    for match in re.finditer(re.compile(u"(?P<date>" + patterns['month'] + patterns['punctuation'] + patterns['day'] + patterns['punctuation'] + patterns['year'] + u")", re.MULTILINE|re.IGNORECASE), text):
        date = get_date_from_match_group(match)
        if date:
            dates.append(date)

    # add year month day to matches
    # to make sure don't match 23 May 2015 as May 2, 2023
    for match in re.finditer(re.compile(u"(?P<date>" + patterns['year'] + patterns['punctuation'] + patterns['month'] + patterns['punctuation'] + patterns['day'] + u")", re.MULTILINE|re.IGNORECASE), text):
        date = get_date_from_match_group(match)
        if date:
            dates.append(date)

    # sorts the dates by the number of times they appear in the text
    dates = [date for date, freq in Counter(dates).most_common()]

    return dates
<|endoftext|>


<commit_before>"""Handles low-level interfacing for files' tags. Wraps Mutagen to
automatically detect file types and provide a unified interface for a useful
subset of music files' tags.

Usage:
>>> f = MediaFile('Lucy.mp3')
>>> f.title
u'Lucy in the Sky with Diamonds'
>>> f.artist = 'The Beatles'
>>> f.save()

A field will always return a reasonable value of the correct type, even if no
tag is present. If no value is available, the value will be false (e.g., zero
or the empty string)."""

import mutagen
import os.path

__all__ = ['FileTypeError', 'MediaFile']

# Currently allowed values for type:
# mp3, mp4
class FileTypeError(IOError):
    pass

#### utility functions ####

def fromslashed(slashed, sep=u'/'):
    """Extract a pair of items from a slashed string. If only one
    value is present, it is assumed to be the left-hand value."""

    if slashed is None:
        return (None, None)

    items = slashed.split(sep)

    if len(items) == 1:
        out = (items[0], None)
    else:
        out = (items[0], items[1])

    # represent "nothing stored" more gracefully
    if out[0] == '': out[0] = None
    if out[1] == '': out[1] = None

    return out

def toslashed(pair_or_val, sep=u'/'):
    """Store a pair of items or a single item in a slashed string. If
    only one value is provided (in a list/tuple or as a single value),
    no slash is used."""
    if type(pair_or_val) is list or type(pair_or_val) is tuple:
        if len(pair_or_val) == 0:
            out = [u'']
        elif len(pair_or_val) == 1:
            out = [unicode(pair_or_val[0])]
        else:
            out = [unicode(pair_or_val[0]), unicode(pair_or_val[1])]
    else: # "scalar"
        out = [unicode(pair_or_val)]
    return sep.join(out)

def unpair(pair, right=False, noneval=None):
    """Return the left or right value in a pair (as selected by the "right"
    parameter. If the value on that side is not available, return noneval.)"""
    if right: idx = 1
    else: idx = 0

    try:
        out = pair[idx]
    except:
        out = None
    finally:
        if out is None:
            return noneval
        else:
            return out

def normalize_pair(pair, noneval=None):
    """Make sure the pair is a tuple that has exactly two entries. If we need
    to fill anything in, we'll use noneval."""
    return (unpair(pair, False, noneval),
            unpair(pair, True, noneval))

#### flags used to define fields' behavior ####

# mutually exclusive; choose one
STORE_RAW =     0      # stored as a single object (not in a list)
STORE_LIST =    1 << 0 # stored in the first element of a list

# mutually exclusive; choose one
STORE_UNICODE = 1 << 1 # stored as a unicode string
STORE_INTEGER = 1 << 2 # as an int
STORE_BOOLEAN = 1 << 3 # as a bool
STORE_SLASHED = 1 << 4 # int stored in a string on one side of a / char
STORE_2PLE =    1 << 5 # stored as one value in an integer 2-tuple

# for use with STORE_SLASHED and STORE_2PLE; mutually exclusive; choose one
STORE_LEFT =    1 << 6 # value is in first entry
STORE_RIGHT =   1 << 7 # in second entry

class MediaField(object):
    """A descriptor providing access to a particular (abstract) metadata
    field. The various messy parameters control the translation to concrete
    metadata manipulations in the language of mutagen."""

    def __init__(self, id3key, mp4key, flackey,
            # in ID3 tags, use only the frame with this "desc" field
            id3desc=None,
            # the field's semantic (Python) type
            out_type = unicode,
            # compositions of the STORE_ flags above
            id3_style =  STORE_UNICODE | STORE_LIST,
            mp4_style =  STORE_UNICODE | STORE_LIST,
            flac_style = STORE_UNICODE | STORE_LIST
            ):

        self.id3desc = id3desc
        self.out_type = out_type
        self.keys = { 'mp3':  id3key,
                      'mp4':  mp4key,
                      'flac': flackey }
        self.styles = { 'mp3':  id3_style,
                        'mp4':  mp4_style,
                        'flac': flac_style }

    def _fetchdata(self, obj):
        """Get the value associated with this descriptor's key (and id3desc if
        present) from the mutagen tag dict. Unwraps from a list if
        necessary."""
        (key, style) = self._params(obj)

        try:
            # fetch the value, which may be a scalar or a list
            if obj.type == 'mp3':
                if self.id3desc is not None: # also match on 'desc' field
                    frames = obj.mgfile.tags.getall(key)
                    entry = None
                    for frame in frames:
                        if frame.desc == self.id3desc:
                            entry = frame.text
                            break
                    if entry is None: # no desc match
                        return None
                else:
                    entry = obj.mgfile[key].text
            else:
                entry = obj.mgfile[key]

            # possibly index the list
            if style & STORE_LIST:
                return entry[0]
            else:
                return entry
        except KeyError: # the tag isn't present
            return None

    def _storedata(self, obj, val):
        """Store val for this descriptor's key in the tag dictionary. Store it
        as a single-item list if necessary. Uses id3desc if present."""
        (key, style) = self._params(obj)

        # wrap as a list if necessary
        if style & STORE_LIST: out = [val]
        else:                         out = val

        if obj.type == 'mp3':
            if self.id3desc is not None: # match on id3desc
                frames = obj.mgfile.tags.getall(key)

                # try modifying in place
                found = False
                for frame in frames:
                    if frame.desc == self.id3desc:
                        frame.text = out
                        found = True
                        break

                # need to make a new frame?
                if not found:
                    frame = mutagen.id3.Frames[key](
                                encoding=3, desc=self.id3desc, text=val)
                    obj.mgfile.tags.add(frame)

            else: # no match on desc; just replace based on key
                frame = mutagen.id3.Frames[key](encoding=3, text=val)
                obj.mgfile.tags.setall(key, [frame])
        else:
            obj.mgfile[key] = out

    def _params(self, obj):
        return (self.keys[obj.type],
                self.styles[obj.type])

    def __get__(self, obj, owner):
        """Retrieve the value of this metadata field."""
        (key, style) = self._params(obj)
        out = self._fetchdata(obj)

        # deal with slashed and tuple storage
        if style & STORE_SLASHED or style & STORE_2PLE:
            if style & STORE_SLASHED:
                out = fromslashed(out)
            out = unpair(out, style & STORE_RIGHT, noneval=0)

        # return the appropriate type
        if self.out_type == int:
            if out is None:
                return 0
            else:
                try:
                    return int(out)
                except: # in case out is not convertible directly to an int
                    return int(unicode(out))
        elif self.out_type == bool:
            if out is None:
                return False
            else:
                return bool(int(out)) # should work for strings, bools, ints
        elif self.out_type == unicode:
            if out is None:
                return u''
            else:
                return unicode(out)
        else:
            return out

    def __set__(self, obj, val):
        """Set the value of this metadata field."""
        (key, style) = self._params(obj)

        # possibly pack a slashed or tuple
        if style & STORE_SLASHED or style & STORE_2PLE:
            # fetch the existing value so we can preserve half of it
            pair = self._fetchdata(obj)
            if style & STORE_SLASHED:
                pair = fromslashed(pair)
            pair = normalize_pair(pair, noneval=0)

            # set the appropriate side of the pair
            if style & STORE_LEFT:
                pair = (val, pair[1])
            else:
                pair = (pair[0], val)

            if style & STORE_SLASHED:
                out = toslashed(pair)
            else:
                out = pair
        else: # plain, integer, or boolean
            out = val

        # deal with Nones according to abstract type if present
        if out is None:
            if self.out_type == int:
                out = 0
            elif self.out_type == bool:
                out = False
            elif self.out_type == unicode:
                out = u''
            # We trust that SLASHED and 2PLE are handled above.

        # convert to correct storage type
        if style & STORE_UNICODE:
            if out is None:
                out = u''
            else:
                if self.out_type == bool:
                    # store bools as 1,0 instead of True,False
                    out = unicode(int(out))
                else:
                    out = unicode(out)
        elif style & STORE_INTEGER:
            if out is None:
                out = 0
            else:
                out = int(out)
        elif style & STORE_BOOLEAN:
            out = bool(out)

        # store the data
        self._storedata(obj, out)

class MediaFile(object):
    """Represents a multimedia file on disk and provides access to its
    metadata."""

    def __init__(self, path):
        self.mgfile = mutagen.File(path)
        if self.mgfile is None: # Mutagen couldn't guess the type
            raise FileTypeError('file type unsupported by Mutagen')
        elif type(self.mgfile).__name__ == 'M4A' or \
             type(self.mgfile).__name__ == 'MP4':
            self.type = 'mp4'
        elif type(self.mgfile).__name__ == 'ID3' or \
             type(self.mgfile).__name__ == 'MP3':
            self.type = 'mp3'
        elif type(self.mgfile).__name__ == 'FLAC':
            self.type = 'flac'
        else:
            raise FileTypeError('file type unsupported by MediaFile')

        # add a set of tags if it's missing
        if not self.mgfile.tags:
            self.mgfile.add_tags()

    def save(self):
        self.mgfile.save()

    #### field definitions ####

    title = MediaField('TIT2', "\xa9nam", 'title')
    artist = MediaField('TPE1', "\xa9ART", 'artist')
    album = MediaField('TALB', "\xa9alb", 'album')
    genre = MediaField('TCON', "\xa9gen", 'genre')
    composer = MediaField('TCOM', "\xa9wrt", 'composer')
    grouping = MediaField('TIT1', "\xa9grp", 'grouping')
    year = MediaField('TDRC', "\xa9day", 'date', out_type=int)
    track = MediaField('TRCK', 'trkn', 'tracknumber',
                id3_style = STORE_LIST | STORE_SLASHED | STORE_LEFT,
                mp4_style = STORE_LIST | STORE_2PLE | STORE_LEFT,
                out_type = int)
    tracktotal = MediaField('TRCK', 'trkn', 'tracktotal',
                id3_style = STORE_LIST | STORE_SLASHED | STORE_RIGHT,
                mp4_style = STORE_LIST | STORE_2PLE | STORE_RIGHT,
                out_type = int)
    disc = MediaField('TPOS', 'disk', 'disc',
                id3_style = STORE_LIST | STORE_SLASHED | STORE_LEFT,
                mp4_style = STORE_LIST | STORE_2PLE | STORE_LEFT,
                out_type = int)
    disctotal = MediaField('TPOS', 'disk', 'disctotal',
                id3_style = STORE_LIST | STORE_SLASHED | STORE_RIGHT,
                mp4_style = STORE_LIST | STORE_2PLE | STORE_RIGHT,
                out_type = int)
    lyrics = MediaField(u"USLT", "\xa9lyr", 'lyrics',
                id3desc = u'',
                id3_style = STORE_RAW | STORE_UNICODE)
    comments = MediaField(u"COMM", "\xa9cmt", 'description',
                id3desc = u'')
    bpm = MediaField('TBPM', 'tmpo', 'bpm',
                mp4_style = STORE_LIST | STORE_INTEGER,
                out_type = int)
    comp = MediaField('TCMP', 'cpil', 'compilation',
                mp4_style = STORE_RAW | STORE_BOOLEAN,
                out_type = bool)
<commit_msg>regorganized flags into StorageStyle objects<commit_after>"""Handles low-level interfacing for files' tags. Wraps Mutagen to
automatically detect file types and provide a unified interface for a useful
subset of music files' tags.

Usage:
>>> f = MediaFile('Lucy.mp3')
>>> f.title
u'Lucy in the Sky with Diamonds'
>>> f.artist = 'The Beatles'
>>> f.save()

A field will always return a reasonable value of the correct type, even if no
tag is present. If no value is available, the value will be false (e.g., zero
or the empty string)."""

import mutagen
import os.path

__all__ = ['FileTypeError', 'MediaFile']

# Currently allowed values for type:
# mp3, mp4
class FileTypeError(IOError):
    pass

#### utility functions ####

def fromslashed(slashed, sep=u'/'):
    """Extract a pair of items from a slashed string. If only one
    value is present, it is assumed to be the left-hand value."""

    if slashed is None:
        return (None, None)

    items = slashed.split(sep)

    if len(items) == 1:
        out = (items[0], None)
    else:
        out = (items[0], items[1])

    # represent "nothing stored" more gracefully
    if out[0] == '': out[0] = None
    if out[1] == '': out[1] = None

    return out

def toslashed(pair_or_val, sep=u'/'):
    """Store a pair of items or a single item in a slashed string. If
    only one value is provided (in a list/tuple or as a single value),
    no slash is used."""
    if type(pair_or_val) is list or type(pair_or_val) is tuple:
        if len(pair_or_val) == 0:
            out = [u'']
        elif len(pair_or_val) == 1:
            out = [unicode(pair_or_val[0])]
        else:
            out = [unicode(pair_or_val[0]), unicode(pair_or_val[1])]
    else: # "scalar"
        out = [unicode(pair_or_val)]
    return sep.join(out)

def unpair(pair, right=False, noneval=None):
    """Return the left or right value in a pair (as selected by the "right"
    parameter. If the value on that side is not available, return noneval.)"""
    if right: idx = 1
    else: idx = 0

    try:
        out = pair[idx]
    except:
        out = None
    finally:
        if out is None:
            return noneval
        else:
            return out

def normalize_pair(pair, noneval=None):
    """Make sure the pair is a tuple that has exactly two entries. If we need
    to fill anything in, we'll use noneval."""
    return (unpair(pair, False, noneval),
            unpair(pair, True, noneval))

#### flags used to define fields' behavior ####

class Enumeration(object):
    def __init__(self, *values):
        for value, equiv in zip(values, range(1, len(values)+1)):
            setattr(self, value, equiv)
packing = Enumeration('SLASHED', 'TUPLE')

class StorageStyle(object):
    """Parameterizes the storage behavior of a single field for a certain tag
    format."""
    def __init__(self,
                 # Store item as a single object or as first element of a list.
                 list_elem = True,
                 # Which type the value is stored as (unicode, int, or bool).
                 as_type = unicode,
                 # If this value is packed in a multiple-value storage unit,
                 # which type of packing (in the packing enum). Otherwise,
                 # None. (Makes as_type irrelevant).
                 packing = None,
                 # If the value is packed, in which position it is stored.
                 pack_pos = 0):
        self.list_elem = list_elem
        self.as_type = as_type
        self.packing = packing
        self.pack_pos = pack_pos

class MediaField(object):
    """A descriptor providing access to a particular (abstract) metadata
    field. The various messy parameters control the translation to concrete
    metadata manipulations in the language of mutagen."""

    def __init__(self, id3key, mp4key, flackey,
            # in ID3 tags, use only the frame with this "desc" field
            id3desc=None,
            # the field's semantic (exterior) type
            out_type = unicode,
            # StorageStyle instances parameterizing the field's storage
            id3_style =  StorageStyle(),
            mp4_style =  StorageStyle(),
            flac_style = StorageStyle()
            ):

        self.id3desc = id3desc
        self.out_type = out_type
        self.keys = { 'mp3':  id3key,
                      'mp4':  mp4key,
                      'flac': flackey }
        self.styles = { 'mp3':  id3_style,
                        'mp4':  mp4_style,
                        'flac': flac_style }

    def _fetchdata(self, obj):
        """Get the value associated with this descriptor's key (and id3desc if
        present) from the mutagen tag dict. Unwraps from a list if
        necessary."""
        (key, style) = self._params(obj)

        try:
            # fetch the value, which may be a scalar or a list
            if obj.type == 'mp3':
                if self.id3desc is not None: # also match on 'desc' field
                    frames = obj.mgfile.tags.getall(key)
                    entry = None
                    for frame in frames:
                        if frame.desc == self.id3desc:
                            entry = frame.text
                            break
                    if entry is None: # no desc match
                        return None
                else:
                    entry = obj.mgfile[key].text
            else:
                entry = obj.mgfile[key]

            # possibly index the list
            if style.list_elem:
                return entry[0]
            else:
                return entry
        except KeyError: # the tag isn't present
            return None

    def _storedata(self, obj, val):
        """Store val for this descriptor's key in the tag dictionary. Store it
        as a single-item list if necessary. Uses id3desc if present."""
        (key, style) = self._params(obj)

        # wrap as a list if necessary
        if style.list_elem: out = [val]
        else:             out = val

        if obj.type == 'mp3':
            if self.id3desc is not None: # match on id3desc
                frames = obj.mgfile.tags.getall(key)

                # try modifying in place
                found = False
                for frame in frames:
                    if frame.desc == self.id3desc:
                        frame.text = out
                        found = True
                        break

                # need to make a new frame?
                if not found:
                    frame = mutagen.id3.Frames[key](
                                encoding=3, desc=self.id3desc, text=val)
                    obj.mgfile.tags.add(frame)

            else: # no match on desc; just replace based on key
                frame = mutagen.id3.Frames[key](encoding=3, text=val)
                obj.mgfile.tags.setall(key, [frame])
        else:
            obj.mgfile[key] = out

    def _params(self, obj):
        return (self.keys[obj.type],
                self.styles[obj.type])

    def __get__(self, obj, owner):
        """Retrieve the value of this metadata field."""
        (key, style) = self._params(obj)
        out = self._fetchdata(obj)

        # deal with slashed and tuple storage
        if style.packing:
            if style.packing == packing.SLASHED:
                out = fromslashed(out)
            out = unpair(out, style.pack_pos, noneval=0)

        # return the appropriate type
        if self.out_type == int:
            if out is None:
                return 0
            else:
                try:
                    return int(out)
                except: # in case out is not convertible directly to an int
                    return int(unicode(out))
        elif self.out_type == bool:
            if out is None:
                return False
            else:
                return bool(int(out)) # should work for strings, bools, ints
        elif self.out_type == unicode:
            if out is None:
                return u''
            else:
                return unicode(out)
        else:
            return out

    def __set__(self, obj, val):
        """Set the value of this metadata field."""
        (key, style) = self._params(obj)

        if style.packing:
            # fetch the existing value so we can preserve half of it
            pair = self._fetchdata(obj)
            if style.packing == packing.SLASHED:
                pair = fromslashed(pair)
            pair = normalize_pair(pair, noneval=0)

            # set the appropriate side of the pair
            if style.pack_pos == 0:
                pair = (val, pair[1])
            else:
                pair = (pair[0], val)

            if style.packing == packing.SLASHED:
                out = toslashed(pair)
            else:
                out = pair

        else: # unicode, integer, or boolean
            out = val

            # deal with Nones according to abstract type if present
            if out is None:
                if self.out_type == int:
                    out = 0
                elif self.out_type == bool:
                    out = False
                elif self.out_type == unicode:
                    out = u''
                # We trust that packed values are handled above.

            # convert to correct storage type (irrelevant for packed values)
            if style.as_type == unicode:
                if out is None:
                    out = u''
                else:
                    if self.out_type == bool:
                        # store bools as 1,0 instead of True,False
                        out = unicode(int(out))
                    else:
                        out = unicode(out)
            elif style.as_type == int:
                if out is None:
                    out = 0
                else:
                    out = int(out)
            elif style.as_type == bool:
                out = bool(out)

        # store the data
        self._storedata(obj, out)

class MediaFile(object):
    """Represents a multimedia file on disk and provides access to its
    metadata."""

    def __init__(self, path):
        self.mgfile = mutagen.File(path)
        if self.mgfile is None: # Mutagen couldn't guess the type
            raise FileTypeError('file type unsupported by Mutagen')
        elif type(self.mgfile).__name__ == 'M4A' or \
             type(self.mgfile).__name__ == 'MP4':
            self.type = 'mp4'
        elif type(self.mgfile).__name__ == 'ID3' or \
             type(self.mgfile).__name__ == 'MP3':
            self.type = 'mp3'
        elif type(self.mgfile).__name__ == 'FLAC':
            self.type = 'flac'
        else:
            raise FileTypeError('file type unsupported by MediaFile')

        # add a set of tags if it's missing
        if not self.mgfile.tags:
            self.mgfile.add_tags()

    def save(self):
        self.mgfile.save()

    #### field definitions ####

    title = MediaField('TIT2', "\xa9nam", 'title')
    artist = MediaField('TPE1', "\xa9ART", 'artist')
    album = MediaField('TALB', "\xa9alb", 'album')
    genre = MediaField('TCON', "\xa9gen", 'genre')
    composer = MediaField('TCOM', "\xa9wrt", 'composer')
    grouping = MediaField('TIT1', "\xa9grp", 'grouping')
    year = MediaField('TDRC', "\xa9day", 'date', out_type=int)
    track = MediaField('TRCK', 'trkn', 'tracknumber',
                id3_style = StorageStyle(packing = packing.SLASHED,
                                pack_pos = 0),
                mp4_style = StorageStyle(packing = packing.TUPLE,
                                pack_pos = 0),
                out_type = int)
    tracktotal = MediaField('TRCK', 'trkn', 'tracktotal',
                id3_style = StorageStyle(packing = packing.SLASHED,
                                pack_pos = 1),
                mp4_style = StorageStyle(packing = packing.TUPLE,
                                pack_pos = 1),
                out_type = int)
    disc = MediaField('TPOS', 'disk', 'disc',
                id3_style = StorageStyle(packing = packing.SLASHED,
                                pack_pos = 0),
                mp4_style = StorageStyle(packing = packing.TUPLE,
                                pack_pos = 0),
                out_type = int)
    disctotal = MediaField('TPOS', 'disk', 'disctotal',
                id3_style = StorageStyle(packing = packing.SLASHED,
                                pack_pos = 1),
                mp4_style = StorageStyle(packing = packing.TUPLE,
                                pack_pos = 1),
                out_type = int)
    lyrics = MediaField(u"USLT", "\xa9lyr", 'lyrics',
                id3desc = u'',
                id3_style = StorageStyle(list_elem = False))
    comments = MediaField(u"COMM", "\xa9cmt", 'description',
                id3desc = u'')
    bpm = MediaField('TBPM', 'tmpo', 'bpm',
                mp4_style = StorageStyle(as_type = int),
                out_type = int)
    comp = MediaField('TCMP', 'cpil', 'compilation',
                mp4_style = StorageStyle(list_elem = False, as_type = bool),
                out_type = bool)
<|endoftext|>