<commit_before>#    Copyright 2013-2015 ARM Limited
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

# pylint: disable=W0613,E1101
from __future__ import division
import os
import time
import subprocess
from collections import defaultdict

from wlauto import Instrument, Parameter, Executable
from wlauto.exceptions import InstrumentError, ConfigError
from wlauto.core import signal
from wlauto.utils.types import boolean

OUTPUT_TRACE_FILE = 'trace.dat'
OUTPUT_TEXT_FILE = '{}.txt'.format(os.path.splitext(OUTPUT_TRACE_FILE)[0])
TIMEOUT = 180

class TraceCmdInstrument(Instrument):

    name = 'trace-cmd'
    description = """
    trace-cmd is an instrument which interacts with Ftrace Linux kernel internal
    tracer

    From trace-cmd man page:

    trace-cmd command interacts with the Ftrace tracer that is built inside the
    Linux kernel. It interfaces with the Ftrace specific files found in the
    debugfs file system under the tracing directory.

    trace-cmd reads a list of events it will trace, which can be specified in
    the config file as follows ::

        trace_events = ['irq*', 'power*']

    If no event is specified in the config file, trace-cmd traces the following events:

        - sched*
        - irq*
        - power*
        - cpufreq_interactive*

    The list of available events can be obtained by rooting and running the following
    command line on the device ::

       trace-cmd list

    You may also specify ``trace_buffer_size`` setting which must be an integer that will
    be used to set the ftrace buffer size. It will be interpreted as KB::

        trace_cmd_buffer_size = 8000

    The maximum buffer size varies from device to device, but there is a maximum and trying
    to set buffer size beyound that will fail. If you plan on collecting a lot of trace over
    long periods of time, the buffer size will not be enough and you will only get trace for
    the last portion of your run. To deal with this you can set the ``trace_mode`` setting to
    ``'record'`` (the default is ``'start'``)::

        trace_cmd_mode = 'record'

    This will cause trace-cmd to trace into file(s) on disk, rather than the buffer, and so the
    limit for the max size of the trace is set by the storage available on device. Bear in mind
    that ``'record'`` mode *is* more instrusive than the default, so if you do not plan on
    generating a lot of trace, it is best to use the default ``'start'`` mode.

    .. note:: Mode names correspend to the underlying trace-cmd exectuable's command used to
              implement them. You can find out more about what is happening in each case from
              trace-cmd documentation: https://lwn.net/Articles/341902/.

    This instrument comes with an Android trace-cmd binary that will be copied and used on the
    device, however post-processing will be done on-host and you must have trace-cmd installed and
    in your path. On Ubuntu systems, this may be done with::

        sudo apt-get install trace-cmd

    """

    parameters = [
        Parameter('events', kind=list, default=['sched*', 'irq*', 'power*', 'cpufreq_interactive*'],
                  global_alias='trace_events',
                  description="""
                  Specifies the list of events to be traced. Each event in the list will be passed to
                  trace-cmd with -e parameter and must be in the format accepted by trace-cmd.
                  """),
        Parameter('mode', default='start', allowed_values=['start', 'record'],
                  global_alias='trace_mode',
                  description="""
                  Trace can be collected using either 'start' or 'record' trace-cmd
                  commands. In 'start' mode, trace will be collected into the ftrace buffer;
                  in 'record' mode, trace will be written into a file on the device's file
                  system. 'start' mode is (in theory) less intrusive than 'record' mode, however
                  it is limited by the size of the ftrace buffer (which is configurable --
                  see ``buffer_size`` -- but only up to a point) and that may overflow
                  for long-running workloads, which will result in dropped events.
                  """),
        Parameter('buffer_size', kind=int, default=None,
                  global_alias='trace_buffer_size',
                  description="""
                  Attempt to set ftrace buffer size to the specified value (in KB). Default buffer size
                  may need to be increased for long-running workloads, or if a large number
                  of events have been enabled. Note: there is a maximum size that the buffer can
                  be set, and that varies from device to device. Attempting to set buffer size higher
                  than this will fail. In that case, this instrument will set the size to the highest
                  possible value by going down from the specified size in ``buffer_size_step`` intervals.
                  """),
        Parameter('buffer_size_step', kind=int, default=1000,
                  global_alias='trace_buffer_size_step',
                  description="""
                  Defines the decremental step used if the specified ``buffer_size`` could not be set.
                  This will be subtracted form the buffer size until set succeeds or size is reduced to
                  1MB.
                  """),
        Parameter('buffer_size_file', default='/sys/kernel/debug/tracing/buffer_size_kb',
                  description="""
                  Path to the debugs file that may be used to set ftrace buffer size. This should need
                  to be modified for the vast majority devices.
                  """),
        Parameter('report', kind=boolean, default=True,
                  description="""
                  Specifies whether host-side reporting should be performed once the binary trace has been
                  pulled form the device.

                  .. note:: This requires the latest version of trace-cmd to be installed on the host (the
                            one in your distribution's repos may be too old).

                  """),
        Parameter('no_install', kind=boolean, default=False,
                  description="""
                  Do not install the bundled trace-cmd  and use the one on the device instead. If there is
                  not already a trace-cmd on the device, an error is raised.

                  """),
    ]

    def __init__(self, device, **kwargs):
        super(TraceCmdInstrument, self).__init__(device, **kwargs)
        self.trace_cmd = None
        self.event_string = _build_trace_events(self.events)
        self.output_file = os.path.join(self.device.working_directory, OUTPUT_TRACE_FILE)
        self.temp_trace_file = self.device.path.join(self.device.working_directory, OUTPUT_TRACE_FILE)

    def on_run_init(self, context):
        if not self.device.is_rooted:
            raise InstrumentError('trace-cmd instrument cannot be used on an unrooted device.')
        if not self.no_install:
            host_file = context.resolver.get(Executable(self, self.device.abi, 'trace-cmd'))
            self.trace_cmd = self.device.install_executable(host_file)
        else:
            if not self.device.is_installed('trace-cmd'):
                raise ConfigError('No trace-cmd found on device and no_install=True is specified.')
            self.trace_cmd = 'trace-cmd'
        # Register ourselves as absolute last event before and
        #   first after so we can mark the trace at the right time
        signal.connect(self.insert_start_mark, signal.BEFORE_WORKLOAD_EXECUTION, priority=11)
        signal.connect(self.insert_end_mark, signal.AFTER_WORKLOAD_EXECUTION, priority=11)

    def setup(self, context):
        if self.mode == 'start':
            if self.buffer_size:
                self._set_buffer_size()
            self.device.execute('{} reset'.format(self.trace_cmd), as_root=True, timeout=180)
        elif self.mode == 'record':
            pass
        else:
            raise ValueError('Bad mode: {}'.format(self.mode))  # should never get here

    def very_slow_start(self, context):
        self.start_time = time.time()  # pylint: disable=attribute-defined-outside-init
        if self.mode == 'start':
            self.device.execute('{} start {}'.format(self.trace_cmd, self.event_string), as_root=True)
        elif self.mode == 'record':
            self.device.kick_off('{} record -o {} {}'.format(self.trace_cmd, self.output_file, self.event_string))
        else:
            raise ValueError('Bad mode: {}'.format(self.mode))  # should never get here

    def stop(self, context):
        self.stop_time = time.time()  # pylint: disable=attribute-defined-outside-init
        if self.mode == 'start':
            self.device.execute('{} stop'.format(self.trace_cmd), timeout=60, as_root=True)
        elif self.mode == 'record':
            # There will be a trace-cmd worker process per CPU core plus a main
            # control trace-cmd process. Interrupting the control process will
            # trigger the generation of the single binary trace file.
            trace_cmds = self.device.ps(name=self.trace_cmd)
            if not trace_cmds:
                raise InstrumentError('Could not find running trace-cmd on device.')
            # The workers will have their PPID set to the PID of control.
            parent_map = defaultdict(list)
            for entry in trace_cmds:
                parent_map[entry.ppid].append(entry.pid)
            controls = [v[0] for _, v in parent_map.iteritems()
                        if len(v) == 1 and v[0] in parent_map]
            if len(controls) > 1:
                self.logger.warning('More than one trace-cmd instance found; stopping all of them.')
            for c in controls:
                self.device.kill(c, signal='INT', as_root=True)
        else:
            raise ValueError('Bad mode: {}'.format(self.mode))  # should never get here

    def update_result(self, context):  # NOQA pylint: disable=R0912
        if self.mode == 'start':
            self.device.execute('{} extract -o {}'.format(self.trace_cmd, self.output_file),
                                timeout=TIMEOUT, as_root=True)
        elif self.mode == 'record':
            self.logger.debug('Waiting for trace.dat to be generated.')
            while self.device.ps(name=self.trace_cmd):
                time.sleep(2)
        else:
            raise ValueError('Bad mode: {}'.format(self.mode))  # should never get here

        # The size of trace.dat will depend on how long trace-cmd was running.
        # Therefore timout for the pull command must also be adjusted
        # accordingly.
        pull_timeout = (self.stop_time - self.start_time)
        self.device.pull_file(self.output_file, context.output_directory, timeout=pull_timeout)
        context.add_iteration_artifact('bintrace', OUTPUT_TRACE_FILE, kind='data',
                                       description='trace-cmd generated ftrace dump.')

        local_trace_file = os.path.join(context.output_directory, OUTPUT_TRACE_FILE)
        local_txt_trace_file = os.path.join(context.output_directory, OUTPUT_TEXT_FILE)

        if self.report:
            # To get the output of trace.dat, trace-cmd must be installed
            # This is done host-side because the generated file is very large
            if not os.path.isfile(local_trace_file):
                self.logger.warning('Not generating trace.txt, as {} does not exist.'.format(OUTPUT_TRACE_FILE))
            try:
                command = 'trace-cmd report {} > {}'.format(local_trace_file, local_txt_trace_file)
                self.logger.debug(command)
                process = subprocess.Popen(command, stderr=subprocess.PIPE, shell=True)
                _, error = process.communicate()
                if process.returncode:
                    raise InstrumentError('trace-cmd returned non-zero exit code {}'.format(process.returncode))
                if error:
                    # logged at debug level, as trace-cmd always outputs some
                    # errors that seem benign.
                    self.logger.debug(error)
                if os.path.isfile(local_txt_trace_file):
                    context.add_iteration_artifact('txttrace', OUTPUT_TEXT_FILE, kind='export',
                                                   description='trace-cmd generated ftrace dump.')
                    self.logger.debug('Verifying traces.')
                    with open(local_txt_trace_file) as fh:
                        for line in fh:
                            if 'EVENTS DROPPED' in line:
                                self.logger.warning('Dropped events detected.')
                                break
                        else:
                            self.logger.debug('Trace verified.')
                else:
                    self.logger.warning('Could not generate trace.txt.')
            except OSError:
                raise InstrumentError('Could not find trace-cmd. Please make sure it is installed and is in PATH.')

    def teardown(self, context):
        self.device.delete_file(os.path.join(self.device.working_directory, OUTPUT_TRACE_FILE))

    def on_run_end(self, context):
        pass

    def validate(self):
        if self.report and os.system('which trace-cmd > /dev/null'):
            raise InstrumentError('trace-cmd is not in PATH; is it installed?')
        if self.buffer_size:
            if self.mode == 'record':
                self.logger.debug('trace_buffer_size specified with record mode; it will be ignored.')
            else:
                try:
                    int(self.buffer_size)
                except ValueError:
                    raise ConfigError('trace_buffer_size must be an int.')

    def insert_start_mark(self, context):
        # trace marker appears in ftrace as an ftrace/print event with TRACE_MARKER_START in info field
        self.device.set_sysfile_value("/sys/kernel/debug/tracing/trace_marker", "TRACE_MARKER_START", verify=False)

    def insert_end_mark(self, context):
        # trace marker appears in ftrace as an ftrace/print event with TRACE_MARKER_STOP in info field
        self.device.set_sysfile_value("/sys/kernel/debug/tracing/trace_marker", "TRACE_MARKER_STOP", verify=False)

    def _set_buffer_size(self):
        target_buffer_size = self.buffer_size
        attempt_buffer_size = target_buffer_size
        buffer_size = 0
        floor = 1000 if target_buffer_size > 1000 else target_buffer_size
        while attempt_buffer_size >= floor:
            self.device.set_sysfile_value(self.buffer_size_file, attempt_buffer_size, verify=False)
            buffer_size = self.device.get_sysfile_value(self.buffer_size_file, kind=int)
            if buffer_size == attempt_buffer_size:
                break
            else:
                attempt_buffer_size -= self.buffer_size_step
        if buffer_size == target_buffer_size:
            return
        while attempt_buffer_size < target_buffer_size:
            attempt_buffer_size += self.buffer_size_step
            self.device.set_sysfile_value(self.buffer_size_file, attempt_buffer_size, verify=False)
            buffer_size = self.device.get_sysfile_value(self.buffer_size_file, kind=int)
            if attempt_buffer_size != buffer_size:
                self.logger.warning('Failed to set trace buffer size to {}, value set was {}'.format(target_buffer_size, buffer_size))
                break

