<commit_before>import os
import gzip
import bz2
import zipfile
import shutil
import tempfile
from contextlib import contextmanager

__all__ = ['compress', 'compress_as', 'decompress']

_known_compressions = {
    '.gz': gzip.GzipFile,
    '.bz2': bz2.BZ2File,
    '.zip': zipfile.ZipFile,
}

try:
    import lzma
except ImportError:  # no lzma
    pass
else:
    _known_compressions['.xz'] = lzma.LZMAFile

@contextmanager
def compress(filename, fmt=None, tmpdir=None):
    """Compress a file after writing to it.

    Supported compression formats are: gzip, bzip2, zip, and lzma (Python
    3.3 or newer only).

    This function is tailored for use in a with statement. It uses a context
    manager to automatically create a temporary file to which the data
    can be written. After writing to the file, it compresses and renames it.

    TODO: Preferably, it should be possible to write to the compressed file
    directly instead of copying it.
    TODO: Contains a bug. The original file won't be preserved, it changes
    to the name of the temporary file.

    Args:
        filename: The path and name of the compressed that should be
            created. The filename's extension decides to which format the
            file will be compressed (look at *fmt* for a list).
        fmt: (optional) If want to specify the compression format without
            the filename's extension, use this argument.
            * *zip*: Uses the standard zip library.
            * *bz2*: Uses the bz2 library.
            * *gz*: Uses the GNU zip library.
            * *xz*: Uses the lzma format.
        tmpdir (str): Path to directory for temporary storage of the
            uncompressed file. The directory must exist. The default is the
            temporary dir of the system.

    Yields:
        Generator containing the path to the temporary file.

    Examples:
        >>> with typhon.files.compress('datafile.txt.gz') as file:
        >>>     f = netCDF4.Dataset(file, 'w')
        >>>     #...
    """
    if fmt is None:
        _, fmt = os.path.splitext(filename)

    if not is_compression_format(fmt):
        yield filename
        return

    if tmpdir is None:
        tmpdir = tempfile.gettempdir()

    with tempfile.NamedTemporaryFile(prefix=os.path.join(tmpdir, '')) as tfile:
        yield tfile.name
        compress_as(tfile.name, fmt, filename)

def compress_as(filename, fmt, target=None, keep=True):
    """Compress an existing file.

    Supported compression formats are: gzip, bzip2, zip, and lzma (Python
    3.3 or newer only).

    Args:
        filename: The path and name of the uncompressed file.
        fmt: Decides to which format the file will be compressed.
            * *zip*: Uses the standard zip library.
            * *bz2*: Uses the bz2 library.
            * *gz*: Uses the GNU zip library.
            * *xz*: Uses the lzma format.
        target: (optional) The default output filename is *filename.fmt*.
            If you do not like it, you can set another filename here.
        keep: (optional) If true, keep the original file after
            compressing. Otherwise it will be deleted. Default is keeping.

    Returns:
        The filename of the newly created file.
    """
    if not is_compression_format(fmt):
        raise ValueError("Unknown compression format '%s'!" % suffix)

    suffix = "."+fmt

    if target is None:
        target = filename+fmt

    # Read datafile in 100 MiB chunks for good performance/memory usage
    chunksize = 100 * 1024 * 1024
    compfile = _known_compressions[suffix]
    try:
        if suffix == ".zip":
            with compfile(target, 'w') as f_out:
                f_out.write(filename, compress_type=zipfile.ZIP_DEFLATED)
        else:
            with open(filename, 'rb') as f_in:
                if fmt == "gz":
                    # Coming from https://stackoverflow.com/a/38020236
                    with open(target, 'wb') as f_out:
                        with compfile(filename, 'wb', fileobj=f_out) as f_out:
                            shutil.copyfileobj(f_in, f_out, length=chunksize)
                elif fmt == "bz2" or fmt == "xz":
                    with compfile(target, 'wb') as f_out:
                        shutil.copyfileobj(f_in, f_out, length=chunksize)
    except Exception as e:
        raise e
    else:
        if not keep:
            os.unlink(filename)

    return target

@contextmanager
def decompress(filename, tmpdir=None):
    """Temporarily decompress file for reading.

    Returns the full path to the uncompressed temporary file or the original
    filename if it was not compressed.

    Supported compression formats are: gzip, bzip2, zip, and lzma (Python
    3.3 or newer only).

    This function is tailored for use in a with statement. It uses a context
    manager to automatically remove the decompressed file after use.

    Args:
        filename (str): Input file.
        tmpdir (str): Path to directory for temporary storage of the
            uncompressed file. The directory must exist. The default is the
            temporary dir of the system.

    Yields:
        Generator containing the path to the input filename.

    Example:
        >>> tmpdir = '/tmp'
        >>> with typhon.files.decompress('datafile.nc.gz', tmpdir) as file:
        >>>     f = netCDF4.Dataset(file)
        >>>     #...
    """
    if tmpdir is None:
        tmpdir = tempfile.gettempdir()

    filebase, fileext = os.path.splitext(filename)
    filebase = os.path.basename(filebase)

    if is_compression_format(fileext):
        tmpfile = tempfile.NamedTemporaryFile(prefix=os.path.join(tmpdir, ''),
                                              delete=False)
        # Read datafile in 100 MiB chunks for good performance/memory usage
        chunksize = 100 * 1024 * 1024
        compfile = _known_compressions[fileext]
        try:
            if fileext == '.zip':
                shutil.copyfileobj(compfile(filename, 'r').open(filebase, 'r'),
                                   tmpfile,
                                   chunksize)
            else:
                shutil.copyfileobj(compfile(filename, 'r'),
                                   tmpfile,
                                   chunksize)
            tmpfile.close()
            yield tmpfile.name
        finally:
            os.unlink(tmpfile.name)

    else:
        yield filename

def is_compression_format(fmt):
    if not fmt.startswith("."):
        fmt = "."+fmt

    return fmt in _known_compressions
<commit_msg>Fix various bugs from compression functions<commit_after>import os
import gzip
import bz2
import zipfile
import shutil
import tempfile
from contextlib import contextmanager

__all__ = ['compress', 'compress_as', 'decompress']

_known_compressions = {
    'gz': gzip.GzipFile,
    'bz2': bz2.BZ2File,
    'zip': zipfile.ZipFile,
}

try:
    import lzma
except ImportError:  # no lzma
    pass
else:
    _known_compressions['.xz'] = lzma.LZMAFile

@contextmanager
def compress(filename, fmt=None,):
    """Compress a file after writing to it.

    Supported compression formats are: gzip, bzip2, zip, and lzma (Python
    3.3 or newer only).

    This function is tailored for use in a with statement. It uses a context
    manager to automatically create a temporary file to which the data
    can be written. After writing to the file, it compresses and renames it.

    TODO: Preferably, it should be possible to write to the compressed file
    directly instead of copying it.
    TODO: Contains a bug. The original file won't be preserved, it changes
    to the name of the temporary file.

    Args:
        filename: The path and name of the compressed that should be
            created. The filename's extension decides to which format the
            file will be compressed (look at *fmt* for a list).
        fmt: If want to specify the compression format without using the
            filename's extension, use this argument.
            * *zip*: Uses the standard zip library.
            * *bz2*: Uses the bz2 library.
            * *gz*: Uses the GNU zip library.
            * *xz*: Uses the lzma format.

    Yields:
        Generator containing the path to the temporary file.

    Examples:
        >>> with typhon.files.compress('datafile.nc.gz') as file:
        >>>     f = netCDF4.Dataset(file, 'w')
        >>>     #...
    """
    filebase, fileext = os.path.splitext(filename)

    if fmt is None:
        fmt = fileext.lstrip(".")

    if not is_compression_format(fmt):
        yield filename
        return

    with tempfile.NamedTemporaryFile(prefix=tempfile.gettempdir()) as tfile:
        yield tfile.name
        compress_as(tfile.name, fmt, filename, True)

def compress_as(filename, fmt, target=None, keep=True):
    """Compress an existing file.

    Supported compression formats are: gzip, bzip2, zip, and lzma (Python
    3.3 or newer only).

    Args:
        filename: The path and name of the uncompressed file.
        fmt: Decides to which format the file will be compressed.
            * *zip*: Uses the standard zip library.
            * *bz2*: Uses the bz2 library.
            * *gz*: Uses the GNU zip library.
            * *xz*: Uses the lzma format.
        target: The default output filename is *filename.fmt*.
            If you do not like it, you can set another filename here.
        keep: If true, keep the original file after compressing. Otherwise it
            will be deleted. Default is keeping.

    Returns:
        The filename of the newly created file.
    """

    if not is_compression_format(fmt):
        raise ValueError("Unknown compression format '%s'!" % fmt)

    if target is None:
        target = ".".join([filename, fmt])

    # The filename inside the zipped archive
    target_filename = os.path.basename(target)
    target_filename = target_filename.strip("."+fmt)

    # Read datafile in 100 MiB chunks for good performance/memory usage
    chunksize = 100 * 1024 * 1024
    compfile = get_compressor(fmt)
    try:
        if fmt == "zip":
            with compfile(target, 'w') as f_out:
                f_out.write(
                    filename, arcname=target_filename,
                    compress_type=zipfile.ZIP_DEFLATED
                )
        else:
            with open(filename, 'rb') as f_in:
                if fmt == "gz":
                    # Coming from https://stackoverflow.com/a/38020236
                    with open(target, 'wb') as f_out:
                        with compfile(filename, 'wb', fileobj=f_out) as f_out:
                            shutil.copyfileobj(f_in, f_out, length=chunksize)
                elif fmt == "bz2" or fmt == "xz":
                    with compfile(target, 'wb') as f_out:
                        shutil.copyfileobj(f_in, f_out, length=chunksize)
    except Exception as e:
        raise e
    else:
        if not keep:
            os.unlink(filename)

    return target

@contextmanager
def decompress(filename, tmpdir=None):
    """Temporarily decompress file for reading.

    Returns the full path to the uncompressed temporary file or the original
    filename if it was not compressed.

    Supported compression formats are: gzip, bzip2, zip, and lzma (Python
    3.3 or newer only).

    This function is tailored for use in a with statement. It uses a context
    manager to automatically remove the decompressed file after use.

    Args:
        filename (str): Input file.
        tmpdir (str): Path to directory for temporary storage of the
            uncompressed file. The directory must exist. The default is the
            temporary dir of the system.

    Yields:
        Generator containing the path to the input filename.

    Example:
        >>> tmpdir = '/tmp'
        >>> with typhon.files.decompress('datafile.nc.gz', tmpdir) as file:
        >>>     f = netCDF4.Dataset(file)
        >>>     #...
    """
    if tmpdir is None:
        tmpdir = tempfile.gettempdir()

    filebase, fileext = os.path.splitext(filename)
    filebase = os.path.basename(filebase)
    fmt = fileext.lstrip(".")

    if is_compression_format(fmt):
        tmpfile = tempfile.NamedTemporaryFile(prefix=os.path.join(tmpdir, ''),
                                              delete=False)
        # Read datafile in 100 MiB chunks for good performance/memory usage
        chunksize = 100 * 1024 * 1024
        compfile = get_compressor(fmt)
        try:
            if fmt == 'zip':
                shutil.copyfileobj(compfile(filename, 'r').open(filebase, 'r'),
                                   tmpfile,
                                   chunksize)
            else:
                shutil.copyfileobj(compfile(filename, 'r'),
                                   tmpfile,
                                   chunksize)
            tmpfile.close()
            yield tmpfile.name
        finally:
            os.unlink(tmpfile.name)

    else:
        yield filename

def get_compressor(fmt):
    return _known_compressions[fmt]

def is_compression_format(fmt):
    return fmt in _known_compressions
<|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 LogCategory(orm.Model):
    """ Model name: Log Category
    """
    _name = 'log.category'
    _description = 'Log category'
    _order = 'name'

    _columns = {
        'active': fields.boolean('Active'),
        'code': fields.char('Code', size=15),
        'name': fields.char('Category', size=64, required=True),
        'note': fields.text('Note'),
        }

class LogActivity(orm.Model):
    """ Model name: Log event
    """

    _name = 'log.activity'
    _description = 'Log activity'
    _order = 'name'

    _columns = {
        'active': fields.boolean('Active'),
        'code': fields.char('Code', size=15),
        'monitor': fields.boolean(
            'Monitor', help='Monitored event are represented in dashboard'),
        'name': fields.char('Event', size=64, required=True),
        'from_date': fields.date(
            'From date',
            help='For period event, time was the current start time'),
        'to_date': fields.date('To date', help='End period for timed event'),
        'duration': fields.float(
            'Duration', digits=(16, 3), help='Normal duration of operation'),
        'check_duration':fields.boolean(
            'Check duration', help='Check duration period of operation'),
        'duration_warning_range': fields.float(
            'Warning range', digits=(16, 3),
            help='-/+ perc. time for raise warning'),
        'duration_error_range': fields.float(
            'Error range', digits=(16, 3),
            help='-/+ perc. time for raise error'),
        'auto_duration': fields.boolean(
            'Autoduration',
            help='If checked duration will be update automatically'),
        'partner_id': fields.many2one(
            'res.partner', 'Partner', required=True),
        'category_id': fields.many2one(
            'log.category', 'Category', required=True),
        'email_alert': fields.boolean('Email alert'),
        'email_error': fields.char('Email error', size=180),
        'email_warning': fields.char('Email warning', size=180),
        'script': fields.text('Script'),
        'origin': fields.text(
            'Origin', help='Info of origin server for the activity'),

        'note': fields.text('Note'),
        'state': fields.selection([
            ('unactive', 'Unactive'), # not working
            ('active', 'Active'), # Working
            ('pause', 'Pause'), # Currently not work butt soon yes
            ('timed', 'Out of time'), # Out of date period
            ], 'State',
            )
        }

    _defaults = {
        # Default value for state:
        'state': lambda *x: 'active',
        }

class LogActivityEvent(orm.Model):
    """ Model name: LogActivityEvent
    """

    _name = 'log.activity.event'
    _description = 'Event'
    _rec_name = 'datetime'
    _order = 'datetime'

    # -------------------------------------------------------------------------
    # XMLRPC Procedure:
    # -------------------------------------------------------------------------
    def log_event(self, cr, uid, data, update_id=False, context=None):
        ''' ERPEEK procedure called for create event from remote scripts
            data dict contain:
                code_partner: Key field for reach partner, inserad use copmany
                code_activity: Key field for reach activity, instead create ERR
                start: datetime start
                end: datetime stop
                origin: note for origin server
                log: log text
                log_warning: warning text
                log_error: error text
        '''
        # Pool used:
        category_pool = self.pool.get('log.category')
        activity_pool = self.pool.get('log.activity')
        partner_pool = self.pool.get('res.partner')

        # Read key field:
        code_partner = data.get('code_partner', False) # default company
        code_activity = data.get('code_activity', False)

        # ---------------------------------------------------------------------
        # Search foreign keys:
        # ---------------------------------------------------------------------
        # partner_id
        if code_partner:
            partner_ids = partner_pool.search(cr, uid, [
                ('log_code', '=', code_partner),
                ], context=context)
            if partner_ids:
                partner_id = partner_ids[0]
            else:
                _logger.error('Code partner not present %s (take company)!' % (
                    code_partner))
                partner_id = 1 # Use default
        else:
            _logger.error('Code partner present (take company)!')
            partner_id = 1 # Use detault company address

        # activity_id
        if not code_activity:
            _logger.error('Code activity not present (take ERR)!')
            code_activity = 'ERR'

        activity_ids = activity_pool.search(cr, uid, [
            ('partner_id', '=', partner_id),
            ('code', '=', code_activity),
            ], context=context)

        if activity_ids:
            # Find:
            activity_id = activity_ids[0]
        else:
            # Create new (to compile after on ODOO:
            # Get error category:
            category_ids = category_pool.search(cr, uid, [
                ('code', '=', code_activity), # use same code (cat.-act.)
                ], context=context)
            if category_ids:
                category_id = category_ids[0]
            else:
                _logger.error('Code category not present (take ERR)!')
                category_id = category_pool.create(cr, uid, {
                    'code': code_activity,
                    'name': 'Error',
                    'note': 'Log error activity of the system (admin purpose)'
                    }, context=context)

            # Get activity:
            activity_id = activity_pool.create(cr, uid, {
                'code': code_activity,
                'name': 'Error',
                'partner_id': partner_id,
                'category_id': category_id,
                #'from_date': Fa,
                #'to_date': ,
                #'duration': ,
                #'active'
                #'monitor'
                #'check_duration':
                #'duration_warning_range':
                #'duration_error_range':
                #'auto_duration':
                #'email_alert':
                #'email_error':
                #'email_warning':
                #'script':
                #'origin':
                #'note':
                #'state':
                }, context=context)
            _logger.error('Code activity not present %s create empty!' % (
                code_activity))

        # ---------------------------------------------------------------------
        # Create event:
        # ---------------------------------------------------------------------
        # Get data passed
        start = data.get('start', False)
        end = data.get('end', False)
        origin = data.get('origin', '')
        log_info = data.get('log_info', '')
        log_warning = data.get('log_warning', '')
        log_error = data.get('log_error', '')
        duration = 0 # TODO

        record = {
            #'datetime': now
            #'mark_ok': False,
            'activity_id': activity_id,
            'start': start,
            'end': end,
            'duration': duration,
            'origin': origin,
            'log_info': log_info,
            'log_warning': log_warning,
            'log_error': log_error,
            }

        if update_id:
            return self.write(cr, uid, update_id, record, context=context)
        else:
            return self.create(cr, uid, record, context=context)

    _columns = {
        'datetime': fields.date('Date', required=True),
        'activity_id': fields.many2one(
            'log.activity', 'Activity'),
        'partner_id': fields.related(
            'activity_id', 'partner_id',
            type='many2one', relation='res.partner',
            string='Partner', store=True),

        'start': fields.datetime('Start'),
        'end': fields.datetime('End'),
        'duration': fields.float(
            'Duration', digits=(16, 3), help='Duration of operation'),

        'origin': fields.text('Origin', help='Server info (log origin)'),

        'log_info': fields.text('Log info'),
        'log_warning': fields.text('Log warning'),
        'log_error': fields.text('Log error'),

        'mark_ok': fields.boolean('Mark as OK',
            help='For error / warning message'),
        'state': fields.selection([
            ('started', 'Started'), # Start
            ('correct', 'Correct'), # End
            ('warning', 'Warning'), # End with warning
            ('error', 'Error'), # End with error
            ], 'State', help='State info, not workflow management here'),
        }

    _defaults = {
        'datetime': lambda *a: datetime.now().strftime(
            DEFAULT_SERVER_DATETIME_FORMAT),
        'state': lambda *x: 'correct',
        }