def _build_trace_events(events):
    event_string = ' '.join(['-e {}'.format(e) for e in events])
    return event_string

<commit_msg>trace-cmd: Added ability to generate reports on target device<commit_after>#    Copyright 2013-2015 ARM Limited
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

# pylint: disable=W0613,E1101
from __future__ import division
import os
import time
import subprocess
from collections import defaultdict

from wlauto import Instrument, Parameter, Executable
from wlauto.exceptions import InstrumentError, ConfigError, DeviceError
from wlauto.core import signal
from wlauto.utils.types import boolean

OUTPUT_TRACE_FILE = 'trace.dat'
OUTPUT_TEXT_FILE = '{}.txt'.format(os.path.splitext(OUTPUT_TRACE_FILE)[0])
TIMEOUT = 180

class TraceCmdInstrument(Instrument):

    name = 'trace-cmd'
    description = """
    trace-cmd is an instrument which interacts with Ftrace Linux kernel internal
    tracer

    From trace-cmd man page:

    trace-cmd command interacts with the Ftrace tracer that is built inside the
    Linux kernel. It interfaces with the Ftrace specific files found in the
    debugfs file system under the tracing directory.

    trace-cmd reads a list of events it will trace, which can be specified in
    the config file as follows ::

        trace_events = ['irq*', 'power*']

    If no event is specified in the config file, trace-cmd traces the following events:

        - sched*
        - irq*
        - power*
        - cpufreq_interactive*

    The list of available events can be obtained by rooting and running the following
    command line on the device ::

       trace-cmd list

    You may also specify ``trace_buffer_size`` setting which must be an integer that will
    be used to set the ftrace buffer size. It will be interpreted as KB::

        trace_cmd_buffer_size = 8000

    The maximum buffer size varies from device to device, but there is a maximum and trying
    to set buffer size beyound that will fail. If you plan on collecting a lot of trace over
    long periods of time, the buffer size will not be enough and you will only get trace for
    the last portion of your run. To deal with this you can set the ``trace_mode`` setting to
    ``'record'`` (the default is ``'start'``)::

        trace_cmd_mode = 'record'

    This will cause trace-cmd to trace into file(s) on disk, rather than the buffer, and so the
    limit for the max size of the trace is set by the storage available on device. Bear in mind
    that ``'record'`` mode *is* more instrusive than the default, so if you do not plan on
    generating a lot of trace, it is best to use the default ``'start'`` mode.

    .. note:: Mode names correspend to the underlying trace-cmd exectuable's command used to
              implement them. You can find out more about what is happening in each case from
              trace-cmd documentation: https://lwn.net/Articles/341902/.

    This instrument comes with an Android trace-cmd binary that will be copied and used on the
    device, however post-processing will be done on-host and you must have trace-cmd installed and
    in your path. On Ubuntu systems, this may be done with::

        sudo apt-get install trace-cmd

    """

    parameters = [
        Parameter('events', kind=list, default=['sched*', 'irq*', 'power*', 'cpufreq_interactive*'],
                  global_alias='trace_events',
                  description="""
                  Specifies the list of events to be traced. Each event in the list will be passed to
                  trace-cmd with -e parameter and must be in the format accepted by trace-cmd.
                  """),
        Parameter('mode', default='start', allowed_values=['start', 'record'],
                  global_alias='trace_mode',
                  description="""
                  Trace can be collected using either 'start' or 'record' trace-cmd
                  commands. In 'start' mode, trace will be collected into the ftrace buffer;
                  in 'record' mode, trace will be written into a file on the device's file
                  system. 'start' mode is (in theory) less intrusive than 'record' mode, however
                  it is limited by the size of the ftrace buffer (which is configurable --
                  see ``buffer_size`` -- but only up to a point) and that may overflow
                  for long-running workloads, which will result in dropped events.
                  """),
        Parameter('buffer_size', kind=int, default=None,
                  global_alias='trace_buffer_size',
                  description="""
                  Attempt to set ftrace buffer size to the specified value (in KB). Default buffer size
                  may need to be increased for long-running workloads, or if a large number
                  of events have been enabled. Note: there is a maximum size that the buffer can
                  be set, and that varies from device to device. Attempting to set buffer size higher
                  than this will fail. In that case, this instrument will set the size to the highest
                  possible value by going down from the specified size in ``buffer_size_step`` intervals.
                  """),
        Parameter('buffer_size_step', kind=int, default=1000,
                  global_alias='trace_buffer_size_step',
                  description="""
                  Defines the decremental step used if the specified ``buffer_size`` could not be set.
                  This will be subtracted form the buffer size until set succeeds or size is reduced to
                  1MB.
                  """),
        Parameter('buffer_size_file', default='/sys/kernel/debug/tracing/buffer_size_kb',
                  description="""
                  Path to the debugs file that may be used to set ftrace buffer size. This should need
                  to be modified for the vast majority devices.
                  """),
        Parameter('report', kind=boolean, default=True,
                  description="""
                  Specifies whether reporting should be performed once the binary trace has been generated.
                  """),
        Parameter('no_install', kind=boolean, default=False,
                  description="""
                  Do not install the bundled trace-cmd  and use the one on the device instead. If there is
                  not already a trace-cmd on the device, an error is raised.

                  """),
        Parameter('report_on_target', kind=boolean, default=False,
                  description="""
                  When enabled generation of reports will be done host-side because the generated file is
                  very large. If trace-cmd is not available on the host device this setting and be disabled
                  and the report will be generated on the target device.

                  .. note:: This requires the latest version of trace-cmd to be installed on the host (the
                            one in your distribution's repos may be too old).
                  """),
    ]

    def __init__(self, device, **kwargs):
        super(TraceCmdInstrument, self).__init__(device, **kwargs)
        self.trace_cmd = None
        self.event_string = _build_trace_events(self.events)
        self.output_file = os.path.join(self.device.working_directory, OUTPUT_TRACE_FILE)
        self.temp_trace_file = self.device.path.join(self.device.working_directory, OUTPUT_TRACE_FILE)

    def on_run_init(self, context):
        if not self.device.is_rooted:
            raise InstrumentError('trace-cmd instrument cannot be used on an unrooted device.')
        if not self.no_install:
            host_file = context.resolver.get(Executable(self, self.device.abi, 'trace-cmd'))
            self.trace_cmd = self.device.install_executable(host_file)
        else:
            if not self.device.is_installed('trace-cmd'):
                raise ConfigError('No trace-cmd found on device and no_install=True is specified.')
            self.trace_cmd = 'trace-cmd'
        # Register ourselves as absolute last event before and
        #   first after so we can mark the trace at the right time
        signal.connect(self.insert_start_mark, signal.BEFORE_WORKLOAD_EXECUTION, priority=11)
        signal.connect(self.insert_end_mark, signal.AFTER_WORKLOAD_EXECUTION, priority=11)

    def setup(self, context):
        if self.mode == 'start':
            if self.buffer_size:
                self._set_buffer_size()
            self.device.execute('{} reset'.format(self.trace_cmd), as_root=True, timeout=180)
        elif self.mode == 'record':
            pass
        else:
            raise ValueError('Bad mode: {}'.format(self.mode))  # should never get here

    def very_slow_start(self, context):
        self.start_time = time.time()  # pylint: disable=attribute-defined-outside-init
        if self.mode == 'start':
            self.device.execute('{} start {}'.format(self.trace_cmd, self.event_string), as_root=True)
        elif self.mode == 'record':
            self.device.kick_off('{} record -o {} {}'.format(self.trace_cmd, self.output_file, self.event_string))
        else:
            raise ValueError('Bad mode: {}'.format(self.mode))  # should never get here

    def stop(self, context):
        self.stop_time = time.time()  # pylint: disable=attribute-defined-outside-init
        if self.mode == 'start':
            self.device.execute('{} stop'.format(self.trace_cmd), timeout=60, as_root=True)
        elif self.mode == 'record':
            # There will be a trace-cmd worker process per CPU core plus a main
            # control trace-cmd process. Interrupting the control process will
            # trigger the generation of the single binary trace file.
            trace_cmds = self.device.ps(name=self.trace_cmd)
            if not trace_cmds:
                raise InstrumentError('Could not find running trace-cmd on device.')
            # The workers will have their PPID set to the PID of control.
            parent_map = defaultdict(list)
            for entry in trace_cmds:
                parent_map[entry.ppid].append(entry.pid)
            controls = [v[0] for _, v in parent_map.iteritems()
                        if len(v) == 1 and v[0] in parent_map]
            if len(controls) > 1:
                self.logger.warning('More than one trace-cmd instance found; stopping all of them.')
            for c in controls:
                self.device.kill(c, signal='INT', as_root=True)
        else:
            raise ValueError('Bad mode: {}'.format(self.mode))  # should never get here

    def update_result(self, context):  # NOQA pylint: disable=R0912
        if self.mode == 'start':
            self.device.execute('{} extract -o {}'.format(self.trace_cmd, self.output_file),
                                timeout=TIMEOUT, as_root=True)
        elif self.mode == 'record':
            self.logger.debug('Waiting for trace.dat to be generated.')
            while self.device.ps(name=self.trace_cmd):
                time.sleep(2)
        else:
            raise ValueError('Bad mode: {}'.format(self.mode))  # should never get here

        # The size of trace.dat will depend on how long trace-cmd was running.
        # Therefore timout for the pull command must also be adjusted
        # accordingly.
        self._pull_timeout = (self.stop_time - self.start_time)  # pylint: disable=attribute-defined-outside-init
        self.device.pull_file(self.output_file, context.output_directory, timeout=self._pull_timeout)
        context.add_iteration_artifact('bintrace', OUTPUT_TRACE_FILE, kind='data',
                                       description='trace-cmd generated ftrace dump.')

        local_txt_trace_file = os.path.join(context.output_directory, OUTPUT_TEXT_FILE)

        if self.report:
            # To get the output of trace.dat, trace-cmd must be installed
            # By default this is done host-side because the generated file is
            # very large
            if self.report_on_target:
                self._generate_report_on_target(context)
            else:
                self._generate_report_on_host(context)

            if os.path.isfile(local_txt_trace_file):
                context.add_iteration_artifact('txttrace', OUTPUT_TEXT_FILE, kind='export',
                                               description='trace-cmd generated ftrace dump.')
                self.logger.debug('Verifying traces.')
                with open(local_txt_trace_file) as fh:
                    for line in fh:
                        if 'EVENTS DROPPED' in line:
                            self.logger.warning('Dropped events detected.')
                            break
                    else:
                        self.logger.debug('Trace verified.')
            else:
                self.logger.warning('Could not generate trace.txt.')

    def teardown(self, context):
        self.device.delete_file(os.path.join(self.device.working_directory, OUTPUT_TRACE_FILE))

    def on_run_end(self, context):
        pass

    def validate(self):
        if self.report and os.system('which trace-cmd > /dev/null'):
            raise InstrumentError('trace-cmd is not in PATH; is it installed?')
        if self.buffer_size:
            if self.mode == 'record':
                self.logger.debug('trace_buffer_size specified with record mode; it will be ignored.')
            else:
                try:
                    int(self.buffer_size)
                except ValueError:
                    raise ConfigError('trace_buffer_size must be an int.')

    def insert_start_mark(self, context):
        # trace marker appears in ftrace as an ftrace/print event with TRACE_MARKER_START in info field
        self.device.set_sysfile_value("/sys/kernel/debug/tracing/trace_marker", "TRACE_MARKER_START", verify=False)

    def insert_end_mark(self, context):
        # trace marker appears in ftrace as an ftrace/print event with TRACE_MARKER_STOP in info field
        self.device.set_sysfile_value("/sys/kernel/debug/tracing/trace_marker", "TRACE_MARKER_STOP", verify=False)

    def _set_buffer_size(self):
        target_buffer_size = self.buffer_size
        attempt_buffer_size = target_buffer_size
        buffer_size = 0
        floor = 1000 if target_buffer_size > 1000 else target_buffer_size
        while attempt_buffer_size >= floor:
            self.device.set_sysfile_value(self.buffer_size_file, attempt_buffer_size, verify=False)
            buffer_size = self.device.get_sysfile_value(self.buffer_size_file, kind=int)
            if buffer_size == attempt_buffer_size:
                break
            else:
                attempt_buffer_size -= self.buffer_size_step
        if buffer_size == target_buffer_size:
            return
        while attempt_buffer_size < target_buffer_size:
            attempt_buffer_size += self.buffer_size_step
            self.device.set_sysfile_value(self.buffer_size_file, attempt_buffer_size, verify=False)
            buffer_size = self.device.get_sysfile_value(self.buffer_size_file, kind=int)
            if attempt_buffer_size != buffer_size:
                self.logger.warning('Failed to set trace buffer size to {}, value set was {}'.format(target_buffer_size, buffer_size))
                break

    def _generate_report_on_target(self, context):
        try:
            trace_file = self.output_file
            txt_trace_file = os.path.join(self.device.working_directory, OUTPUT_TEXT_FILE)
            command = 'trace-cmd report {} > {}'.format(trace_file, txt_trace_file)
            self.device.execute(command)
            self.device.pull_file(txt_trace_file, context.output_directory, timeout=self._pull_timeout)
        except DeviceError:
            raise InstrumentError('Could not generate TXT report on target.')

    def _generate_report_on_host(self, context):
        local_trace_file = os.path.join(context.output_directory, OUTPUT_TRACE_FILE)
        local_txt_trace_file = os.path.join(context.output_directory, OUTPUT_TEXT_FILE)
        command = 'trace-cmd report {} > {}'.format(local_trace_file, local_txt_trace_file)
        self.logger.debug(command)
        if not os.path.isfile(local_trace_file):
            self.logger.warning('Not generating trace.txt, as {} does not exist.'.format(OUTPUT_TRACE_FILE))
        else:
            try:
                process = subprocess.Popen(command, stderr=subprocess.PIPE, shell=True)
                _, error = process.communicate()
                if process.returncode:
                    raise InstrumentError('trace-cmd returned non-zero exit code {}'.format(process.returncode))
                if error:
                    # logged at debug level, as trace-cmd always outputs some
                    # errors that seem benign.
                    self.logger.debug(error)
            except OSError:
                raise InstrumentError('Could not find trace-cmd. Please make sure it is installed and is in PATH.')

def _build_trace_events(events):
    event_string = ' '.join(['-e {}'.format(e) for e in events])
    return event_string
<|endoftext|>


<commit_before>import dupescan

import argparse
import collections
import itertools
import os
import sys

__all__ = [ "correlate", "run" ]

def get_arg_parser():
    p = argparse.ArgumentParser(
        description="Compare two directories by content.",
        epilog="""If none of -m/--matches, -r/--removes, -a/--adds is
                  specified, all are reported."""
    )

    p.add_argument("dirs",
        nargs=2,
        metavar="DIR",
        help="""Paths to the directories to be compared."""
    )

    #p.add_argument("-s", "--symlinks",
        #action="store_true",
        #help="""Include symlinks."""
    #)

    p.add_argument("-v", "--verbose",
        action="store_true",
        help="""Log detailed information to STDERR."""
    )

    p.add_argument("-m", "--matches",
        action="store_true",
        help="""List files that appear in both directories."""
    )

    p.add_argument("-r", "--removes",
        action="store_true",
        help="""List files that appear only as a descendant of the first directory."""
    )

    p.add_argument("-a", "--adds",
        action="store_true",
        help="""List files that appear only as a descendant of the second directory."""
    )

    p.add_argument("-c", "--colorize",
        dest="colorize",
        default=None,
        action="store_true",
        help="""Colorize output."""
    )

    p.add_argument("--no-colorize",
        dest="colorize",
        action="store_false",
        help="""Force colorizing off. If neither --colorize or --no-colorize is
                specified, it will be enabled if a compatible terminal is
                detected."""
    )

    p.add_argument("--no-summary",
        action="store_true",
        help="""Suppress the summary."""
    )

    p.add_argument("--version",
        action="version",
        version="%(prog)s " + dupescan.__version__
    )

    return p

MATCH = 1
ADDED = 2
REMOVED = 4
ACTION_ORDER = (MATCH, ADDED, REMOVED)
ALL_ACTIONS = sum(ACTION_ORDER)
def correlate(root1, root2, verbose=False):
    origin = collections.defaultdict(int)

    def iter_origin():
        for path, bits in origin.items():
            if bits & 1:
                yield path
            if bits & 2:
                yield path

    ignore_symlinks = lambda f: not os.path.islink(f)
    for root, bit in ((root1, 1), (root2, 2)):
        for path in dupescan.walk.recurse_iterator((root,), ignore_symlinks, ignore_symlinks):
            origin[path] |= bit

    for dupe_set in dupescan.find_duplicate_files(
        iter_origin(),
        collect_inodes=False,
        unique_paths=False,
        error_cb="print_stderr",
        log_cb="print_stderr" if verbose else None
    ):
        partitions = [ [ ], [ ] ]
        for instance in dupe_set:
            path = instance.path()

            if origin[path] & 1:
                partitions[0].append(path)
                origin[path] &= ~1

            elif origin[path] & 2:
                partitions[1].append(path)
                origin[path] &= ~2

            if origin[path] == 0:
                del origin[path]

        for a, b in itertools.zip_longest(*partitions):
            if a is None:
                # in this case, b cannot be None. b was added
                assert b is not None, "zip_longest somehow yielded (None, None)"
                action = ADDED
            else: # a is not None
                if b is None:
                    # a was removed
                    action = REMOVED
                else:
                    # a and b match
                    action = MATCH

            yield (action, a, b)

    # origin paths are removed as they are encountered. all that remains at
    # this point will be files that weren't matched in the other root
    for path, bit in origin.items():
        assert bit not in (0, 3), "{!r} has invalid value {}".format(path, bit)
        if bit == 1:
            yield (REMOVED, path, None)
        else:
            yield (ADDED, None, path)