class LogCategory(orm.Model):
    """ Model name: Log Category
    """
    _inherit = 'log.category'

    _columns = {
        'activity_ids': fields.one2many(
            'log.activity', 'category_id', 'Activity'),
    }

class ResUser(orm.Model):
    """ Model name: ResUser
    """
    _inherit = 'res.users'

    _columns = {
        'log_partner_id': fields.many2one('res.partner', 'Log partner'),
    }

class ResPartner(orm.Model):
    """ Model name: Res partner
    """
    _inherit = 'res.partner'

    _columns = {
        'log_code': fields.char('Partner log code', size=64,
            help='Partner code for link activity'),
        'log_users_ids': fields.one2many(
            'res.users', 'log_partner_id', 'Log user'),
        'log_activity_ids': fields.one2many(
            'log.activity', 'partner_id', 'Log activity'),
    }

# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
<commit_msg>add state managemetnt<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 LogCategory(orm.Model):
    """ Model name: Log Category
    """
    _name = 'log.category'
    _description = 'Log category'
    _order = 'name'

    _columns = {
        'active': fields.boolean('Active'),
        'code': fields.char('Code', size=15),
        'name': fields.char('Category', size=64, required=True),
        'note': fields.text('Note'),
        }

class LogActivity(orm.Model):
    """ Model name: Log event
    """

    _name = 'log.activity'
    _description = 'Log activity'
    _order = 'name'

    _columns = {
        'active': fields.boolean('Active'),
        'code': fields.char('Code', size=15),
        'monitor': fields.boolean(
            'Monitor', help='Monitored event are represented in dashboard'),
        'name': fields.char('Event', size=64, required=True),
        'from_date': fields.date(
            'From date',
            help='For period event, time was the current start time'),
        'to_date': fields.date('To date', help='End period for timed event'),
        'duration': fields.float(
            'Duration', digits=(16, 3), help='Normal duration of operation'),
        'check_duration':fields.boolean(
            'Check duration', help='Check duration period of operation'),
        'duration_warning_range': fields.float(
            'Warning range', digits=(16, 3),
            help='-/+ perc. time for raise warning'),
        'duration_error_range': fields.float(
            'Error range', digits=(16, 3),
            help='-/+ perc. time for raise error'),
        'auto_duration': fields.boolean(
            'Autoduration',
            help='If checked duration will be update automatically'),
        'partner_id': fields.many2one(
            'res.partner', 'Partner', required=True),
        'category_id': fields.many2one(
            'log.category', 'Category', required=True),
        'email_alert': fields.boolean('Email alert'),
        'email_error': fields.char('Email error', size=180),
        'email_warning': fields.char('Email warning', size=180),
        'script': fields.text('Script'),
        'origin': fields.text(
            'Origin', help='Info of origin server for the activity'),

        'note': fields.text('Note'),
        'state': fields.selection([
            ('unactive', 'Unactive'), # not working
            ('active', 'Active'), # Working
            ('pause', 'Pause'), # Currently not work butt soon yes
            ('timed', 'Out of time'), # Out of date period
            ], 'State',
            )
        }

    _defaults = {
        # Default value for state:
        'state': lambda *x: 'active',
        }

class LogActivityEvent(orm.Model):
    """ Model name: LogActivityEvent
    """

    _name = 'log.activity.event'
    _description = 'Event'
    _rec_name = 'datetime'
    _order = 'datetime'

    # -------------------------------------------------------------------------
    # XMLRPC Procedure:
    # -------------------------------------------------------------------------
    def log_event(self, cr, uid, data, update_id=False, context=None):
        ''' ERPEEK procedure called for create event from remote scripts
            data dict contain:
                code_partner: Key field for reach partner, inserad use copmany
                code_activity: Key field for reach activity, instead create ERR
                start: datetime start
                end: datetime stop
                origin: note for origin server
                log: log text
                log_warning: warning text
                log_error: error text
        '''
        # Pool used:
        category_pool = self.pool.get('log.category')
        activity_pool = self.pool.get('log.activity')
        partner_pool = self.pool.get('res.partner')

        # Read key field:
        code_partner = data.get('code_partner', False) # default company
        code_activity = data.get('code_activity', False)

        # ---------------------------------------------------------------------
        # Search foreign keys:
        # ---------------------------------------------------------------------
        # partner_id
        if code_partner:
            partner_ids = partner_pool.search(cr, uid, [
                ('log_code', '=', code_partner),
                ], context=context)
            if partner_ids:
                partner_id = partner_ids[0]
            else:
                _logger.error('Code partner not present %s (take company)!' % (
                    code_partner))
                partner_id = 1 # Use default
        else:
            _logger.error('Code partner present (take company)!')
            partner_id = 1 # Use detault company address

        # activity_id
        if not code_activity:
            _logger.error('Code activity not present (take ERR)!')
            code_activity = 'ERR'

        activity_ids = activity_pool.search(cr, uid, [
            ('partner_id', '=', partner_id),
            ('code', '=', code_activity),
            ], context=context)

        if activity_ids:
            # Find:
            activity_id = activity_ids[0]
        else:
            # Create new (to compile after on ODOO:
            # Get error category:
            category_ids = category_pool.search(cr, uid, [
                ('code', '=', code_activity), # use same code (cat.-act.)
                ], context=context)
            if category_ids:
                category_id = category_ids[0]
            else:
                _logger.error('Code category not present (take ERR)!')
                category_id = category_pool.create(cr, uid, {
                    'code': code_activity,
                    'name': 'Error',
                    'note': 'Log error activity of the system (admin purpose)'
                    }, context=context)

            # Get activity:
            activity_id = activity_pool.create(cr, uid, {
                'code': code_activity,
                'name': 'Error',
                'partner_id': partner_id,
                'category_id': category_id,
                #'from_date': Fa,
                #'to_date': ,
                #'duration': ,
                #'active'
                #'monitor'
                #'check_duration':
                #'duration_warning_range':
                #'duration_error_range':
                #'auto_duration':
                #'email_alert':
                #'email_error':
                #'email_warning':
                #'script':
                #'origin':
                #'note':
                #'state':
                }, context=context)
            _logger.error('Code activity not present %s create empty!' % (
                code_activity))

        # ---------------------------------------------------------------------
        # Create event:
        # ---------------------------------------------------------------------
        # Get data passed
        start = data.get('start', False)
        end = data.get('end', False)
        origin = data.get('origin', '')
        log_info = data.get('log_info', '')
        log_warning = data.get('log_warning', '')
        log_error = data.get('log_error', '')
        duration = 0 # TODO

        if not end:
            state = 'start'
        elif error:
            state = 'error'
        elif warning:
            state = 'warning'
        else:
            state = 'correct'

        record = {
            #'datetime': now
            #'mark_ok': False,
            'activity_id': activity_id,
            'start': start,
            'end': end,
            'duration': duration,
            'origin': origin,
            'log_info': log_info,
            'log_warning': log_warning,
            'log_error': log_error,
            'state': state,
            }

        if update_id:
            return self.write(cr, uid, update_id, record, context=context)
        else:
            return self.create(cr, uid, record, context=context)

    _columns = {
        'datetime': fields.date('Date', required=True),
        'activity_id': fields.many2one(
            'log.activity', 'Activity'),
        'partner_id': fields.related(
            'activity_id', 'partner_id',
            type='many2one', relation='res.partner',
            string='Partner', store=True),

        'start': fields.datetime('Start'),
        'end': fields.datetime('End'),
        'duration': fields.float(
            'Duration', digits=(16, 3), help='Duration of operation'),

        'origin': fields.text('Origin', help='Server info (log origin)'),

        'log_info': fields.text('Log info'),
        'log_warning': fields.text('Log warning'),
        'log_error': fields.text('Log error'),

        'mark_ok': fields.boolean('Mark as OK',
            help='For error / warning message'),
        'state': fields.selection([
            ('started', 'Started'), # Start
            ('correct', 'Correct'), # End
            ('warning', 'Warning'), # End with warning
            ('error', 'Error'), # End with error
            ], 'State', help='State info, not workflow management here'),
        }

    _defaults = {
        'datetime': lambda *a: datetime.now().strftime(
            DEFAULT_SERVER_DATETIME_FORMAT),
        'state': lambda *x: 'correct',
        }

class LogCategory(orm.Model):
    """ Model name: Log Category
    """
    _inherit = 'log.category'

    _columns = {
        'activity_ids': fields.one2many(
            'log.activity', 'category_id', 'Activity'),
    }

class ResUser(orm.Model):
    """ Model name: ResUser
    """
    _inherit = 'res.users'

    _columns = {
        'log_partner_id': fields.many2one('res.partner', 'Log partner'),
    }

class ResPartner(orm.Model):
    """ Model name: Res partner
    """
    _inherit = 'res.partner'

    _columns = {
        'log_code': fields.char('Partner log code', size=64,
            help='Partner code for link activity'),
        'log_users_ids': fields.one2many(
            'res.users', 'log_partner_id', 'Log user'),
        'log_activity_ids': fields.one2many(
            'log.activity', 'partner_id', 'Log activity'),
    }

# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
<|endoftext|>


<commit_before>"""Tab-completion over zmq"""

# Trying to get print statements to work during completion, not very
# successfully...
from __future__ import print_function

import itertools
import readline
import rlcompleter
import time

import session

class KernelCompleter(object):
    """Kernel-side completion machinery."""
    def __init__(self, namespace):
        self.namespace = namespace
        self.completer = rlcompleter.Completer(namespace)

    def complete(self, line, text):
        # We'll likely use linel later even if now it's not used for anything
        matches = []
        complete = self.completer.complete
        for state in itertools.count():
            comp = complete(text, state)
            if comp is None:
                break
            matches.append(comp)
        return matches

class ClientCompleter(object):
    """Client-side completion machinery.

    How it works: self.complete will be called multiple times, with
    state=0,1,2,... When state=0 it should compute ALL the completion matches,
    and then return them for each value of state."""

    def __init__(self, client, session, socket):
         # ugly, but we get called asynchronously and need access to some
         # client state, like backgrounded code
        self.client = client
        self.session = session
        self.socket = socket
        self.matches = []

    def request_completion(self, text):
        # Get full line to give to the kernel in case it wants more info.
        line = readline.get_line_buffer()
        # send completion request to kernel
        msg = self.session.send(self.socket,
                                'complete_request',
                                dict(text=text, line=line))

        # Give the kernel up to 0.5s to respond
        for i in range(5):
            ident,rep = self.session.recv(self.socket)
            rep = Message(rep)
            if rep is not None and rep.msg_type == 'complete_reply':
                matches = rep.content.matches
                break
            time.sleep(0.1)
        else:
            # timeout
            print ('TIMEOUT')  # Can't see this message...
            matches = None
        return matches

    def complete(self, text, state):

        if self.client.backgrounded > 0:
            print("\n[Not completing, background tasks active]")
            print(readline.get_line_buffer(), end='')
            return None

        if state==0:
            matches = self.request_completion(text)
            if matches is None:
                self.matches = []
                print('WARNING: Kernel timeout on tab completion.')
            else:
                self.matches = matches

        try:
            return self.matches[state]
        except IndexError:
            return None
<commit_msg>allow IPython.zmq.completer to be imported without readline<commit_after>"""Tab-completion over zmq"""

# Trying to get print statements to work during completion, not very
# successfully...
from __future__ import print_function

import itertools
try:
    import readline
except ImportError:
    readline = None
import rlcompleter
import time

import session

class KernelCompleter(object):
    """Kernel-side completion machinery."""
    def __init__(self, namespace):
        self.namespace = namespace
        self.completer = rlcompleter.Completer(namespace)

    def complete(self, line, text):
        # We'll likely use linel later even if now it's not used for anything
        matches = []
        complete = self.completer.complete
        for state in itertools.count():
            comp = complete(text, state)
            if comp is None:
                break
            matches.append(comp)
        return matches

class ClientCompleter(object):
    """Client-side completion machinery.

    How it works: self.complete will be called multiple times, with
    state=0,1,2,... When state=0 it should compute ALL the completion matches,
    and then return them for each value of state."""

    def __init__(self, client, session, socket):
        # ugly, but we get called asynchronously and need access to some
        # client state, like backgrounded code
        assert readline is not None, "ClientCompleter depends on readline"
        self.client = client
        self.session = session
        self.socket = socket
        self.matches = []

    def request_completion(self, text):
        # Get full line to give to the kernel in case it wants more info.
        line = readline.get_line_buffer()
        # send completion request to kernel
        msg = self.session.send(self.socket,
                                'complete_request',
                                dict(text=text, line=line))

        # Give the kernel up to 0.5s to respond
        for i in range(5):
            ident,rep = self.session.recv(self.socket)
            rep = Message(rep)
            if rep is not None and rep.msg_type == 'complete_reply':
                matches = rep.content.matches
                break
            time.sleep(0.1)
        else:
            # timeout
            print ('TIMEOUT')  # Can't see this message...
            matches = None
        return matches

    def complete(self, text, state):

        if self.client.backgrounded > 0:
            print("\n[Not completing, background tasks active]")
            print(readline.get_line_buffer(), end='')
            return None

        if state==0:
            matches = self.request_completion(text)
            if matches is None:
                self.matches = []
                print('WARNING: Kernel timeout on tab completion.')
            else:
                self.matches = matches

        try:
            return self.matches[state]
        except IndexError:
            return None
<|endoftext|>


<commit_before>"""
The :ref:`asyncpg` connector for PostgreSQL databases.
"""
import asyncio
import logging
import typing
import warnings

import asyncpg
from asyncpg import Record
from asyncpg.cursor import Cursor
from asyncpg.transaction import Transaction

from katagawa.backends.base import BaseConnector, BaseResultSet, BaseTransaction
from katagawa.exc import IntegrityError, OperationalError

logger = logging.getLogger(__name__)

def get_param_query(sql: str, params: dict) -> typing.Tuple[str, tuple]:
    """
    Re-does a SQL query so that it uses asyncpg's special query format.

    :param sql: The SQL statement to use.
    :param params: The dict of parameters to use.
    :return: A two-item tuple of (new_query, arguments)
    """
    if not params or len(params) < 1:
        return sql, ()
    # Dump the params into key -> value pairs.
    kv = [(k, v) for (k, v) in params.items()]

    # Define the list of items that are used to return.
    items = []
    fmt_dict = {}

    # Iterate over the key-values, adding each key to the fmt_dict with the number index.
    for n, (k, v) in enumerate(kv):
        n += 1
        # Add to the format dict.
        fmt_dict[k] = "${}".format(n)
        items.append(v)

    # Finally, format the SQL with the required params.
    sql_statement = sql.format(**fmt_dict)

    return sql_statement, tuple(items)

class AsyncpgResultSet(BaseResultSet):
    def __init__(self, cur: Cursor):
        self.cur = cur

        self._keys = None

    async def fetch_many(self, n: int):
        res = await self.cur.fetch(n)
        if res and self._keys is None:
            self._keys = res[0].keys()

        return res

    @property
    def keys(self) -> typing.Iterable[str]:
        if self._keys is None:
            raise RuntimeError("No keys have been fetched")
        return self._keys

    async def fetch_row(self):
        row = await self.cur.fetchrow()  # type: Record
        if self._keys is None and row is not None:
            self._keys = row.keys()

        return row

    async def close(self):
        pass

class AsyncpgTransaction(BaseTransaction):
    """
    A transaction that uses the `asyncpg <https://github.com/MagicStack/asyncpg`_ library.
    """

    def __init__(self, conn: 'AsyncpgConnector'):
        super().__init__(conn)

        #: The acquired connection from the connection pool.
        self.acquired_connection = None  # type: asyncpg.connection.Connection

        #: The asyncpg internal transaction.
        self.transaction = None  # type: Transaction

    async def begin(self, **transaction_options):
        """
        Begins the transaction.
        """
        logger.debug("Acquiring new transaction...")
        self.acquired_connection = \
            await self.connector.pool.acquire()  # type: asyncpg.connection.Connection
        self.transaction = self.acquired_connection.transaction(**transaction_options)
        await self.transaction.start()
        logger.debug("Acquired and started transaction {}".format(self.transaction))

        return self

    async def commit(self):
        """
        Commits the transaction.
        """
        await self.transaction.commit()

    async def rollback(self, checkpoint: str = None):
        if checkpoint is not None:
            # execute the ROLLBACK TO
            await self.acquired_connection.execute("ROLLBACK TO {}".format(checkpoint))
        else:
            await self.transaction.rollback()

    async def close(self):
        await self.connector.pool.release(self.acquired_connection)

    async def execute(self, sql: str, params: typing.Mapping[str, typing.Any] = None):
        """
        Executes SQL inside the transaction.

        :param sql: The SQL to execute.
        :param params: The parameters to excuse with.
        """
        # re-paramatarize the query
        logger.debug("Executing query {} with params {}".format(sql, params))
        query, params = get_param_query(sql, params)

        try:
            results = await self.acquired_connection.execute(query, *params)
        except asyncpg.IntegrityConstraintViolationError as e:
            raise IntegrityError(*e.args) from e
        except asyncpg.ObjectNotInPrerequisiteStateError as e:
            raise OperationalError(*e.args) from e

        return results

    async def cursor(self, sql: str, params: typing.Mapping[str, typing.Any] = None) \
            -> AsyncpgResultSet:
        """
        Executes a SQL statement and returns a cursor to iterate over the rows of the result.
        """
        logger.debug("Transforming query {} with params {}".format(sql, params))
        query, params = get_param_query(sql, params)
        logger.debug("Executing query {} with params {}".format(query, params))
        cur = await self.acquired_connection.cursor(query, *params)
        result = AsyncpgResultSet(cur)

        return result