ACTION_STRINGS = {
    MATCH:   ("=", "Matches", None),
    ADDED:   ("+", "Adds",    "32"),
    REMOVED: ("-", "Removes", "31")
}
SYMBOL = 0
SUMMARY_WORD = 1
DEFAULT_SGR = 2

def format_ansi_sgr(string, sgr):
    if sgr is None:
        return string
    return "\x1b[{}m{}\x1b[0m".format(sgr, string)

def print_report(
    root1, root2,
    filter_actions=ALL_ACTIONS,
    ansi=None, # None: autodetect, True: use default, False: no colors, or tuple of 3 things to appear between \x1b[ and m
    summary=True,
    verbose=False,
    file=None
):
    if file is None:
        file = sys.stdout

    if ansi is None:
        try:
            ansi = file.isatty()
        except AttributeError:
            ansi = False

    if ansi is True:
        ansi = tuple(
            ACTION_STRINGS[action][DEFAULT_SGR]
            for action in ACTION_ORDER
        )
    elif ansi is False:
        ansi = (None, None, None)

    sgr_lookup = dict(zip(ACTION_ORDER, ansi))

    action_count = collections.Counter()

    for action, path1, path2 in correlate(root1, root2, verbose=verbose):
        action_count[action] += 1
        if filter_actions & action:
            symbol = ACTION_STRINGS[action][SYMBOL]

            if path1 is not None:
                print(format_ansi_sgr(
                    "{} {}".format(symbol, dupescan.report.format_path(path1)),
                    sgr_lookup[action]
                ), file=file)
                symbol = " "

            if path2 is not None:
                print(format_ansi_sgr(
                    "{} {}".format(symbol, dupescan.report.format_path(path2)),
                    sgr_lookup[action]
                ), file=file)

            print()

    if summary:
        print(
            "# " +
            ", ".join(
                "{}: {}".format(ACTION_STRINGS[action][SUMMARY_WORD], action_count[action])
                for action in (MATCH, ADDED, REMOVED)
            ),
            file=file
        )

def run(argv=None):
    p = get_arg_parser()
    args = p.parse_args(argv)

    filter_actions = sum(
        action for
        action, select in (
            (MATCH,   args.matches),
            (ADDED,   args.adds),
            (REMOVED, args.removes)
        )
        if select
    )

    if filter_actions == 0:
        filter_actions = ALL_ACTIONS

    print_report(
        *args.dirs,
        filter_actions=filter_actions,
        ansi=args.colorize,
        summary=not args.no_summary,
        verbose=args.verbose,
        file=sys.stdout
    )

    return 0

def main():
    return run(sys.argv[1:])
<commit_msg>correlate:print_report -> generate_report and add it to __all__<commit_after>import dupescan

import argparse
import collections
import itertools
import os
import sys

__all__ = [ "correlate", "generate_report", "run" ]

def get_arg_parser():
    p = argparse.ArgumentParser(
        description="Compare two directories by content.",
        epilog="""If none of -m/--matches, -r/--removes, -a/--adds is
                  specified, all are reported."""
    )

    p.add_argument("dirs",
        nargs=2,
        metavar="DIR",
        help="""Paths to the directories to be compared."""
    )

    #p.add_argument("-s", "--symlinks",
        #action="store_true",
        #help="""Include symlinks."""
    #)

    p.add_argument("-v", "--verbose",
        action="store_true",
        help="""Log detailed information to STDERR."""
    )

    p.add_argument("-m", "--matches",
        action="store_true",
        help="""List files that appear in both directories."""
    )

    p.add_argument("-r", "--removes",
        action="store_true",
        help="""List files that appear only as a descendant of the first directory."""
    )

    p.add_argument("-a", "--adds",
        action="store_true",
        help="""List files that appear only as a descendant of the second directory."""
    )

    p.add_argument("-c", "--colorize",
        dest="colorize",
        default=None,
        action="store_true",
        help="""Colorize output."""
    )

    p.add_argument("--no-colorize",
        dest="colorize",
        action="store_false",
        help="""Force colorizing off. If neither --colorize or --no-colorize is
                specified, it will be enabled if a compatible terminal is
                detected."""
    )

    p.add_argument("--no-summary",
        action="store_true",
        help="""Suppress the summary."""
    )

    p.add_argument("--version",
        action="version",
        version="%(prog)s " + dupescan.__version__
    )

    return p

MATCH = 1
ADDED = 2
REMOVED = 4
ACTION_ORDER = (MATCH, ADDED, REMOVED)
ALL_ACTIONS = sum(ACTION_ORDER)
def correlate(root1, root2, verbose=False):
    origin = collections.defaultdict(int)

    def iter_origin():
        for path, bits in origin.items():
            if bits & 1:
                yield path
            if bits & 2:
                yield path

    ignore_symlinks = lambda f: not os.path.islink(f)
    for root, bit in ((root1, 1), (root2, 2)):
        for path in dupescan.walk.recurse_iterator((root,), ignore_symlinks, ignore_symlinks):
            origin[path] |= bit

    for dupe_set in dupescan.find_duplicate_files(
        iter_origin(),
        collect_inodes=False,
        unique_paths=False,
        error_cb="print_stderr",
        log_cb="print_stderr" if verbose else None
    ):
        partitions = [ [ ], [ ] ]
        for instance in dupe_set:
            path = instance.path()

            if origin[path] & 1:
                partitions[0].append(path)
                origin[path] &= ~1

            elif origin[path] & 2:
                partitions[1].append(path)
                origin[path] &= ~2

            if origin[path] == 0:
                del origin[path]

        for a, b in itertools.zip_longest(*partitions):
            if a is None:
                # in this case, b cannot be None. b was added
                assert b is not None, "zip_longest somehow yielded (None, None)"
                action = ADDED
            else: # a is not None
                if b is None:
                    # a was removed
                    action = REMOVED
                else:
                    # a and b match
                    action = MATCH

            yield (action, a, b)

    # origin paths are removed as they are encountered. all that remains at
    # this point will be files that weren't matched in the other root
    for path, bit in origin.items():
        assert bit not in (0, 3), "{!r} has invalid value {}".format(path, bit)
        if bit == 1:
            yield (REMOVED, path, None)
        else:
            yield (ADDED, None, path)

ACTION_STRINGS = {
    MATCH:   ("=", "Matches", None),
    ADDED:   ("+", "Adds",    "32"),
    REMOVED: ("-", "Removes", "31")
}
SYMBOL = 0
SUMMARY_WORD = 1
DEFAULT_SGR = 2

def format_ansi_sgr(string, sgr):
    if sgr is None:
        return string
    return "\x1b[{}m{}\x1b[0m".format(sgr, string)

def generate_report(
    root1, root2,
    filter_actions=ALL_ACTIONS,
    ansi=None, # None: autodetect, True: use default, False: no colors, or tuple of 3 things to appear between \x1b[ and m
    summary=True,
    verbose=False,
    file=None
):
    if file is None:
        file = sys.stdout

    if ansi is None:
        try:
            ansi = file.isatty()
        except AttributeError:
            ansi = False

    if ansi is True:
        ansi = tuple(
            ACTION_STRINGS[action][DEFAULT_SGR]
            for action in ACTION_ORDER
        )
    elif ansi is False:
        ansi = (None, None, None)

    sgr_lookup = dict(zip(ACTION_ORDER, ansi))

    action_count = collections.Counter()

    for action, path1, path2 in correlate(root1, root2, verbose=verbose):
        action_count[action] += 1
        if filter_actions & action:
            symbol = ACTION_STRINGS[action][SYMBOL]

            if path1 is not None:
                print(format_ansi_sgr(
                    "{} {}".format(symbol, dupescan.report.format_path(path1)),
                    sgr_lookup[action]
                ), file=file)
                symbol = " "

            if path2 is not None:
                print(format_ansi_sgr(
                    "{} {}".format(symbol, dupescan.report.format_path(path2)),
                    sgr_lookup[action]
                ), file=file)

            print()

    if summary:
        print(
            "# " +
            ", ".join(
                "{}: {}".format(ACTION_STRINGS[action][SUMMARY_WORD], action_count[action])
                for action in (MATCH, ADDED, REMOVED)
            ),
            file=file
        )

def run(argv=None):
    p = get_arg_parser()
    args = p.parse_args(argv)

    filter_actions = sum(
        action for
        action, select in (
            (MATCH,   args.matches),
            (ADDED,   args.adds),
            (REMOVED, args.removes)
        )
        if select
    )

    if filter_actions == 0:
        filter_actions = ALL_ACTIONS

    generate_report(
        *args.dirs,
        filter_actions=filter_actions,
        ansi=args.colorize,
        summary=not args.no_summary,
        verbose=args.verbose,
        file=sys.stdout
    )

    return 0

def main():
    return run(sys.argv[1:])
<|endoftext|>


<commit_before>from dijkstra import shortestPath
from UserDict import DictMixin

class Station(DictMixin):
    def __init__(self, line, name, links):
        self.line = line
        self.name = name
        self.links = links
    def __getitem__(self, key):
        return self.links[key]
    def __setitem__(self, key, item):
        self.links[key] = item
    def __delitem__(self, key):
        del self.links[key]
    def keys(self):
        return self.links.keys()
    def __repr__(self):
        return 'Station(line=%s name=%s)' % (self.line, self.name,)
    def get_unique_name(self):
        return "%s_%s" % (self.line, self.name)