class AsyncpgConnector(BaseConnector):
    """
    A connector that uses the `asyncpg <https://github.com/MagicStack/asyncpg>`_ library.
    """

    def __init__(self, parsed):
        super().__init__(parsed)

        self.loop = asyncio.get_event_loop()

        #: The :class:`asyncpg.pool.Pool` connection pool.
        self.pool = None  # type: asyncpg.pool.Pool

    def __del__(self):
        if self.pool is not None and not self.pool._closed:
            warnings.warn("Unclosed asyncpg pool {}".format(self.pool))
            self.loop.create_task(self.close())

    async def close(self):
        await self.pool.close()

    def emit_param(self, name: str) -> str:
        # note: asyncpg doesn't support DBAPI params
        # so we have to do a "fun" re-parsing pass
        # which has the potential to KILL performance
        # inst
        return "{{{name}}}".format(name=name)

    async def connect(self) -> 'BaseConnector':
        # create our connection pool
        logger.debug("Connecting to postgresql://{}:{}/{}".format(self.host, self.port, self.db))
        self.pool = await asyncpg.create_pool(self.dsn)
        return self

    def get_transaction(self) -> 'AsyncpgTransaction':
        return AsyncpgTransaction(self)

    async def get_db_server_info(self):
        return None

# define the asyncpg connector as the connector type to make an instance of
CONNECTOR_TYPE = AsyncpgConnector
<commit_msg>Convert rows to dicts before returning them.<commit_after>"""
The :ref:`asyncpg` connector for PostgreSQL databases.
"""
import asyncio
import logging
import typing
import warnings

import asyncpg
from asyncpg import Record
from asyncpg.cursor import Cursor
from asyncpg.transaction import Transaction

from katagawa.backends.base import BaseConnector, BaseResultSet, BaseTransaction
from katagawa.exc import IntegrityError, OperationalError

logger = logging.getLogger(__name__)

def get_param_query(sql: str, params: dict) -> typing.Tuple[str, tuple]:
    """
    Re-does a SQL query so that it uses asyncpg's special query format.

    :param sql: The SQL statement to use.
    :param params: The dict of parameters to use.
    :return: A two-item tuple of (new_query, arguments)
    """
    if not params or len(params) < 1:
        return sql, ()
    # Dump the params into key -> value pairs.
    kv = [(k, v) for (k, v) in params.items()]

    # Define the list of items that are used to return.
    items = []
    fmt_dict = {}

    # Iterate over the key-values, adding each key to the fmt_dict with the number index.
    for n, (k, v) in enumerate(kv):
        n += 1
        # Add to the format dict.
        fmt_dict[k] = "${}".format(n)
        items.append(v)

    # Finally, format the SQL with the required params.
    sql_statement = sql.format(**fmt_dict)

    return sql_statement, tuple(items)

class AsyncpgResultSet(BaseResultSet):
    def __init__(self, cur: Cursor):
        self.cur = cur

        self._keys = None

    async def fetch_many(self, n: int):
        res = await self.cur.fetch(n)
        if res and self._keys is None:
            self._keys = res[0].keys()

        return [dict(r) for r in res]

    @property
    def keys(self) -> typing.Iterable[str]:
        if self._keys is None:
            raise RuntimeError("No keys have been fetched")
        return self._keys

    async def fetch_row(self):
        row = await self.cur.fetchrow()  # type: Record
        if self._keys is None and row is not None:
            self._keys = row.keys()

        if row is not None:
            return dict(row)

    async def close(self):
        pass

class AsyncpgTransaction(BaseTransaction):
    """
    A transaction that uses the `asyncpg <https://github.com/MagicStack/asyncpg`_ library.
    """

    def __init__(self, conn: 'AsyncpgConnector'):
        super().__init__(conn)

        #: The acquired connection from the connection pool.
        self.acquired_connection = None  # type: asyncpg.connection.Connection

        #: The asyncpg internal transaction.
        self.transaction = None  # type: Transaction

    async def begin(self, **transaction_options):
        """
        Begins the transaction.
        """
        logger.debug("Acquiring new transaction...")
        self.acquired_connection = \
            await self.connector.pool.acquire()  # type: asyncpg.connection.Connection
        self.transaction = self.acquired_connection.transaction(**transaction_options)
        await self.transaction.start()
        logger.debug("Acquired and started transaction {}".format(self.transaction))

        return self

    async def commit(self):
        """
        Commits the transaction.
        """
        await self.transaction.commit()

    async def rollback(self, checkpoint: str = None):
        if checkpoint is not None:
            # execute the ROLLBACK TO
            await self.acquired_connection.execute("ROLLBACK TO {}".format(checkpoint))
        else:
            await self.transaction.rollback()

    async def close(self):
        await self.connector.pool.release(self.acquired_connection)

    async def execute(self, sql: str, params: typing.Mapping[str, typing.Any] = None):
        """
        Executes SQL inside the transaction.

        :param sql: The SQL to execute.
        :param params: The parameters to excuse with.
        """
        # re-paramatarize the query
        logger.debug("Executing query {} with params {}".format(sql, params))
        query, params = get_param_query(sql, params)

        try:
            results = await self.acquired_connection.execute(query, *params)
        except asyncpg.IntegrityConstraintViolationError as e:
            raise IntegrityError(*e.args) from e
        except asyncpg.ObjectNotInPrerequisiteStateError as e:
            raise OperationalError(*e.args) from e

        return results

    async def cursor(self, sql: str, params: typing.Mapping[str, typing.Any] = None) \
            -> AsyncpgResultSet:
        """
        Executes a SQL statement and returns a cursor to iterate over the rows of the result.
        """
        logger.debug("Transforming query {} with params {}".format(sql, params))
        query, params = get_param_query(sql, params)
        logger.debug("Executing query {} with params {}".format(query, params))
        cur = await self.acquired_connection.cursor(query, *params)
        result = AsyncpgResultSet(cur)

        return result

class AsyncpgConnector(BaseConnector):
    """
    A connector that uses the `asyncpg <https://github.com/MagicStack/asyncpg>`_ library.
    """

    def __init__(self, parsed):
        super().__init__(parsed)

        self.loop = asyncio.get_event_loop()

        #: The :class:`asyncpg.pool.Pool` connection pool.
        self.pool = None  # type: asyncpg.pool.Pool

    def __del__(self):
        if self.pool is not None and not self.pool._closed:
            warnings.warn("Unclosed asyncpg pool {}".format(self.pool))
            self.loop.create_task(self.close())

    async def close(self):
        await self.pool.close()

    def emit_param(self, name: str) -> str:
        # note: asyncpg doesn't support DBAPI params
        # so we have to do a "fun" re-parsing pass
        # which has the potential to KILL performance
        # inst
        return "{{{name}}}".format(name=name)

    async def connect(self) -> 'BaseConnector':
        # create our connection pool
        logger.debug("Connecting to postgresql://{}:{}/{}".format(self.host, self.port, self.db))
        self.pool = await asyncpg.create_pool(self.dsn)
        return self

    def get_transaction(self) -> 'AsyncpgTransaction':
        return AsyncpgTransaction(self)

    async def get_db_server_info(self):
        return None

# define the asyncpg connector as the connector type to make an instance of
CONNECTOR_TYPE = AsyncpgConnector
<|endoftext|>


<commit_before>#!/usr/bin/env python3

# Extending the classical rope-and-pencil ellipse drawing technique to more than 2 foci.
# The generic problem is, given a set of 2D points, make a rope loop around them and the tip of a pencil
# and draw a convex smooth curve around the points with this pencil while keeping the loop taught.
#
# For 3 non-collinear foci the resulting curve is a smooth combination of 6 ellipse fragments.

import math
import svgwrite

def distance(P1, P2):
    "Find the distance between two 2D points"
    return math.sqrt( (P2[0]-P1[0])**2 + (P2[1]-P1[1])**2 )

def midpoint(P1, P2):
    "Find the midpoint of two 2D points"
    return ((P1[0]+P2[0])/2, (P1[1]+P2[1])/2)

def scalar_product(P1, P0, P2):
    "Find the scalar product of P1-P0 and P2-P0 given all the three points (note the order or args)"
    return (P1[0]-P0[0])*(P2[0]-P0[0])+(P1[1]-P0[1])*(P2[1]-P0[1])

def three_point_cosine(P1, P0, P2):
    "Find cosine of the angle between P1-P0 and P2-P0 (note the order of args)"
    return scalar_product(P1, P0, P2)/(distance(P1,P0)*distance(P2,P0))

def sign(x):
    "Amusingly, Python doesn't have a native sign() function"
    return (x>0)-(x<0)

def points_are_clockwise(P1, P2, P3):
    "Detect whether the points are ordered clockwise (1), collinear (0) or counter-clockwise(-1)"
    return  sign((P2[0]-P1[0])*(P3[1]-P1[1])-(P2[1]-P1[1])*(P3[0]-P1[0]))