class TransitNetwork(DictMixin):
    def disjoin(self, key):
        line, name = key.split("_")
        return (line, name)
    def rejoin(self, line, name):
        return "_".join([line,name])
    def __init__(self, *args, **kwargs):
        self.station_dict = {}
    def __getitem__(self, key):
        line, name = self.disjoin(key)
        return self.station_dict[line][name]
    def __setitem__(self, key, item):
        line, name = self.disjoin(key)
        self.station_dict[line] = {'name':item}
    def __delitem__(self, key):
        line, name = self.disjoin(key)
        del self.station_dict[line][name]
        if not len(self.station_dict[line]):
            del self.station_dict[line]
    def keys(self):
        keys = self.station_dict.keys()
        return keys
    def add_station(self, key, links):
        line, name = self.disjoin(key)
        station = Station(line, name, links)
        if not self.station_dict.has_key(line):
            self.station_dict[line] = {name:station}
        else:
            self.station_dict[line].update({name:station})
    def get_stops_on_line(self, line):
        return_value = []
        if self.station_dict.has_key(line):
            return_value =  [self.rejoin(line, stop) for stop in self.station_dict[line].keys()]
        return return_value
    def get_stops_at_station(self, name):
        keys = []
        for line, v in self.station_dict.items():
            if v.has_key(name):
                keys.append(self.rejoin(line,name))
        return keys
    def shortest_path(self, start, end):
        return shortestPath(self, start, end)
    def __repr__(self):
        return 'TransitNetwork(<>)'

tn = TransitNetwork()
tn.add_station('red_b', {'red_d':5})
tn.add_station('red_d', {'red_g':1, 'green_d':5, 'brown_d':5})
tn.add_station('red_g', {'red_d':1, 'red_i':1})
tn.add_station('red_i', {'red_k':1, 'blue_i':5})
tn.add_station('red_k', {'red_i':1, 'red_l':3, 'brown_k':5, })
tn.add_station('red_l', {'red_k':3})

tn.add_station('green_c', {'green_e':2})
tn.add_station('green_e', {'green_c':2, 'green_d':1, 'brown_e':5, 'blue_e':5})
tn.add_station('green_d', {'green_e':1, 'red_d':5, 'brown_d':5, 'green_h':3})
tn.add_station('green_h', {'green_d':3, 'green_j':2, 'brown_h':5})
tn.add_station('green_j', {'green_h':2, 'green_m':5, 'brown_j':5})
tn.add_station('green_m', {'green_j':5})

tn.add_station('blue_n', {'blue_e':2})
tn.add_station('blue_e', {'blue_n':2, 'blue_i':2, 'green_e':5, 'brown_e':5})
tn.add_station('blue_i', {'blue_e':2, 'blue_o':5, 'red_i':5})
tn.add_station('blue_o', {'blue_i':5})

tn.add_station('brown_a', {'brown_f':2})
tn.add_station('brown_f', {'brown_k':3})
tn.add_station('brown_k', {'brown_j':2, 'red_k':5})
tn.add_station('brown_j', {'brown_h':2, 'green_j':5})
tn.add_station('brown_h', {'brown_d':3, 'green_h':5})
tn.add_station('brown_d', {'brown_e':1, 'red_d':5, 'green_d':5})
tn.add_station('brown_e', {'brown_a':2, 'blue_e':5, 'green_e':5})

tn.shortest_path('green_c', 'red_k')
tn.get_stops_on_line('green')
tn.get_stops_at_station('e')

<commit_msg>Added most of the loop connections<commit_after>from dijkstra import shortestPath
from UserDict import DictMixin

class Station(DictMixin):
    def __init__(self, line, name, links):
        self.line = line
        self.name = name
        self.links = links
    def __getitem__(self, key):
        return self.links[key]
    def __setitem__(self, key, item):
        self.links[key] = item
    def __delitem__(self, key):
        del self.links[key]
    def keys(self):
        return self.links.keys()
    def __repr__(self):
        return 'Station(line=%s name=%s)' % (self.line, self.name,)
    def get_unique_name(self):
        return "%s_%s" % (self.line, self.name)

class TransitNetwork(DictMixin):
    def disjoin(self, key):
        line, name = key.split("_")
        return (line, name)
    def rejoin(self, line, name):
        return "_".join([line,name])
    def __init__(self, *args, **kwargs):
        self.station_dict = {}
        self._path_cache = {}
    def __getitem__(self, key):
        line, name = self.disjoin(key)
        return self.station_dict[line][name]
    def __setitem__(self, key, item):
        line, name = self.disjoin(key)
        self.station_dict[line] = {'name':item}
    def __delitem__(self, key):
        line, name = self.disjoin(key)
        del self.station_dict[line][name]
        if not len(self.station_dict[line]):
            del self.station_dict[line]
    def keys(self):
        keys = self.station_dict.keys()
        return keys
    def add_station(self, key, links, *args, **kwargs):
        line, name = self.disjoin(key)
        station = Station(line, name, links)
        if not self.station_dict.has_key(line):
            self.station_dict[line] = {name:station}
        else:
            self.station_dict[line].update({name:station})
    def get_stops_on_line(self, line):
        return_value = []
        if self.station_dict.has_key(line):
            return_value =  [self.rejoin(line, stop) for stop in self.station_dict[line].keys()]
        return return_value
    def get_stops_at_station(self, name):
        keys = []
        for line, v in self.station_dict.items():
            if v.has_key(name):
                keys.append(self.rejoin(line,name))
        return keys
    def _shortest_path(self, start, end):
        return shortestPath(self, start, end)
    def shortest_path(self, start, end):
        cache_key = "%s.%s" % (start, end)
        if self._path_cache.has_key(cache_key):
            return_value = self._path_cache[cache_key]
        else:
            return_value = self._shortest_path(start, end)
            self._path_cache[cache_key] = return_value
        return return_value
    def clear_path_cache(self):
        self.path_cache = {}
    def __repr__(self):
        return 'TransitNetwork(<>)'

tn = TransitNetwork()
wait_time = 5.0

#>>> trans(['Brn', 'P'], '40460', desc="Merchandise Mart")
tn.add_station('Brn_40460', {'P_40460':wait_time, 'Brn_40730':2}, desc='Merchandise Mart')  # done
tn.add_station('P_40460', {'Brn_40460':wait_time, }, desc='Merchandise Mart')   # done loop

#>>> trans(['Brn', 'P', 'Org', 'Pink'], '40730', desc="Washington/Wells")
tn.add_station('Brn_40730', {'Org_40730':wait_time, 'P_40730':wait_time, 'Pink_40730':wait_time, 'Brn_40040':1, }, desc='Washington/Wells') # done
tn.add_station('P_40730', {'Brn_40730':wait_time, 'Org_40730':wait_time, 'Pink_40730':wait_time, 'P_40460':2, }, desc='Washington/Wells')   # done
tn.add_station('Org_40730', {'Brn_40730':wait_time, 'P_40730':wait_time, 'Pink_40730':wait_time, 'Org_40380':3 }, desc='Washington/Wells')  # done
tn.add_station('Pink_40730', {'Brn_40730':wait_time, 'Org_40730':wait_time, 'P_40730':wait_time, }, desc='Washington/Wells')

#>>> trans(['Brn', 'P', 'Org', 'Pink'], '40040', desc="Quincy")
tn.add_station('Brn_40040', {'Pink_40040':wait_time, 'P_40040':wait_time, 'Org_40040':wait_time, 'Brn_40160':1, }, desc='Quincy') # done
tn.add_station('P_40040', {'Brn_40040':wait_time, 'Pink_40040':wait_time, 'Org_40040':wait_time, 'P_40730':1, }, desc='Quincy', ) # done
tn.add_station('Org_40040', {'Brn_40040':wait_time, 'Pink_40040':wait_time, 'P_40040':wait_time, 'Org_40730':1, }, desc='Quincy') # done
tn.add_station('Pink_40040', {'Brn_40040':wait_time, 'P_40040':wait_time, 'Org_40040':wait_time, 'Pink_40730':1 }, desc='Quincy') # done

#>>> trans(['Brn', 'P', 'Org', 'Pink'], '40160', desc="LaSalle")
tn.add_station('Brn_40160', {'P_40160':wait_time, 'Pink_40160':wait_time, 'Org_40160':wait_time, 'Brn_40850':1, }, desc='LaSalle') # done
tn.add_station('P_40160', {'Pink_40160':wait_time, 'Brn_40160':wait_time, 'Org_40160':wait_time, 'P_40040':1, }, desc='LaSalle') # done
tn.add_station('Org_40160', {'P_40160':wait_time, 'Pink_40160':wait_time, 'Brn_40160':wait_time, 'Org_40040':1, }, desc='LaSalle') # done
tn.add_station('Pink_40160', {'P_40160':wait_time, 'Brn_40160':wait_time, 'Org_40160':wait_time, 'Pink_40040':1 }, desc='LaSalle') # done

#>>> trans(['Brn', 'P', 'Org', 'Pink'], '40850', desc="Library")
tn.add_station('Brn_40850', {'Pink_40850':wait_time, 'P_40850':wait_time, 'Org_40850':wait_time, 'Red_40560':10, 'Brn_40680':2, }, desc='Library') # partial
tn.add_station('P_40850', {'Brn_40850':wait_time, 'Pink_40850':wait_time, 'Org_40850':wait_time, 'Red_40560':10, 'P_40160':1, }, desc='Library')  # partial
tn.add_station('Org_40850', {'Brn_40850':wait_time, 'Pink_40850':wait_time, 'P_40850':wait_time, 'Red_40560':10, 'Org_40160':1, }, desc='Library')  # partial
tn.add_station('Pink_40850', {'Brn_40850':wait_time, 'P_40850':wait_time, 'Org_40850':wait_time, 'Red_40560':10, 'Pink_40160':1, }, desc='Library') # partial

#>>> trans(['Brn', 'P', 'Org', 'G', 'Pink'], '40680', desc="Adams/Wabash")
tn.add_station('Brn_40680', {'P_40680':wait_time, 'G_40680':wait_time, 'Org_40680':wait_time, 'Pink_40680':wait_time, 'Brn_40640':1, }, desc='Adams/Wabash') # done
tn.add_station('P_40680', {'Brn_40680':wait_time, 'G_40680':wait_time, 'Org_40680':wait_time, 'Pink_40680':wait_time, 'P_40850':2, }, desc='Adams/Wabash') # done
tn.add_station('Org_40680', {'Brn_40680':wait_time, 'P_40680':wait_time, 'G_40680':wait_time, 'Pink_40680':wait_time, 'Org_41400':4, }, desc='Adams/Wabash') # done
tn.add_station('G_40680', {'Brn_40680':wait_time, 'P_40680':wait_time, 'Org_40680':wait_time, 'Pink_40680':wait_time, 'G_41400':3, 'G_40640':1, }, desc='Adams/Wabash') # done
tn.add_station('Pink_40680', {'Brn_40680':wait_time, 'P_40680':wait_time, 'G_40680':wait_time, 'Org_40680':wait_time, 'Pink_40850':2, }, desc='Adams/Wabash') # done

#>>> trans(['Brn', 'P', 'Org', 'G', 'Pink'], '40640', desc="Maddison/Wabash")
tn.add_station('Brn_40640', {'Org_40640':wait_time, 'P_40640':wait_time, 'Pink_40640':wait_time, 'G_40640':wait_time, 'Brn_40200':1, }, desc='Maddison/Wabash') # done
tn.add_station('P_40640', {'Org_40640':wait_time, 'Pink_40640':wait_time, 'Brn_40640':wait_time, 'G_40640':wait_time, 'P_40680':1, }, desc='Maddison/Wabash') # done
tn.add_station('Org_40640', {'P_40640':wait_time, 'Pink_40640':wait_time, 'Brn_40640':wait_time, 'G_40640':wait_time, 'Org_40680':1, }, desc='Maddison/Wabash') # done
tn.add_station('G_40640', {'Org_40640':wait_time, 'P_40640':wait_time, 'Pink_40640':wait_time, 'Brn_40640':wait_time, 'G_40680':1, 'G_40200':1, }, desc='Maddison/Wabash') # done
tn.add_station('Pink_40640', {'Org_40640':wait_time, 'P_40640':wait_time, 'Brn_40640':wait_time, 'G_40640':wait_time, 'Pink_40680':1, }, desc='Maddison/Wabash')  # done

#>>> trans(['Brn', 'P', 'Org', 'G', 'Pink'], '40200', desc="Randolph/Wabash")
tn.add_station('Brn_40200', {'G_40200':wait_time, 'P_40200':wait_time, 'Org_40200':wait_time, 'Pink_40200':wait_time, 'Brn_40260':1, }, desc='Randolph/Wabash') # done
tn.add_station('P_40200', {'Brn_40200':wait_time, 'G_40200':wait_time, 'Org_40200':wait_time, 'Pink_40200':wait_time, 'P_40640':1, }, desc='Randolph/Wabash') # done
tn.add_station('Org_40200', {'Brn_40200':wait_time, 'G_40200':wait_time, 'P_40200':wait_time, 'Pink_40200':wait_time, 'Org_40640':1, }, desc='Randolph/Wabash') # done
tn.add_station('G_40200', {'Brn_40200':wait_time, 'P_40200':wait_time, 'Org_40200':wait_time, 'Pink_40200':wait_time, 'G_40640':1,  'G_40260':1, }, desc='Randolph/Wabash') # done
tn.add_station('Pink_40200', {'Brn_40200':wait_time, 'G_40200':wait_time, 'P_40200':wait_time, 'Org_40200':wait_time, 'Pink_40640':1 }, desc='Randolph/Wabash') # done

#>>> trans(['Brn', 'P', 'Org', 'G', 'Pink'], '40260', desc="State/Lake")
tn.add_station('Brn_40260', {'G_40260':wait_time, 'Org_40260':wait_time, 'Pink_40260':wait_time, 'P_40260':wait_time, 'Red_41660':wait_time, 'Brn_40380':1 }, desc='State/Lake')  # done
tn.add_station('P_40260', {'G_40260':wait_time, 'Org_40260':wait_time, 'Pink_40260':wait_time, 'Brn_40260':wait_time, 'Red_41660':wait_time, 'P_40200':1, }, desc='State/Lake')  # done
tn.add_station('Org_40260', {'G_40260':wait_time, 'Pink_40260':wait_time, 'Brn_40260':wait_time, 'P_40260':wait_time, 'Red_41660':wait_time, 'Org_40200':1 }, desc='State/Lake')  # done
tn.add_station('G_40260', {'Org_40260':wait_time, 'Pink_40260':wait_time, 'Brn_40260':wait_time, 'P_40260':wait_time, 'Red_41660':wait_time, 'G_40200':1, 'G_40380':1 }, desc='State/Lake') # done
tn.add_station('Pink_40260', {'G_40260':wait_time, 'Org_40260':wait_time, 'Brn_40260':wait_time, 'P_40260':wait_time, 'Red_41660':wait_time, 'Pink_40200':1 }, desc='State/Lake')  # done

#>>> trans(['Brn', 'P', 'Org', 'G', 'Blue', 'Pink'], '40380', desc="Clark/Lake")
tn.add_station('Brn_40380', {'Blue_40380':wait_time, 'Pink_40380':wait_time, 'Org_40380':wait_time, 'G_40380':wait_time, 'P_40380':wait_time, 'Brn_40460':4, }, desc='Clark/Lake')  # partial
tn.add_station('P_40380', {'Blue_40380':wait_time, 'Brn_40380':wait_time, 'Pink_40380':wait_time, 'Org_40380':wait_time, 'G_40380':wait_time, 'P_40260':1, }, desc='Clark/Lake')  # partial
tn.add_station('Org_40380', {'Blue_40380':wait_time, 'Brn_40380':wait_time, 'Pink_40380':wait_time, 'G_40380':wait_time, 'P_40380':wait_time, 'Org_40260':1, }, desc='Clark/Lake')  # partial
tn.add_station('G_40380', {'Blue_40380':wait_time, 'Brn_40380':wait_time, 'Pink_40380':wait_time, 'Org_40380':wait_time, 'P_40380':wait_time, 'G_40260': 1, 'G_41160':2 }, desc='Clark/Lake')  # partial
tn.add_station('Blue_40380', {'Brn_40380':wait_time, 'Pink_40380':wait_time, 'Org_40380':wait_time, 'G_40380':wait_time, 'P_40380':wait_time, }, desc='Clark/Lake')
tn.add_station('Pink_40380', {'Blue_40380':wait_time, 'Brn_40380':wait_time, 'Org_40380':wait_time, 'G_40380':wait_time, 'P_40380':wait_time, 'Pink_40260':1, }, desc='Clark/Lake') # partial

#>>> trans(['Red', 'Org', 'G', ], '41400', desc="Roosevelt")
tn.add_station('Red_41400', {'Org_41400':wait_time, 'G_41400':wait_time, }, desc='Roosevelt')
tn.add_station('Org_41400', {'Red_41400':wait_time, 'G_41400':wait_time, 'Org_40850':3 }, desc='Roosevelt')  # done loop
tn.add_station('G_41400', {'Org_41400':wait_time, 'Red_41400':wait_time, 'G_40680':3, }, desc='Roosevelt')  # done loop

#>>> trans(['Pink', 'G', ], '41160', desc="Clinton")
tn.add_station('Pink_41160', {'G_41160':wait_time, 'Pink_40380':2, }, desc='Clinton') # done loop
tn.add_station('G_41160', {'Pink_41160':wait_time, 'G_40380':2, }, desc='Clinton') # done loop

# Harrison Red
tn.add_station('Red_41490', { 'Red_41400':1, 'Red_40560':1 }, desc='Harrison')  # done

# Jackson Red
tn.add_station('Red_40560', { 'Red_41490':1, 'Red_41090':2, 'Brn_40850':10, 'P_40850':10, 'Org_40850':10, 'Pink_40850':10 }, desc='Jackson')  # done

# Monroe Red
tn.add_station('Red_41090', { 'Red_40560':2, 'Red_41660':1 }, desc='Monroe')  # done

# Lake Red
tn.add_station('Red_41660', { 'Red_41090':1, 'Red_40330':1,'Brn_40260':wait_time, 'P_40260':wait_time, 'Org_40260':wait_time, 'G_40260':wait_time, 'Pink_40260':wait_time, }, desc='Lake Red')  # done

# Grand Red
tn.add_station('Red_40330', { 'Red_41660':1, }, desc='Grand Red')  # done loop

tn.shortest_path('Red_40330', 'Red_41400')

##################################################

tn = TransitNetwork()
tn.add_station('red_b', {'red_d':5})
tn.add_station('red_d', {'red_g':1, 'green_d':5, 'brown_d':5})
tn.add_station('red_g', {'red_d':1, 'red_i':1})
tn.add_station('red_i', {'red_k':1, 'blue_i':5})
tn.add_station('red_k', {'red_i':1, 'red_l':3, 'brown_k':5, })
tn.add_station('red_l', {'red_k':3})

tn.add_station('green_c', {'green_e':2})
tn.add_station('green_e', {'green_c':2, 'green_d':1, 'brown_e':5, 'blue_e':5})
tn.add_station('green_d', {'green_e':1, 'red_d':5, 'brown_d':5, 'green_h':3})
tn.add_station('green_h', {'green_d':3, 'green_j':2, 'brown_h':5})
tn.add_station('green_j', {'green_h':2, 'green_m':5, 'brown_j':5})
tn.add_station('green_m', {'green_j':5})

tn.add_station('blue_n', {'blue_e':2})
tn.add_station('blue_e', {'blue_n':2, 'blue_i':2, 'green_e':5, 'brown_e':5})
tn.add_station('blue_i', {'blue_e':2, 'blue_o':5, 'red_i':5})
tn.add_station('blue_o', {'blue_i':5})

tn.add_station('brown_a', {'brown_f':2})
tn.add_station('brown_f', {'brown_k':3})
tn.add_station('brown_k', {'brown_j':2, 'red_k':5})
tn.add_station('brown_j', {'brown_h':2, 'green_j':5})
tn.add_station('brown_h', {'brown_d':3, 'green_h':5})
tn.add_station('brown_d', {'brown_e':1, 'red_d':5, 'green_d':5})
tn.add_station('brown_e', {'brown_a':2, 'blue_e':5, 'green_e':5})

tn.shortest_path('green_c', 'red_k')
tn.get_stops_on_line('green')
tn.get_stops_at_station('e')

tn = TransitNetwork()
tn.add_station('red_a', {'red_b':1, })
tn.add_station('red_b', {'red_a':1, 'red_c':1,} )
tn.add_station('red_c', {'red_b':1, 'red_d':1,} )
tn.add_station('red_d', {'red_c':1, } )
tn.shortest_path('red_a', 'red_d')<|endoftext|>


<commit_before># Copyright (c) 2008 Pavol Rusnak
# see __init__.py for license details

from configparser import SafeConfigParser
from fnmatch import fnmatch
from subprocess import call
import gettext
import os
import re
import sys

import scout

_ = gettext.gettext

try:
    solv = __import__('solv')
except Exception:
    solv = None