def draw_ellipsystem(P1, P2, P3, slack=250, show_leftovers=False, show_tickmarks=True, filename="example.svg", canvas_size=(1000,1000), pencil_mark_fraction=None):
    "Draw a system of 6 ellipse fragments that make up the sought-for smooth convex shape"

    dwg             = svgwrite.Drawing(filename=filename, debug=True, size=canvas_size)

    def draw_ellipse_fragment(F1, F2, Pl, d, colour='grey', pencil_mark_fraction=None):
        "Draw an ellipse fragment given two foci, the third point used for cut-off and the length of slack part of the rope attached to the foci"

        clockwise_sign  = points_are_clockwise(F1, F2, Pl)

            # internal absolute measurements of the ellipse (also available to the nested function) :
        c               = distance(F1, F2)/2
        a               = d/2
        b               = math.sqrt( a**2 - c**2 )

        def point_on_the_ellipse(cos_f, focus_sign):
            rho             = b**2 / (a + focus_sign * c * cos_f)
            sin_f           = math.sqrt(1 - cos_f**2)
            x               = rho * cos_f + focus_sign * c
            y               = rho * sin_f
            return (x,y)

            # Translate, rotate and flip the coordinates so that inside the SVG group element
            #  we assume Y grows upwards (a standard mathematical convention),
            #  the ellipse is centered around the origin and the major axis is horizontal:
        tilt_deg        = math.degrees( math.atan2(F2[1]-F1[1], F2[0]-F1[0]) )
        (Cx,Cy)         = midpoint(F1, F2)
        target_group    = dwg.g( stroke=colour, stroke_width='2', fill='none', transform='translate(%f,%f),rotate(%f,0,0),scale(1,-1)' % (Cx,Cy,tilt_deg) )

        cos_alpha   = three_point_cosine(F2, F1, Pl)
        cos_beta    = three_point_cosine(F1, F2, Pl)

            # A and B are start and end points of the ellipse fragment:
        (cos_for_A, cos_for_B) = (-cos_alpha, cos_beta) if clockwise_sign == 1 else (-cos_beta, cos_alpha)
        (Ax,Ay)     = point_on_the_ellipse( cos_for_A, focus_sign=-clockwise_sign )
        (Bx,By)     = point_on_the_ellipse( cos_for_B, focus_sign= clockwise_sign )

            # visible part of the component ellipse:
        target_group.add( dwg.path( d="M %f,%f A %f,%f 0 0,0 %f,%f" % (Ax, Ay, a, b, Bx, By), stroke_width=4 ) )

            # invisible part of the component ellipse:
        if show_leftovers:
            target_group.add( dwg.path( d="M %f,%f A %f,%f 0 1,1 %f,%f" % (Ax, Ay, a, b, Bx, By), stroke_dasharray='3,7' ) )

        if show_tickmarks:
#            target_group.add( dwg.circle( center=(Ax,Ay), r=12, stroke=F1[2], fill=Pl[2] ) )    # "from" tick mark
            target_group.add( dwg.circle( center=(Bx,By), r=8,  fill=Pl[2] ) )     # "to" tick mark

        if pencil_mark_fraction is not None:
                # find the angles relative to F1 in local coordinates:
            gamma   = math.acos( three_point_cosine((c,0), (-c,0), (Ax,Ay)) )
            delta   = math.acos( three_point_cosine((c,0), (-c,0), (Bx,By)) )
                # now we can create any convex combination and map it onto the corresponding ellipse fragment:
            mix     = gamma * (1-pencil_mark_fraction) + delta * pencil_mark_fraction
            (Mx,My) = point_on_the_ellipse( math.cos( mix ), focus_sign=-1 )

            target_group.add( dwg.circle( center=(Mx,My), r=5,     stroke='blue', stroke_width=1, fill='none' ) )    # "mix" tick mark
            target_group.add( dwg.line( start=(-c,0), end=(Mx,My), stroke='blue', stroke_width=1  ) )
            target_group.add( dwg.line( start=( c,0), end=(Mx,My), stroke='blue', stroke_width=1  ) )

        dwg.add( target_group )

    dwg.add( dwg.circle( center=P1[0:2], r=5, stroke=P1[2], stroke_width=2, fill=P1[2] ) )
    dwg.add( dwg.circle( center=P2[0:2], r=5, stroke=P2[2], stroke_width=2, fill=P2[2] ) )
    dwg.add( dwg.circle( center=P3[0:2], r=5, stroke=P3[2], stroke_width=2, fill=P3[2] ) )

    d12             = distance(P1, P2)
    d23             = distance(P2, P3)
    d31             = distance(P3, P1)
    tight_loop      = d12 + d23 + d31

    loop_length     = tight_loop+slack
    draw_ellipse_fragment(P1, P2, P3, loop_length-d23-d31,  colour=P3[2], pencil_mark_fraction=pencil_mark_fraction)
    draw_ellipse_fragment(P1, P3, P2, loop_length-d31,      colour=P2[2])
    draw_ellipse_fragment(P2, P3, P1, loop_length-d12-d31,  colour=P1[2])
    draw_ellipse_fragment(P2, P1, P3, loop_length-d12,      colour=P3[2])
    draw_ellipse_fragment(P3, P1, P2, loop_length-d12-d23,  colour=P2[2])
    draw_ellipse_fragment(P3, P2, P1, loop_length-d23,      colour=P1[2])
    dwg.save()

if __name__ == '__main__':
    P1              = (400, 500, 'red')
    P2              = (600, 400, 'orange')
    P3              = (500, 700, 'green')
    draw_ellipsystem(P1, P2, P3, filename='examples/three_foci_without_leftovers.svg')
    draw_ellipsystem(P1, P2, P3, filename='examples/pencil_mark.svg', pencil_mark_fraction=0.1)
    draw_ellipsystem(P1, P2, P3, show_leftovers=True, filename='examples/three_foci_with_leftovers.svg')
#    draw_ellipsystem(P1, P2, P3, show_tickmarks=False, slacks=[1, 10, 50, 150, 250, 500], filename='examples/three_foci_different_slacks.svg')

<commit_msg>change of interface in preparation to N foci<commit_after>#!/usr/bin/env python3

# Extending the classical rope-and-pencil ellipse drawing technique to more than 2 foci.
# The generic problem is, given a set of 2D points, make a rope loop around them and the tip of a pencil
# and draw a convex smooth curve around the points with this pencil while keeping the loop taught.
#
# For 3 non-collinear foci the resulting curve is a smooth combination of 6 ellipse fragments.

import math
import svgwrite

def distance(P1, P2):
    "Find the distance between two 2D points"
    return math.sqrt( (P2[0]-P1[0])**2 + (P2[1]-P1[1])**2 )

def midpoint(P1, P2):
    "Find the midpoint of two 2D points"
    return ((P1[0]+P2[0])/2, (P1[1]+P2[1])/2)

def scalar_product(P1, P0, P2):
    "Find the scalar product of P1-P0 and P2-P0 given all the three points (note the order or args)"
    return (P1[0]-P0[0])*(P2[0]-P0[0])+(P1[1]-P0[1])*(P2[1]-P0[1])

def three_point_cosine(P1, P0, P2):
    "Find cosine of the angle between P1-P0 and P2-P0 (note the order of args)"
    return scalar_product(P1, P0, P2)/(distance(P1,P0)*distance(P2,P0))

def sign(x):
    "Amusingly, Python doesn't have a native sign() function"
    return (x>0)-(x<0)

def points_are_clockwise(P1, P2, P3):
    "Detect whether the points are ordered clockwise (1), collinear (0) or counter-clockwise(-1)"
    return  sign((P2[0]-P1[0])*(P3[1]-P1[1])-(P2[1]-P1[1])*(P3[0]-P1[0]))

def draw_ellipsystem(P1, P2, P3, slack=250, show_leftovers=False, show_tickmarks=True, filename="example.svg", canvas_size=(1000,1000), pencil_mark_fraction=None):
    "Draw a system of 6 ellipse fragments that make up the sought-for smooth convex shape"

    dwg             = svgwrite.Drawing(filename=filename, debug=True, size=canvas_size)

    def draw_ellipse_fragment(Pprev, F1, F2, Pnext, d, pencil_mark_fraction=None):
        "Draw an ellipse fragment given two foci, the third point used for cut-off and the length of slack part of the rope attached to the foci"

            # internal absolute measurements of the ellipse (also available to the nested function) :
        c               = distance(F1, F2)/2
        a               = d/2
        b               = math.sqrt( a**2 - c**2 )

        def point_on_the_ellipse(cos_f, focus_sign=-1):
            rho             = b**2 / (a + focus_sign * c * cos_f)
            sin_f           = math.sqrt(1 - cos_f**2)
            x               = rho * cos_f + focus_sign * c
            y               = rho * sin_f
            return (x,y)

            # Translate, rotate and flip the coordinates so that inside the SVG group element
            #  we assume Y grows upwards (a standard mathematical convention),
            #  the ellipse is centered around the origin and the major axis is horizontal:
        tilt_deg        = math.degrees( math.atan2(F2[1]-F1[1], F2[0]-F1[0]) )
        (Cx,Cy)         = midpoint(F1, F2)
        target_group    = dwg.g( stroke=Pnext[2], stroke_width='2', fill='none', transform='translate(%f,%f),rotate(%f,0,0),scale(1,-1)' % (Cx,Cy,tilt_deg) )

            # A and B are start and end points of the ellipse fragment:
        if Pprev:
            cos_for_A       = -three_point_cosine(F2, F1, Pprev)
            cos_for_B       =  three_point_cosine(F1, F2, Pnext)
            clockwise_sign  =  1
        else:
            cos_for_A       = -three_point_cosine(F1, F2, Pnext)
            cos_for_B       =  three_point_cosine(F2, F1, Pnext)
            clockwise_sign  = -1

        (Ax,Ay)     = point_on_the_ellipse( cos_for_A, focus_sign=-clockwise_sign )
        (Bx,By)     = point_on_the_ellipse( cos_for_B, focus_sign= clockwise_sign )

            # visible part of the component ellipse:
        target_group.add( dwg.path( d="M %f,%f A %f,%f 0 0,0 %f,%f" % (Ax, Ay, a, b, Bx, By), stroke_width=4 ) )

            # invisible part of the component ellipse:
        if show_leftovers:
            target_group.add( dwg.path( d="M %f,%f A %f,%f 0 1,1 %f,%f" % (Ax, Ay, a, b, Bx, By), stroke_dasharray='3,7' ) )

        if show_tickmarks:
#            target_group.add( dwg.circle( center=(Ax,Ay), r=12, stroke=F1[2], fill=Pnext[2] ) )    # "from" tick mark
            target_group.add( dwg.circle( center=(Bx,By), r=8,  fill=Pnext[2] ) )     # "to" tick mark

        if pencil_mark_fraction is not None:
                # find the angles relative to F1 in local coordinates:
            gamma   = math.acos( three_point_cosine((c,0), (-c,0), (Ax,Ay)) )
            delta   = math.acos( three_point_cosine((c,0), (-c,0), (Bx,By)) )
                # now we can create any convex combination and map it onto the corresponding ellipse fragment:
            mix     = gamma * (1-pencil_mark_fraction) + delta * pencil_mark_fraction
            (Mx,My) = point_on_the_ellipse( math.cos( mix ) )

            target_group.add( dwg.circle( center=(Mx,My), r=5,     stroke='blue', stroke_width=1, fill='none' ) )    # "mix" tick mark
            target_group.add( dwg.line( start=(-c,0), end=(Mx,My), stroke='blue', stroke_width=1  ) )
            target_group.add( dwg.line( start=( c,0), end=(Mx,My), stroke='blue', stroke_width=1  ) )

        dwg.add( target_group )

    dwg.add( dwg.circle( center=P1[0:2], r=5, stroke=P1[2], stroke_width=2, fill=P1[2] ) )
    dwg.add( dwg.circle( center=P2[0:2], r=5, stroke=P2[2], stroke_width=2, fill=P2[2] ) )
    dwg.add( dwg.circle( center=P3[0:2], r=5, stroke=P3[2], stroke_width=2, fill=P3[2] ) )

    d12             = distance(P1, P2)
    d23             = distance(P2, P3)
    d31             = distance(P3, P1)
    tight_loop      = d12 + d23 + d31

    loop_length     = tight_loop+slack
    draw_ellipse_fragment(P3,   P1, P2, P3, loop_length-d23-d31,  pencil_mark_fraction=pencil_mark_fraction)
    draw_ellipse_fragment(None, P1, P3, P2, loop_length-d31     )
    draw_ellipse_fragment(P1,   P2, P3, P1, loop_length-d12-d31 )
    draw_ellipse_fragment(None, P2, P1, P3, loop_length-d12     )
    draw_ellipse_fragment(P2,   P3, P1, P2, loop_length-d12-d23 )
    draw_ellipse_fragment(None, P3, P2, P1, loop_length-d23     )
    dwg.save()

if __name__ == '__main__':
    P1              = (400, 500, 'red')
    P2              = (600, 400, 'orange')
    P3              = (500, 700, 'green')
    draw_ellipsystem(P1, P2, P3, filename='examples/three_foci_without_leftovers.svg')
    draw_ellipsystem(P1, P2, P3, filename='examples/pencil_mark.svg', pencil_mark_fraction=0.1)
    draw_ellipsystem(P1, P2, P3, show_leftovers=True, filename='examples/three_foci_with_leftovers.svg')
#    draw_ellipsystem(P1, P2, P3, show_tickmarks=False, slacks=[1, 10, 50, 150, 250, 500], filename='examples/three_foci_different_slacks.svg')

<|endoftext|>


<commit_before>#!/usr/bin/env python

# MIT License, Copyright 2015 t0x0
# Full text in 'LICENSE' file

# This isn't very well written, or very pythonic. Hazards of 3AM coding absent caffeine.
# May not work for any FFU files other than the Raspberry Pi 2 Windows 10 Insider Preview image.
# Tested on the 2015-05-12 release image with Python 2.7.9
# Use at your own risk, and let me know if it fails for your situation. me@t0x0.com
# I'll try to fix it as I have time.

import sys, os.path, struct, string
from functools import partial
from collections import namedtuple

if len(sys.argv) < 2:
	sys.exit("Error: no filenames provided. Usage: ffu2img.py input.ffu [output.img]\nWarning, will overwrite output file without prior permission.")
ffupath = sys.argv[1]
if len(sys.argv) == 3:
	imgpath = sys.argv[2]
else:
	imgpath = string.rsplit(ffupath, '.', 1)[0] + '.img'
print 'Input File: ' + ffupath
print 'Output File: ' + imgpath

SecurityHeader = namedtuple("SecurityHeader", "cbSize signature dwChunkSizeInKb dwAlgId dwCatalogSize dwHashTableSize")
ImageHeader = namedtuple("ImageHeader", "cbSize signature ManifestLength dwChunkSize")
StoreHeader = namedtuple("StoreHeader", "dwUpdateType MajorVersion MinorVersion FullFlashMajorVersion FullFlashMinorVersion szPlatformId dwBlockSizeInBytes dwWriteDescriptorCount dwWriteDescriptorLength dwValidateDescriptorCount dwValidateDescriptorLength dwInitialTableIndex dwInitialTableCount dwFlashOnlyTableIndex dwFlashOnlyTableCount dwFinalTableIndex dwFinalTableCount")
BlockDataEntry = namedtuple("BlockDataEntry", "dwDiskAccessMethod dwBlockIndex dwLocationCount dwBlockCount")

ffufp = open(ffupath, 'rb')
imgfp = open(imgpath, 'wb')

def readsecheader():
	data = ffufp.read(4)
	cbSize = struct.unpack("<L", data)[0]
	data = ffufp.read(12)
	signature = str(data)
	if data != 'SignedImage ':
		sys.exit("Error: security header signature incorrect.")
	data = ffufp.read(4)
	dwChunkSizeInKb = struct.unpack("<L", data)[0]
	data = ffufp.read(4)
	dwAlgId = struct.unpack("<L", data)[0]
	data = ffufp.read(4)
	dwCatalogSize = struct.unpack("<L", data)[0]
	data = ffufp.read(4)
	dwHashTableSize = struct.unpack("<L", data)[0]
	FFUSecHeader = SecurityHeader(cbSize, signature, dwChunkSizeInKb, dwAlgId, dwCatalogSize, dwHashTableSize)
	return FFUSecHeader

def readimgheader():
	data = ffufp.read(4)
	cbSize = struct.unpack("<L", data)[0]
	data = ffufp.read(12)
	signature = str(data)
	if data != 'ImageFlash  ':
		sys.exit("Error: image header signature incorrect.")
	data = ffufp.read(4)
	ManifestLength = struct.unpack("<L", data)[0]
	data = ffufp.read(4)
	dwChunkSize = struct.unpack("<L", data)[0]
	FFUImgHeader = ImageHeader(cbSize, signature, ManifestLength, dwChunkSize)
	return FFUImgHeader

def readstoreheader():
	data = ffufp.read(4)
	dwUpdateType = struct.unpack("<L", data)[0]
	data = ffufp.read(2)
	MajorVersion = struct.unpack("<H", data)[0]
	data = ffufp.read(2)
	MinorVersion = struct.unpack("<H", data)[0]
	data = ffufp.read(2)
	FullFlashMajorVersion = struct.unpack("<H", data)[0]
	data = ffufp.read(2)
	FullFlashMinorVersion = struct.unpack("<H", data)[0]
	data = ffufp.read(192)
	szPlatformId = str(data)
	data = ffufp.read(4)
	dwBlockSizeInBytes = struct.unpack("<L", data)[0]
	data = ffufp.read(4)
	dwWriteDescriptorCount = struct.unpack("<L", data)[0]
	data = ffufp.read(4)
	dwWriteDescriptorLength = struct.unpack("<L", data)[0]
	data = ffufp.read(4)
	dwValidateDescriptorCount = struct.unpack("<L", data)[0]
	data = ffufp.read(4)
	dwValidateDescriptorLength = struct.unpack("<L", data)[0]
	data = ffufp.read(4)
	dwInitialTableIndex = struct.unpack("<L", data)[0]
	data = ffufp.read(4)
	dwInitialTableCount = struct.unpack("<L", data)[0]
	data = ffufp.read(4)
	dwFlashOnlyTableIndex = struct.unpack("<L", data)[0]
	data = ffufp.read(4)
	dwFlashOnlyTableCount = struct.unpack("<L", data)[0]
	data = ffufp.read(4)
	dwFinalTableIndex = struct.unpack("<L", data)[0]
	data = ffufp.read(4)
	dwFinalTableCount = struct.unpack("<L", data)[0]
	FFUStoreHeader = StoreHeader(dwUpdateType, MajorVersion, MinorVersion, FullFlashMajorVersion, FullFlashMinorVersion, szPlatformId, dwBlockSizeInBytes, dwWriteDescriptorCount, dwWriteDescriptorLength, dwValidateDescriptorCount, dwValidateDescriptorLength, dwInitialTableIndex, dwInitialTableCount, dwFlashOnlyTableIndex, dwFlashOnlyTableCount, dwFinalTableIndex, dwFinalTableCount)
	return FFUStoreHeader