class SolvParser(object):

    etcpath = '/etc/zypp/repos.d'
    solvfile = '/var/cache/zypp/solv/%s/solv'
    # path regular expression for binary paths
    pathre = r'^/(bin|sbin|usr/bin|usr/sbin|usr/games|opt/kde3/bin' \
        r'|opt/kde3/sbin|opt/gnome/bin|opt/gnome/sbin)/'

    def __init__(self):
        self.pool = solv.Pool()

        zypper_refresh_failed = False
        for repofile in [f for f in os.listdir(self.etcpath)
                         if fnmatch(f, '*.repo')]:
            try:
                parser = SafeConfigParser()
                parser.read('%s/%s' % (self.etcpath, repofile))
                for name in parser.sections():
                    if parser.get(name, 'enabled') == '1':
                        if not os.path.isfile(
                                self.solvfile % name.replace('/', '_')) \
                           and not zypper_refresh_failed:
                            ret = call('zypper refresh')
                            assert ret, 'zypper refresh failed ' \
                                'with exit code: %s' % ret
                        repo = self.pool.add_repo(name)
                        repo.add_solv(self.solvfile % name)
            except OSError:
                zypper_refresh_failed = True
            except Exception:
                pass

        if not list(self.pool.repos_iter()):
            print(_('\nWarning: no repos found - make sure your '
                    'repositories are configured.'), file=sys.stderr)
        if zypper_refresh_failed:
            print(_('\nWarning: incomplete repos found but could '
                    'not refresh - try to refresh manually, e.g. '
                    "with 'zypper refresh'."), file=sys.stderr)

    def search(self, term, inversesearch=False):
        pkgmatch = []
        if not inversesearch:
            pathreprg = re.compile(self.pathre + re.escape(term) + '$')
            # SEARCH_STRING without SEARCH_FILES matches basenames
            for d in self.pool.Dataiterator(solv.SOLVABLE_FILELIST,
                                            term,
                                            solv.Dataiterator.SEARCH_STRING):
                path = str(d)
                # do matching for path
                if not pathreprg.match(path):
                    continue
                row = ('zypp (%s)' % d.solvable.repo.name,
                       d.solvable.name,
                       path[:-len(term)-1], term)
                if row not in pkgmatch:
                    pkgmatch.append(row)
        else:
            pathreprg = re.compile(self.pathre + '[^/]+$')
            for d in self.pool.Dataiterator(solv.SOLVABLE_NAME,
                                            term,
                                            solv.Dataiterator.SEARCH_STRING):
                for d2 in self.pool.Dataiterator_solvid(
                        d.solvid, solv.SOLVABLE_FILELIST):
                    path = str(d2)
                    if not pathreprg.match(path):
                        continue
                    binary = os.path.basename(path)
                    row = ('zypp (%s)' % d.solvable.repo.name.decode('utf-8'),
                           d.solvable.name.decode('utf-8'),
                           path[:-len(binary)-1], binary)
                    if row not in pkgmatch:
                        pkgmatch.append(row)
        return pkgmatch

class ScoutModule(scout.BaseScoutModule):

    name = 'bin'
    desc = _('Search for binaries contained in the packages.')

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

        if solv is None:
            self._repo_list = scout.RepoList(self._cls.name)
        else:
            self._repo_list = scout.RepoList(self._cls.name, ('zypp', ))
        self._parser = scout.Parser(self._cls.name, self._repo_list.repos)

    def query_zypp(self, term, inversesearch=False):
        if solv is None:
            return None
        s = SolvParser()
        return s.search(term, inversesearch)

    def query_repo(self, repo, term, inversesearch=False):
        db = scout.Database(self._name + '-' + repo)
        if not inversesearch:
            r = db.query(
                'SELECT package, path, binary '
                'FROM binary '
                'LEFT JOIN path ON binary.id_path=path.id_path '
                'LEFT JOIN package ON binary.id_pkg=package.id_pkg '
                'WHERE binary=?', term)
        else:
            r = db.query(
                'SELECT package, path, binary '
                'FROM binary '
                'LEFT JOIN path ON binary.id_path=path.id_path '
                'LEFT JOIN package ON binary.id_pkg=package.id_pkg '
                'WHERE package=?', term)
        if r is None:
            return None
        if isinstance(r, list):
            return [[repo] + list(x) for x in r]
        else:
            return [[repo] + list(r)]

    def main(self, module_args=None):
        args = None
        try:
            args = self._parser.parse_args(module_args)
        except scout.HelpOptionFound:
            self._parser.print_help()
            sys.exit(1)

        if args.listrepo:
            return self.do_repo_list()

        term = args.query

        scout.null_lang.install()
        result_list = [_('repo'), _('pkg'), _('path'), _('bin')]
        result_list2 = [_('repository'), _('package'), _('path'),
                        _('binary')]
        scout.default_lang.install()

        result = scout.Result(result_list, result_list2)

        repos = self._repo_list.repos
        if repos is None:
            return None
        if args.repo:
            repos = (args.repo, )
        for repo in repos:
            if repo == 'zypp':
                result.add_rows(self.query_zypp(term, args.inversesearch))
            else:
                result.add_rows(self.query_repo(repo, term,
                                                args.inversesearch))

        return result
<commit_msg>Fix finding packages from repo with / in name<commit_after># Copyright (c) 2008 Pavol Rusnak
# see __init__.py for license details

from configparser import SafeConfigParser
from fnmatch import fnmatch
from subprocess import call
import gettext
import os
import re
import sys

import scout

_ = gettext.gettext

try:
    solv = __import__('solv')
except Exception:
    solv = None

class SolvParser(object):

    etcpath = '/etc/zypp/repos.d'
    solvfile_pattern = '/var/cache/zypp/solv/%s/solv'
    # path regular expression for binary paths
    pathre = r'^/(bin|sbin|usr/bin|usr/sbin|usr/games|opt/kde3/bin' \
        r'|opt/kde3/sbin|opt/gnome/bin|opt/gnome/sbin)/'

    def __init__(self):
        self.pool = solv.Pool()

        zypper_refresh_failed = False
        for repofile in [f for f in os.listdir(self.etcpath)
                         if fnmatch(f, '*.repo')]:
            try:
                parser = SafeConfigParser()
                parser.read('%s/%s' % (self.etcpath, repofile))
                for name in parser.sections():
                    if parser.get(name, 'enabled') == '1':
                        solvfile = self.solvfile_pattern % \
                                   name.replace('/', '_')
                        if not os.path.isfile(solvfile) \
                           and not zypper_refresh_failed:
                            ret = call('zypper refresh')
                            assert ret, 'zypper refresh failed ' \
                                'with exit code: %s' % ret
                        repo = self.pool.add_repo(name)
                        repo.add_solv(solvfile)
            except OSError:
                zypper_refresh_failed = True
            except Exception:
                pass

        if not list(self.pool.repos_iter()):
            print(_('\nWarning: no repos found - make sure your '
                    'repositories are configured.'), file=sys.stderr)
        if zypper_refresh_failed:
            print(_('\nWarning: incomplete repos found but could '
                    'not refresh - try to refresh manually, e.g. '
                    "with 'zypper refresh'."), file=sys.stderr)

    def search(self, term, inversesearch=False):
        pkgmatch = []
        if not inversesearch:
            pathreprg = re.compile(self.pathre + re.escape(term) + '$')
            # SEARCH_STRING without SEARCH_FILES matches basenames
            for d in self.pool.Dataiterator(solv.SOLVABLE_FILELIST,
                                            term,
                                            solv.Dataiterator.SEARCH_STRING):
                path = str(d)
                # do matching for path
                if not pathreprg.match(path):
                    continue
                row = ('zypp (%s)' % d.solvable.repo.name,
                       d.solvable.name,
                       path[:-len(term)-1], term)
                if row not in pkgmatch:
                    pkgmatch.append(row)
        else:
            pathreprg = re.compile(self.pathre + '[^/]+$')
            for d in self.pool.Dataiterator(solv.SOLVABLE_NAME,
                                            term,
                                            solv.Dataiterator.SEARCH_STRING):
                for d2 in self.pool.Dataiterator_solvid(
                        d.solvid, solv.SOLVABLE_FILELIST):
                    path = str(d2)
                    if not pathreprg.match(path):
                        continue
                    binary = os.path.basename(path)
                    row = ('zypp (%s)' % d.solvable.repo.name.decode('utf-8'),
                           d.solvable.name.decode('utf-8'),
                           path[:-len(binary)-1], binary)
                    if row not in pkgmatch:
                        pkgmatch.append(row)
        return pkgmatch

class ScoutModule(scout.BaseScoutModule):

    name = 'bin'
    desc = _('Search for binaries contained in the packages.')

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

        if solv is None:
            self._repo_list = scout.RepoList(self._cls.name)
        else:
            self._repo_list = scout.RepoList(self._cls.name, ('zypp', ))
        self._parser = scout.Parser(self._cls.name, self._repo_list.repos)

    def query_zypp(self, term, inversesearch=False):
        if solv is None:
            return None
        s = SolvParser()
        return s.search(term, inversesearch)

    def query_repo(self, repo, term, inversesearch=False):
        db = scout.Database(self._name + '-' + repo)
        if not inversesearch:
            r = db.query(
                'SELECT package, path, binary '
                'FROM binary '
                'LEFT JOIN path ON binary.id_path=path.id_path '
                'LEFT JOIN package ON binary.id_pkg=package.id_pkg '
                'WHERE binary=?', term)
        else:
            r = db.query(
                'SELECT package, path, binary '
                'FROM binary '
                'LEFT JOIN path ON binary.id_path=path.id_path '
                'LEFT JOIN package ON binary.id_pkg=package.id_pkg '
                'WHERE package=?', term)
        if r is None:
            return None
        if isinstance(r, list):
            return [[repo] + list(x) for x in r]
        else:
            return [[repo] + list(r)]

    def main(self, module_args=None):
        args = None
        try:
            args = self._parser.parse_args(module_args)
        except scout.HelpOptionFound:
            self._parser.print_help()
            sys.exit(1)

        if args.listrepo:
            return self.do_repo_list()

        term = args.query

        scout.null_lang.install()
        result_list = [_('repo'), _('pkg'), _('path'), _('bin')]
        result_list2 = [_('repository'), _('package'), _('path'),
                        _('binary')]
        scout.default_lang.install()

        result = scout.Result(result_list, result_list2)

        repos = self._repo_list.repos
        if repos is None:
            return None
        if args.repo:
            repos = (args.repo, )
        for repo in repos:
            if repo == 'zypp':
                result.add_rows(self.query_zypp(term, args.inversesearch))
            else:
                result.add_rows(self.query_repo(repo, term,
                                                args.inversesearch))

        return result
<|endoftext|>


<commit_before>import fixture.basic
from model.contact import Contact
__author__ = "Grzegorz Holak"

class ContactHelper(fixture.basic.BasicHelper):

    def __init__(self, app):
        super(ContactHelper, self).__init__(app)

    def create_without_photo(self, contact):
        wd = self.app.wd
        # go to contact creator
        wd.find_element_by_link_text("nowy wpis").click()
        self.fill_contact_form_without_photo(contact)
        # save contact
        wd.find_element_by_name("submit").click()
        # as return to home page
        self.open_home_page()
        self.contact_cache = None

    def fill_contact_form_without_photo(self, contact):
        self.fill_name_info(contact)
        self.fill_company_info(contact)
        self.fill_address(contact)
        self.fill_phone_info(contact)
        self.fill_online_info(contact)
        self.fill_birthday(contact)
        self.fill_anniversary(contact)
        self.select_group()
        self.fill_additional_info(contact)

    def fill_address(self, contact):
        wd = self.app.wd
        self.change_field_value("address", contact.address)

    def fill_company_info(self, contact):
        wd = self.app.wd
        self.change_field_value("company", contact.company)
        self.change_field_value("title", contact.title)

    def fill_name_info(self, contact):
        wd = self.app.wd
        self.change_field_value("firstname", contact.first_name)
        self.change_field_value("middlename", contact.middle_name)
        self.change_field_value("lastname", contact.last_name)
        self.change_field_value("nickname", contact.nick)

    def fill_phone_info(self, contact):
        wd = self.app.wd
        self.change_field_value("home", contact.home_phone)
        self.change_field_value("mobile", contact.mobile_phone)
        self.change_field_value("work", contact.work_phone)
        self.change_field_value("fax", contact.fax)

    def fill_online_info(self, contact):
        wd = self.app.wd
        self.change_field_value("email", contact.email)
        self.change_field_value("email2", contact.email2)
        self.change_field_value("email3", contact.email3)
        self.change_field_value("homepage", contact.homepage)

    def select_group(self):
        # selecting group - commented to leave it as is
        # wd = self.app.wd
        # wd.find_element_by_xpath('//*[@name="new_group"]/option[0]').click()
        pass

    def fill_additional_info(self, contact):
        wd = self.app.wd
        self.change_field_value("address2", contact.address2)
        self.change_field_value("phone2", contact.home_phone2)
        self.change_field_value("notes", contact.notes)

    def fill_anniversary(self, contact):
        wd = self.app.wd
        wd.find_element_by_xpath('//*[@name="aday"]/option[%d]' % contact.ann_day).click()
        wd.find_element_by_xpath('//*[@name="amonth"]/option[%d]' % contact.ann_month).click()
        self.change_field_value("ayear", contact.ann_year)

    def fill_birthday(self, contact):
        wd = self.app.wd
        wd.find_element_by_xpath('//*[@name="bday"]/option[%d]' % contact.birthday_day).click()
        wd.find_element_by_xpath('//*[@name="bmonth"]/option[%d]' % contact.birthday_month).click()
        self.change_field_value("byear", contact.birthday_year)

    def delete_contact_by_index(self, index):
        wd = self.app.wd
        wd.find_elements_by_name("selected[]")[index].click()
        wd.find_element_by_xpath('//*[@value="Usuń"]').click()
        wd.switch_to_alert().accept()
        self.open_home_page()
        self.contact_cache = None

    def delete_first_contact(self):
        self.delete_contact_by_index(0)

    def modify_contact_without_photo_by_index(self, index, contact):
        wd = self.app.wd
        # go to first contact editor
        wd.find_elements_by_xpath('//*[@src="icons/pencil.png"]')[index].click()
        self.fill_contact_form_without_photo(contact)
        wd.find_element_by_name("update").click()
        self.open_home_page()
        self.contact_cache = None

    def modify_first_contact_without_photo(self, contact):
        self.modify_contact_without_photo_by_index(0, contact)

    def count(self):
        wd = self.app.wd
        # just to be sure that we are on home page
        self.open_home_page()
        return len(wd.find_elements_by_name("selected[]"))

    # this function is really strange, I am not sure is it correct way for now.
    # I wanted make for each loop more user friendly - looping by elements of table, but it is not so easy.
    # Xpaths are from this "not perfect" ones, but no alternative.

    contact_cache = None

    def get_contact_list(self):
        if self.contact_cache is None:
            wd = self.app.wd
            self.open_home_page()
            self.contact_cache = []
            i = 2  # because first element is in row nr = 2
            for row in wd.find_elements_by_name("selected[]"):
                # i-th row td 2nd and 3rd
                last_name = wd.find_element_by_xpath("//*[@id='maintable']/tbody/tr[%d]/td[2]" % i).text
                first_name = wd.find_element_by_xpath("//*[@id='maintable']/tbody/tr[%d]/td[3]" % i).text
                contact_id = row.get_attribute("value")
                self.contact_cache.append(Contact(first_name=first_name, last_name=last_name, contact_id=contact_id))
                i += 1  # making iterator +1 for next row in loop
        return list(self.contact_cache)
<commit_msg>change to make get list exactly same as in video course to avoid futures troubles<commit_after>import fixture.basic
from model.contact import Contact
__author__ = "Grzegorz Holak"

class ContactHelper(fixture.basic.BasicHelper):

    def __init__(self, app):
        super(ContactHelper, self).__init__(app)

    def create_without_photo(self, contact):
        wd = self.app.wd
        # go to contact creator
        wd.find_element_by_link_text("nowy wpis").click()
        self.fill_contact_form_without_photo(contact)
        # save contact
        wd.find_element_by_name("submit").click()
        # as return to home page
        self.open_home_page()
        self.contact_cache = None

    def fill_contact_form_without_photo(self, contact):
        self.fill_name_info(contact)
        self.fill_company_info(contact)
        self.fill_address(contact)
        self.fill_phone_info(contact)
        self.fill_online_info(contact)
        self.fill_birthday(contact)
        self.fill_anniversary(contact)
        self.select_group()
        self.fill_additional_info(contact)

    def fill_address(self, contact):
        wd = self.app.wd
        self.change_field_value("address", contact.address)

    def fill_company_info(self, contact):
        wd = self.app.wd
        self.change_field_value("company", contact.company)
        self.change_field_value("title", contact.title)

    def fill_name_info(self, contact):
        wd = self.app.wd
        self.change_field_value("firstname", contact.first_name)
        self.change_field_value("middlename", contact.middle_name)
        self.change_field_value("lastname", contact.last_name)
        self.change_field_value("nickname", contact.nick)

    def fill_phone_info(self, contact):
        wd = self.app.wd
        self.change_field_value("home", contact.home_phone)
        self.change_field_value("mobile", contact.mobile_phone)
        self.change_field_value("work", contact.work_phone)
        self.change_field_value("fax", contact.fax)

    def fill_online_info(self, contact):
        wd = self.app.wd
        self.change_field_value("email", contact.email)
        self.change_field_value("email2", contact.email2)
        self.change_field_value("email3", contact.email3)
        self.change_field_value("homepage", contact.homepage)

    def select_group(self):
        # selecting group - commented to leave it as is
        # wd = self.app.wd
        # wd.find_element_by_xpath('//*[@name="new_group"]/option[0]').click()
        pass

    def fill_additional_info(self, contact):
        wd = self.app.wd
        self.change_field_value("address2", contact.address2)
        self.change_field_value("phone2", contact.home_phone2)
        self.change_field_value("notes", contact.notes)

    def fill_anniversary(self, contact):
        wd = self.app.wd
        wd.find_element_by_xpath('//*[@name="aday"]/option[%d]' % contact.ann_day).click()
        wd.find_element_by_xpath('//*[@name="amonth"]/option[%d]' % contact.ann_month).click()
        self.change_field_value("ayear", contact.ann_year)

    def fill_birthday(self, contact):
        wd = self.app.wd
        wd.find_element_by_xpath('//*[@name="bday"]/option[%d]' % contact.birthday_day).click()
        wd.find_element_by_xpath('//*[@name="bmonth"]/option[%d]' % contact.birthday_month).click()
        self.change_field_value("byear", contact.birthday_year)

    def delete_contact_by_index(self, index):
        wd = self.app.wd
        wd.find_elements_by_name("selected[]")[index].click()
        wd.find_element_by_xpath('//*[@value="Usuń"]').click()
        wd.switch_to_alert().accept()
        self.open_home_page()
        self.contact_cache = None

    def delete_first_contact(self):
        self.delete_contact_by_index(0)

    def modify_contact_without_photo_by_index(self, index, contact):
        wd = self.app.wd
        # go to first contact editor
        wd.find_elements_by_xpath('//*[@src="icons/pencil.png"]')[index].click()
        self.fill_contact_form_without_photo(contact)
        wd.find_element_by_name("update").click()
        self.open_home_page()
        self.contact_cache = None

    def modify_first_contact_without_photo(self, contact):
        self.modify_contact_without_photo_by_index(0, contact)

    def count(self):
        wd = self.app.wd
        # just to be sure that we are on home page
        self.open_home_page()
        return len(wd.find_elements_by_name("selected[]"))

    # this function is really strange, I am not sure is it correct way for now.
    # I wanted make for each loop more user friendly - looping by elements of table, but it is not so easy.
    # Xpaths are from this "not perfect" ones, but no alternative.

    contact_cache = None

    def get_contact_list(self):
        if self.contact_cache is None:
            wd = self.app.wd
            self.open_home_page()
            self.contact_cache = []
            for row in wd.find_elements_by_name("entry"):
                cells = row.find_elements_by_tag_name("td")
                last_name = cells[1].text
                first_name = cells[2].text
                contact_id = cells[0].find_element_by_tag_name("input").get_attribute("value")
                self.contact_cache.append(Contact(first_name=first_name, last_name=last_name, contact_id=contact_id))

        return list(self.contact_cache)
<|endoftext|>