def readblockdataentry():
	data = ffufp.read(4)
	dwDiskAccessMethod = struct.unpack("<L", data)[0]
	data = ffufp.read(4)
	dwBlockIndex = struct.unpack("<L", data)[0]
	data = ffufp.read(4)
	dwLocationCount = struct.unpack("<L", data)[0]
	data = ffufp.read(4)
	dwBlockCount = struct.unpack("<L", data)[0]
	CurrentBlockDataEntry = BlockDataEntry(dwDiskAccessMethod, dwBlockIndex, dwLocationCount, dwBlockCount)
	return CurrentBlockDataEntry

def gotoendofchunk(chunksizeinkb, position):
	remainderofchunk = position%int(chunksizeinkb*1024)
	distancetochunkend = (chunksizeinkb*1024) - remainderofchunk
	ffufp.seek(distancetochunkend, 1)
	return distancetochunkend

print 'Reading Security Header'
FFUSecHeader = readsecheader()
print 'Passing Signed Catalog'
ffufp.seek(FFUSecHeader.dwCatalogSize, 1)
print 'Passing Hash Table'
ffufp.seek(FFUSecHeader.dwHashTableSize, 1)
print 'Going to end of chunk'
gotoendofchunk(FFUSecHeader.dwChunkSizeInKb, ffufp.tell())
print 'Reading Image Header'
FFUImgHeader = readimgheader()
print 'Passing Manifest'
ffufp.seek(FFUImgHeader.ManifestLength, 1)
print 'Going to end of chunk'
gotoendofchunk(FFUSecHeader.dwChunkSizeInKb, ffufp.tell())
print 'Reading Store Header'
FFUStoreHeader = readstoreheader()
print 'Skipping Validation Entries'
ffufp.seek(FFUStoreHeader.dwValidateDescriptorLength, 1)
# This will not work in any situation where the FFU has any sort of custom block layout.
# I just didn't feel like deciphering the docs since it wasn't necessary.
print 'Skipping Block Data Entries'
ffufp.seek(FFUStoreHeader.dwWriteDescriptorLength, 1)
print 'Going to end of chunk'
gotoendofchunk(FFUSecHeader.dwChunkSizeInKb, ffufp.tell())
# Not sure why I have to skip an extra chunk. Nothing in the docs mentions it.
ffufp.seek(128*1024, 1)
i = 0
print 'Current location: ' + str(ffufp.tell())
with ffufp as openfileobject:
	for chunk in iter(partial(openfileobject.read, 1024*128), ''):
		imgfp.write(chunk)
		print str(i*128) + 'kb written\r',
		i = i+1
imgfp.close()
ffufp.close()
<commit_msg>Refactored, added block following<commit_after>#!/usr/bin/env python

# MIT License, Copyright 2015 t0x0
# Full text in 'LICENSE' file

# This isn't very well written, or very pythonic. Hazards of 3AM coding absent caffeine.
# May not work for any FFU files other than the Raspberry Pi 2 Windows 10 Insider Preview image.
# Tested on the 2015-05-12 release image with Python 2.7.9
# Use at your own risk, and let me know if it fails for your situation. me@t0x0.com
# I'll try to fix it as I have time.

import sys, os.path, struct, string
from functools import partial
from collections import namedtuple

if len(sys.argv) < 2:
	sys.exit("Error: no filenames provided. Usage: ffu2img.py input.ffu [output.img]\nWarning, will overwrite output file without prior permission.")
ffupath = sys.argv[1]
if len(sys.argv) == 3:
	imgpath = sys.argv[2]
else:
	imgpath = string.rsplit(ffupath, '.', 1)[0] + '.img'
print 'Input File: ' + ffupath
print 'Output File: ' + imgpath

SecurityHeader = namedtuple("SecurityHeader", "cbSize signature dwChunkSizeInKb dwAlgId dwCatalogSize dwHashTableSize")
ImageHeader = namedtuple("ImageHeader", "cbSize signature ManifestLength dwChunkSize")
StoreHeader = namedtuple("StoreHeader", "dwUpdateType MajorVersion MinorVersion FullFlashMajorVersion FullFlashMinorVersion szPlatformId dwBlockSizeInBytes dwWriteDescriptorCount dwWriteDescriptorLength dwValidateDescriptorCount dwValidateDescriptorLength dwInitialTableIndex dwInitialTableCount dwFlashOnlyTableIndex dwFlashOnlyTableCount dwFinalTableIndex dwFinalTableCount")
BlockDataEntry = namedtuple("BlockDataEntry", "dwDiskAccessMethod dwBlockIndex dwLocationCount dwBlockCount")

ffufp = open(ffupath, 'rb')
imgfp = open(imgpath, 'wb')

def readsecheader():
	(cbSize, signature, dwChunkSizeInKb, dwAlgId, dwCatalogSize, dwHashTableSize) = struct.unpack("<L12sLLLL", ffufp.read(32))
	if signature != 'SignedImage ':
		sys.exit("Error: security header signature incorrect.")
	return SecurityHeader(cbSize, signature, dwChunkSizeInKb, dwAlgId, dwCatalogSize, dwHashTableSize)

def readimgheader():
	(cbSize, signature, ManifestLength, dwChunkSize) = struct.unpack("<L12sLL", ffufp.read(24))
	if signature != 'ImageFlash  ':
		sys.exit("Error: image header signature incorrect.")
	return ImageHeader(cbSize, signature, ManifestLength, dwChunkSize)

def readstoreheader():
	(dwUpdateType, MajorVersion, MinorVersion, FullFlashMajorVersion, FullFlashMinorVersion, szPlatformId, dwBlockSizeInBytes, dwWriteDescriptorCount, dwWriteDescriptorLength, dwValidateDescriptorCount, dwValidateDescriptorLength, dwInitialTableIndex, dwInitialTableCount, dwFlashOnlyTableIndex, dwFlashOnlyTableCount, dwFinalTableIndex, dwFinalTableCount) = struct.unpack("<LHHHH192sLLLLLLLLLLL", ffufp.read(248))
	return StoreHeader(dwUpdateType, MajorVersion, MinorVersion, FullFlashMajorVersion, FullFlashMinorVersion, szPlatformId, dwBlockSizeInBytes, dwWriteDescriptorCount, dwWriteDescriptorLength, dwValidateDescriptorCount, dwValidateDescriptorLength, dwInitialTableIndex, dwInitialTableCount, dwFlashOnlyTableIndex, dwFlashOnlyTableCount, dwFinalTableIndex, dwFinalTableCount)

def readblockdataentry():
	(dwDiskAccessMethod, dwBlockIndex, dwLocationCount, dwBlockCount) = struct.unpack("<LLLL", ffufp.read(16))
	return BlockDataEntry(dwDiskAccessMethod, dwBlockIndex, dwLocationCount, dwBlockCount)

def gotoendofchunk(chunksizeinkb, position):
	remainderofchunk = position%int(chunksizeinkb*1024)
	distancetochunkend = (chunksizeinkb*1024) - remainderofchunk
	ffufp.seek(distancetochunkend, 1)
	return distancetochunkend

FFUSecHeader = readsecheader()
ffufp.seek(FFUSecHeader.dwCatalogSize, 1)
ffufp.seek(FFUSecHeader.dwHashTableSize, 1)
gotoendofchunk(FFUSecHeader.dwChunkSizeInKb, ffufp.tell())
FFUImgHeader = readimgheader()
ffufp.seek(FFUImgHeader.ManifestLength, 1)
gotoendofchunk(FFUSecHeader.dwChunkSizeInKb, ffufp.tell())
FFUStoreHeader = readstoreheader()
ffufp.seek(FFUStoreHeader.dwValidateDescriptorLength, 1)
print 'Block data entries begin: ' + str(hex(ffufp.tell()))
print 'Block data entries end: ' + str(hex(ffufp.tell() + FFUStoreHeader.dwWriteDescriptorLength))
blockdataaddress = ffufp.tell() + FFUStoreHeader.dwWriteDescriptorLength
blockdataaddress = blockdataaddress + (FFUSecHeader.dwChunkSizeInKb*1024)-(blockdataaddress%int((FFUSecHeader.dwChunkSizeInKb*1024)))
blockdataaddress = blockdataaddress + (128*1024)
print 'Block data chunks begin: ' + str(hex(blockdataaddress))
iBlock = 0
blockdataaddress = blockdataaddress - (1*FFUStoreHeader.dwBlockSizeInBytes)
while iBlock < FFUStoreHeader.dwWriteDescriptorCount:
		CurrentBlockDataEntry = readblockdataentry()
		curraddress = ffufp.tell()
		blockdataaddress = blockdataaddress + (CurrentBlockDataEntry.dwBlockIndex*FFUStoreHeader.dwBlockSizeInBytes)
		ffufp.seek(blockdataaddress)
		imgfp.write(ffufp.read(FFUStoreHeader.dwBlockSizeInBytes))
		ffufp.seek(curraddress)
		iBlock = iBlock + 1
		print str(iBlock) + ' blocks, ' + str((iBlock*FFUStoreHeader.dwBlockSizeInBytes)/1024) + 'kb written\r',
imgfp.close()
ffufp.close()
<|endoftext|>