text:
"""EC2 client support."""

from datetime import datetime
from urllib import quote
from base64 import b64encode

from dateutil.parser import parse as parse_timestamp

from txaws import version
from txaws.client.base import BaseClient, BaseQuery, error_wrapper
from txaws.ec2 import model
from txaws.ec2.exception import EC2Error
from txaws.util import iso8601time, XML

__all__ = ["EC2Client", "Query", "Parser"]

def ec2_error_wrapper(error):
    error_wrapper(error, EC2Error)

class EC2Client(BaseClient):
    """A client for EC2."""

    def __init__(self, creds=None, endpoint=None, query_factory=None,
                 parser=None):
        if query_factory is None:
            query_factory = Query
        if parser is None:
            parser = Parser()
        super(EC2Client, self).__init__(creds, endpoint, query_factory, parser)

    def describe_instances(self, *instance_ids):
        """Describe current instances."""
        instances = {}
        for pos, instance_id in enumerate(instance_ids):
            instances["InstanceId.%d" % (pos + 1)] = instance_id
        query = self.query_factory(
            action="DescribeInstances", creds=self.creds,
            endpoint=self.endpoint, other_params=instances)
        d = query.submit()
        return d.addCallback(self.parser.describe_instances)

    def run_instances(self, image_id, min_count, max_count,
        security_groups=None, key_name=None, instance_type=None,
        user_data=None, availability_zone=None, kernel_id=None,
        ramdisk_id=None, subnet_id=None, security_group_ids=None):
        """Run new instances.

        TODO: blockDeviceMapping, monitoring, subnetId
        """
        params = {"ImageId": image_id, "MinCount": str(min_count),
                  "MaxCount": str(max_count)}
        if key_name is not None:
            params["KeyName"] = key_name
        if subnet_id is not None:
            params["SubnetId"] = subnet_id
            if security_group_ids is not None:
                for i, id in enumerate(security_group_ids):
                    params["SecurityGroupId.%d" % (i + 1)] = id
            else:
                msg = "You must specify the security_group_ids with the subnet_id"
                raise ValueError(msg)
        elif security_groups is not None:
            for i, name in enumerate(security_groups):
                params["SecurityGroup.%d" % (i + 1)] = name
        else:
            msg = ("You must specify either the subnet_id and "
                   "security_group_ids or security_groups")
            raise ValueError(msg)
        if user_data is not None:
            params["UserData"] = b64encode(user_data)
        if instance_type is not None:
            params["InstanceType"] = instance_type
        if availability_zone is not None:
            params["Placement.AvailabilityZone"] = availability_zone
        if kernel_id is not None:
            params["KernelId"] = kernel_id
        if ramdisk_id is not None:
            params["RamdiskId"] = ramdisk_id
        query = self.query_factory(
            action="RunInstances", creds=self.creds, endpoint=self.endpoint,
            other_params=params)
        d = query.submit()
        return d.addCallback(self.parser.run_instances)

    def terminate_instances(self, *instance_ids):
        """Terminate some instances.

        @param instance_ids: The ids of the instances to terminate.
        @return: A deferred which on success gives an iterable of
            (id, old-state, new-state) tuples.
        """
        instances = {}
        for pos, instance_id in enumerate(instance_ids):
            instances["InstanceId.%d" % (pos + 1)] = instance_id
        query = self.query_factory(
            action="TerminateInstances", creds=self.creds,
            endpoint=self.endpoint, other_params=instances)
        d = query.submit()
        return d.addCallback(self.parser.terminate_instances)

    def get_console_output(self, instance_id):
        """Get the console output for a single instance."""
        InstanceIDParam = {"InstanceId": instance_id}
        query = self.query_factory(
            action="GetConsoleOutput", creds=self.creds,
            endpoint=self.endpoint, other_params=InstanceIDParam)
        d = query.submit()
        return d.addCallback(self.parser.get_console_output)

    def describe_security_groups(self, *names):
        """Describe security groups.

        @param names: Optionally, a list of security group names to describe.
            Defaults to all security groups in the account.
        @return: A C{Deferred} that will fire with a list of L{SecurityGroup}s
            retrieved from the cloud.
        """
        group_names = {}
        if names:
            group_names = dict([("GroupName.%d" % (i + 1), name)
                                for i, name in enumerate(names)])
        query = self.query_factory(
            action="DescribeSecurityGroups", creds=self.creds,
            endpoint=self.endpoint, other_params=group_names)
        d = query.submit()
        return d.addCallback(self.parser.describe_security_groups)

    def create_security_group(self, name, description, vpc_id=None):
        """Create security group.

        @param name: Name of the new security group.
        @param description: Description of the new security group.
        @param vpc_id: ID of the VPC to which the security group will belong.
        @return: A C{Deferred} that will fire with a truth value for the
            success of the operation.
        """
        parameters = {"GroupName":  name, "GroupDescription": description}
        if vpc_id:
            parameters["VpcId"] = vpc_id
        query = self.query_factory(
            action="CreateSecurityGroup", creds=self.creds,
            endpoint=self.endpoint, other_params=parameters)
        d = query.submit()
        return d.addCallback(self.parser.create_security_group)

    def delete_security_group(self, name=None, id=None):
        """
        @param name: Name of the security group.
        @param id: Id of the security group.
        @return: A C{Deferred} that will fire with a truth value for the
            success of the operation.
        """
        if name:
            parameter = {"GroupName":  name}
        elif id:
            parameter = {"GroupId": id}
        else:
            raise ValueError("You must provide either the security group name or id")
        query = self.query_factory(
            action="DeleteSecurityGroup", creds=self.creds,
            endpoint=self.endpoint, other_params=parameter)
        d = query.submit()
        return d.addCallback(self.parser.truth_return)

    def authorize_security_group(
        self, group_name=None, group_id=None, source_group_name="", source_group_owner_id="",
        ip_protocol="", from_port="", to_port="", cidr_ip=""):
        """
        There are two ways to use C{authorize_security_group}:
            1) associate an existing group (source group) with the one that you
            are targeting (group_name) with an authorization update; or
            2) associate a set of IP permissions with the group you are
            targeting with an authorization update.

        @param group_name: The group you will be modifying with a new
            authorization.
        @param group_id: The id of the group you will be modifying with
            a new authorization.

        Optionally, the following parameters:
        @param source_group_name: Name of security group to authorize access to
            when operating on a user/group pair.
        @param source_group_owner_id: Owner of security group to authorize
            access to when operating on a user/group pair.

        If those parameters are not specified, then the following must be:
        @param ip_protocol: IP protocol to authorize access to when operating
            on a CIDR IP.
        @param from_port: Bottom of port range to authorize access to when
            operating on a CIDR IP. This contains the ICMP type if ICMP is
            being authorized.
        @param to_port: Top of port range to authorize access to when operating
            on a CIDR IP. This contains the ICMP code if ICMP is being
            authorized.
        @param cidr_ip: CIDR IP range to authorize access to when operating on
            a CIDR IP.

        @return: A C{Deferred} that will fire with a truth value for the
            success of the operation.
        """
        if source_group_name and source_group_owner_id:
            parameters = {
                "SourceSecurityGroupName": source_group_name,
                "SourceSecurityGroupOwnerId": source_group_owner_id,
                }
        elif ip_protocol and from_port and to_port and cidr_ip:
            parameters = {
                "IpProtocol": ip_protocol,
                "FromPort": from_port,
                "ToPort": to_port,
                "CidrIp": cidr_ip,
                }
        else:
            msg = ("You must specify either both group parameters or "
                   "all the ip parameters.")
            raise ValueError(msg)
        if group_id:
            parameters["GroupId"] = group_id
        elif group_name:
            parameters["GroupName"] = group_name
        else:
            raise ValueError("You must specify either the group name of the group id.")
        query = self.query_factory(
            action="AuthorizeSecurityGroupIngress", creds=self.creds,
            endpoint=self.endpoint, other_params=parameters)
        d = query.submit()
        return d.addCallback(self.parser.truth_return)

    def authorize_group_permission(
        self, group_name, source_group_name, source_group_owner_id):
        """
        This is a convenience function that wraps the "authorize group"
        functionality of the C{authorize_security_group} method.

        For an explanation of the parameters, see C{authorize_security_group}.
        """
        d = self.authorize_security_group(
            group_name,
            source_group_name=source_group_name,
            source_group_owner_id=source_group_owner_id)
        return d

    def authorize_ip_permission(
        self, group_name, ip_protocol, from_port, to_port, cidr_ip):
        """
        This is a convenience function that wraps the "authorize ip
        permission" functionality of the C{authorize_security_group} method.

        For an explanation of the parameters, see C{authorize_security_group}.
        """
        d = self.authorize_security_group(
            group_name,
            ip_protocol=ip_protocol, from_port=from_port, to_port=to_port,
            cidr_ip=cidr_ip)
        return d

    def revoke_security_group(
        self, group_name=None, group_id=None, source_group_name="", source_group_owner_id="",
        ip_protocol="", from_port="", to_port="", cidr_ip=""):
        """
        There are two ways to use C{revoke_security_group}:
            1) associate an existing group (source group) with the one that you
            are targeting (group_name) with the revoke update; or
            2) associate a set of IP permissions with the group you are
            targeting with a revoke update.

        @param group_name: The group you will be modifying with an
            authorization removal.

        Optionally, the following parameters:
        @param source_group_name: Name of security group to revoke access from
            when operating on a user/group pair.
        @param source_group_owner_id: Owner of security group to revoke
            access from when operating on a user/group pair.

        If those parameters are not specified, then the following must be:
        @param ip_protocol: IP protocol to revoke access from when operating
            on a CIDR IP.
        @param from_port: Bottom of port range to revoke access from when
            operating on a CIDR IP. This contains the ICMP type if ICMP is
            being revoked.
        @param to_port: Top of port range to revoke access from when operating
            on a CIDR IP. This contains the ICMP code if ICMP is being
            revoked.
        @param cidr_ip: CIDR IP range to revoke access from when operating on
            a CIDR IP.

        @return: A C{Deferred} that will fire with a truth value for the
            success of the operation.
        """
        if source_group_name and source_group_owner_id:
            parameters = {
                "SourceSecurityGroupName": source_group_name,
                "SourceSecurityGroupOwnerId": source_group_owner_id,
                }
        elif ip_protocol and from_port and to_port and cidr_ip:
            parameters = {
                "IpProtocol": ip_protocol,
                "FromPort": from_port,
                "ToPort": to_port,
                "CidrIp": cidr_ip,
                }
        else:
            msg = ("You must specify either both group parameters or "
                   "all the ip parameters.")
            raise ValueError(msg)
        if group_id:
            parameters["GroupId"] = group_id
        elif group_name:
            parameters["GroupName"] = group_name
        else:
            raise ValueError("You must specify either the group name of the group id.")
        query = self.query_factory(
            action="RevokeSecurityGroupIngress", creds=self.creds,
            endpoint=self.endpoint, other_params=parameters)
        d = query.submit()
        return d.addCallback(self.parser.truth_return)

    def revoke_group_permission(
        self, group_name, source_group_name, source_group_owner_id):
        """
        This is a convenience function that wraps the "authorize group"
        functionality of the C{authorize_security_group} method.

        For an explanation of the parameters, see C{revoke_security_group}.
        """
        d = self.revoke_security_group(
            group_name,
            source_group_name=source_group_name,
            source_group_owner_id=source_group_owner_id)
        return d

    def revoke_ip_permission(
        self, group_name, ip_protocol, from_port, to_port, cidr_ip):
        """
        This is a convenience function that wraps the "authorize ip
        permission" functionality of the C{authorize_security_group} method.

        For an explanation of the parameters, see C{revoke_security_group}.
        """
        d = self.revoke_security_group(
            group_name,
            ip_protocol=ip_protocol, from_port=from_port, to_port=to_port,
            cidr_ip=cidr_ip)
        return d

    def describe_volumes(self, *volume_ids):
        """Describe available volumes."""
        volumeset = {}
        for pos, volume_id in enumerate(volume_ids):
            volumeset["VolumeId.%d" % (pos + 1)] = volume_id
        query = self.query_factory(
            action="DescribeVolumes", creds=self.creds, endpoint=self.endpoint,
            other_params=volumeset)
        d = query.submit()
        return d.addCallback(self.parser.describe_volumes)

    def create_volume(self, availability_zone, size=None, snapshot_id=None):
        """Create a new volume."""
        params = {"AvailabilityZone": availability_zone}
        if ((snapshot_id is None and size is None) or
            (snapshot_id is not None and size is not None)):
            raise ValueError("Please provide either size or snapshot_id")
        if size is not None:
            params["Size"] = str(size)
        if snapshot_id is not None:
            params["SnapshotId"] = snapshot_id
        query = self.query_factory(
            action="CreateVolume", creds=self.creds, endpoint=self.endpoint,
            other_params=params)
        d = query.submit()
        return d.addCallback(self.parser.create_volume)

    def delete_volume(self, volume_id):
        query = self.query_factory(
            action="DeleteVolume", creds=self.creds, endpoint=self.endpoint,
            other_params={"VolumeId": volume_id})
        d = query.submit()
        return d.addCallback(self.parser.truth_return)

    def describe_snapshots(self, *snapshot_ids):
        """Describe available snapshots.

        TODO: ownerSet, restorableBySet
        """
        snapshot_set = {}
        for pos, snapshot_id in enumerate(snapshot_ids):
            snapshot_set["SnapshotId.%d" % (pos + 1)] = snapshot_id
        query = self.query_factory(
            action="DescribeSnapshots", creds=self.creds,
            endpoint=self.endpoint, other_params=snapshot_set)
        d = query.submit()
        return d.addCallback(self.parser.snapshots)

    def create_snapshot(self, volume_id):
        """Create a new snapshot of an existing volume.

        TODO: description
        """
        query = self.query_factory(
            action="CreateSnapshot", creds=self.creds, endpoint=self.endpoint,
            other_params={"VolumeId": volume_id})
        d = query.submit()
        return d.addCallback(self.parser.create_snapshot)

    def delete_snapshot(self, snapshot_id):
        """Remove a previously created snapshot."""
        query = self.query_factory(
            action="DeleteSnapshot", creds=self.creds, endpoint=self.endpoint,
            other_params={"SnapshotId": snapshot_id})
        d = query.submit()
        return d.addCallback(self.parser.truth_return)

    def attach_volume(self, volume_id, instance_id, device):
        """Attach the given volume to the specified instance at C{device}."""
        query = self.query_factory(
            action="AttachVolume", creds=self.creds, endpoint=self.endpoint,
            other_params={"VolumeId": volume_id, "InstanceId": instance_id,
                          "Device": device})
        d = query.submit()
        return d.addCallback(self.parser.attach_volume)

    def describe_keypairs(self, *keypair_names):
        """Returns information about key pairs available."""
        keypairs = {}
        for index, keypair_name in enumerate(keypair_names):
            keypairs["KeyName.%d" % (index + 1)] = keypair_name
        query = self.query_factory(
            action="DescribeKeyPairs", creds=self.creds,
            endpoint=self.endpoint, other_params=keypairs)
        d = query.submit()
        return d.addCallback(self.parser.describe_keypairs)

    def create_keypair(self, keypair_name):
        """
        Create a new 2048 bit RSA key pair and return a unique ID that can be
        used to reference the created key pair when launching new instances.
        """
        query = self.query_factory(
            action="CreateKeyPair", creds=self.creds, endpoint=self.endpoint,
            other_params={"KeyName": keypair_name})
        d = query.submit()
        return d.addCallback(self.parser.create_keypair)

    def delete_keypair(self, keypair_name):
        """Delete a given keypair."""
        query = self.query_factory(
            action="DeleteKeyPair", creds=self.creds, endpoint=self.endpoint,
            other_params={"KeyName": keypair_name})
        d = query.submit()
        return d.addCallback(self.parser.truth_return)

    def import_keypair(self, keypair_name, key_material):
        """
        Import an existing SSH key into EC2. It supports:
            * OpenSSH public key format (e.g., the format in
              ~/.ssh/authorized_keys)
            * Base64 encoded DER format
            * SSH public key file format as specified in RFC4716

        @param keypair_name: The name of the key to create.
        @param key_material: The material in one of the supported format.

        @return: A L{Deferred} firing with a L{model.Keypair} instance if
            successful.

        TODO: there is no corresponding method in the 2009-11-30 version
             of the ec2 wsdl. Delete this?
        """
        query = self.query_factory(
            action="ImportKeyPair", creds=self.creds, endpoint=self.endpoint,
            other_params={"KeyName": keypair_name,
                          "PublicKeyMaterial": b64encode(key_material)})
        d = query.submit()
        return d.addCallback(self.parser.import_keypair, key_material)

    def allocate_address(self):
        """
        Acquire an elastic IP address to be attached subsequently to EC2
        instances.

        @return: the IP address allocated.
        """
        # XXX remove empty other_params
        query = self.query_factory(
            action="AllocateAddress", creds=self.creds, endpoint=self.endpoint,
            other_params={})
        d = query.submit()
        return d.addCallback(self.parser.allocate_address)

    def release_address(self, address):
        """
        Release a previously allocated address returned by C{allocate_address}.

        @return: C{True} if the operation succeeded.
        """
        query = self.query_factory(
            action="ReleaseAddress", creds=self.creds, endpoint=self.endpoint,
            other_params={"PublicIp": address})
        d = query.submit()
        return d.addCallback(self.parser.truth_return)

    def associate_address(self, instance_id, address):
        """
        Associate an allocated C{address} with the instance identified by
        C{instance_id}.

        @return: C{True} if the operation succeeded.
        """
        query = self.query_factory(
            action="AssociateAddress", creds=self.creds,
            endpoint=self.endpoint,
            other_params={"InstanceId": instance_id, "PublicIp": address})
        d = query.submit()
        return d.addCallback(self.parser.truth_return)

    def disassociate_address(self, address):
        """
        Disassociate an address previously associated with
        C{associate_address}. This is an idempotent operation, so it can be
        called several times without error.
        """
        query = self.query_factory(
            action="DisassociateAddress", creds=self.creds,
            endpoint=self.endpoint, other_params={"PublicIp": address})
        d = query.submit()
        return d.addCallback(self.parser.truth_return)

    def describe_addresses(self, *addresses):
        """
        List the elastic IPs allocated in this account.

        @param addresses: if specified, the addresses to get information about.

        @return: a C{list} of (address, instance_id). If the elastic IP is not
            associated currently, C{instance_id} will be C{None}.
        """
        address_set = {}
        for pos, address in enumerate(addresses):
            address_set["PublicIp.%d" % (pos + 1)] = address
        query = self.query_factory(
            action="DescribeAddresses", creds=self.creds,
            endpoint=self.endpoint, other_params=address_set)
        d = query.submit()
        return d.addCallback(self.parser.describe_addresses)

    def describe_availability_zones(self, names=None):
        zone_names = None
        if names:
            zone_names = dict([("ZoneName.%d" % (i + 1), name)
                                for i, name in enumerate(names)])
        query = self.query_factory(
            action="DescribeAvailabilityZones", creds=self.creds,
            endpoint=self.endpoint, other_params=zone_names)
        d = query.submit()
        return d.addCallback(self.parser.describe_availability_zones)

class Parser(object):
    """A parser for EC2 responses"""

    def instances_set(self, root, reservation):
        """Parse instance data out of an XML payload.

        @param root: The root node of the XML payload.
        @param reservation: The L{Reservation} associated with the instances
            from the response.
        @return: A C{list} of L{Instance}s.
        """
        instances = []
        for instance_data in root.find("instancesSet"):
            instances.append(self.instance(instance_data, reservation))
        return instances

    def instance(self, instance_data, reservation):
        """Parse instance data out of an XML payload.

        @param instance_data: An XML node containing instance data.
        @param reservation: The L{Reservation} associated with the instance.
        @return: An L{Instance}.

        TODO: reason, platform, monitoring, subnetId, vpcId, privateIpAddress,
              ipAddress, stateReason, architecture, rootDeviceName,
              blockDeviceMapping, instanceLifecycle, spotInstanceRequestId.
        """
        for group_data in instance_data.find("groupSet"):
            group_id = group_data.findtext("groupId")
            group_name = group_data.findtext("groupName")
            reservation.groups.append((group_id, group_name))
        instance_id = instance_data.findtext("instanceId")
        instance_state = instance_data.find(
            "instanceState").findtext("name")
        private_dns_name = instance_data.findtext("privateDnsName")
        dns_name = instance_data.findtext("dnsName")
        private_ip_address = instance_data.findtext("privateIpAddress")
        ip_address = instance_data.findtext("ipAddress")
        key_name = instance_data.findtext("keyName")
        ami_launch_index = instance_data.findtext("amiLaunchIndex")
        products = []
        product_codes = instance_data.find("productCodes")
        if product_codes is not None:
            for product_data in instance_data.find("productCodes"):
                products.append(product_data.text)
        instance_type = instance_data.findtext("instanceType")
        launch_time = instance_data.findtext("launchTime")
        placement = instance_data.find("placement").findtext(
            "availabilityZone")
        kernel_id = instance_data.findtext("kernelId")
        ramdisk_id = instance_data.findtext("ramdiskId")
        image_id = instance_data.findtext("imageId")
        instance = model.Instance(
            instance_id, instance_state, instance_type, image_id,
            private_dns_name, dns_name, private_ip_address, ip_address,
            key_name, ami_launch_index, launch_time, placement, products,
            kernel_id, ramdisk_id, reservation=reservation)
        return instance

    def describe_instances(self, xml_bytes):
        """
        Parse the reservations XML payload that is returned from an AWS
        describeInstances API call.

        Instead of returning the reservations as the "top-most" object, we
        return the object that most developers and their code will be
        interested in: the instances. In instances reservation is available on
        the instance object.

        The following instance attributes are optional:
            * ami_launch_index
            * key_name
            * kernel_id
            * product_codes
            * ramdisk_id
            * reason

        @param xml_bytes: raw XML payload from AWS.
        """
        root = XML(xml_bytes)
        results = []
        # May be a more elegant way to do this:
        for reservation_data in root.find("reservationSet"):
            # Create a reservation object with the parsed data.
            reservation = model.Reservation(
                reservation_id=reservation_data.findtext("reservationId"),
                owner_id=reservation_data.findtext("ownerId"))
            # Get the list of instances.
            instances = self.instances_set(
                reservation_data, reservation)
            results.extend(instances)
        return results

    def run_instances(self, xml_bytes):
        """
        Parse the reservations XML payload that is returned from an AWS
        RunInstances API call.

        @param xml_bytes: raw XML bytes with a C{RunInstancesResponse} root
            element.
        """
        root = XML(xml_bytes)
        # Get the security group information.
        groups = []
        for group_data in root.find("groupSet"):
            group_id = group_data.findtext("groupId")
            groups.append(group_id)
        # Create a reservation object with the parsed data.
        reservation = model.Reservation(
            reservation_id=root.findtext("reservationId"),
            owner_id=root.findtext("ownerId"),
            groups=groups)
        # Get the list of instances.
        instances = self.instances_set(root, reservation)
        return instances

    def terminate_instances(self, xml_bytes):
        """Parse the XML returned by the C{TerminateInstances} function.

        @param xml_bytes: XML bytes with a C{TerminateInstancesResponse} root
            element.
        @return: An iterable of C{tuple} of (instanceId, previousState,
            currentState) for the ec2 instances that where terminated.
        """
        root = XML(xml_bytes)
        result = []
        # May be a more elegant way to do this:
        instances = root.find("instancesSet")
        if instances is not None:
            for instance in instances:
                instanceId = instance.findtext("instanceId")
                previousState = instance.find("previousState").findtext(
                    "name")
                currentState = instance.find("currentState").findtext(
                    "name")
                result.append((instanceId, previousState, currentState))
        return result

    def get_console_output(self, xml_bytes):
        root = XML(xml_bytes)
        output_node = root.find("output")
        instance_id = root.find("instanceId").text.decode("ascii").strip()
        timestamp = parse_timestamp(root.find("timestamp").text)
        console_text = output_node.text.decode("base64").decode("utf-8")
        return model.ConsoleOutput(
            instance_id,
            timestamp,
            console_text,
        )

    def describe_security_groups(self, xml_bytes):
        """Parse the XML returned by the C{DescribeSecurityGroups} function.

        @param xml_bytes: XML bytes with a C{DescribeSecurityGroupsResponse}
            root element.
        @return: A list of L{SecurityGroup} instances.
        """
        root = XML(xml_bytes)
        result = []
        for group_info in root.findall("securityGroupInfo/item"):
            id = group_info.findtext("groupId")
            name = group_info.findtext("groupName")
            description = group_info.findtext("groupDescription")
            owner_id = group_info.findtext("ownerId")
            allowed_groups = []
            allowed_ips = []
            ip_permissions = group_info.find("ipPermissions")
            if ip_permissions is None:
                ip_permissions = ()
            for ip_permission in ip_permissions:

                # openstack doesn't handle self authorized groups properly
                # XXX this is an upstream problem and should be addressed there
                # lp bug #829609
                ip_protocol = ip_permission.findtext("ipProtocol")
                from_port = ip_permission.findtext("fromPort")
                to_port = ip_permission.findtext("toPort")

                if from_port:
                    from_port = int(from_port)

                if to_port:
                    to_port = int(to_port)

                for groups in ip_permission.findall("groups/item") or ():
                    user_id = groups.findtext("userId")
                    group_name = groups.findtext("groupName")
                    if user_id and group_name:
                        if (user_id, group_name) not in allowed_groups:
                            allowed_groups.append((user_id, group_name))
                for ip_ranges in ip_permission.findall("ipRanges/item") or ():
                    cidr_ip = ip_ranges.findtext("cidrIp")
                    allowed_ips.append(
                        model.IPPermission(
                            ip_protocol, from_port, to_port, cidr_ip))

            allowed_groups = [model.UserIDGroupPair(user_id, group_name)
                              for user_id, group_name in allowed_groups]

            security_group = model.SecurityGroup(
                id, name, description, owner_id=owner_id,
                groups=allowed_groups, ips=allowed_ips)
            result.append(security_group)
        return result

    def create_security_group(self, xml_bytes):
        root = XML(xml_bytes)
        return root.findtext("groupId")

    def truth_return(self, xml_bytes):
        """Parse the XML for a truth value.

        @param xml_bytes: XML bytes.
        @return: True if the node contains "return" otherwise False.
        """
        root = XML(xml_bytes)
        return root.findtext("return") == "true"

    def describe_volumes(self, xml_bytes):
        """Parse the XML returned by the C{DescribeVolumes} function.

        @param xml_bytes: XML bytes with a C{DescribeVolumesResponse} root
            element.
        @return: A list of L{Volume} instances.

        TODO: attachementSetItemResponseType#deleteOnTermination
        """
        root = XML(xml_bytes)
        result = []
        for volume_data in root.find("volumeSet"):
            volume_id = volume_data.findtext("volumeId")
            size = int(volume_data.findtext("size"))
            snapshot_id = volume_data.findtext("snapshotId")
            availability_zone = volume_data.findtext("availabilityZone")
            status = volume_data.findtext("status")
            create_time = volume_data.findtext("createTime")
            create_time = datetime.strptime(
                create_time[:19], "%Y-%m-%dT%H:%M:%S")
            volume = model.Volume(
                volume_id, size, status, create_time, availability_zone,
                snapshot_id)
            result.append(volume)
            for attachment_data in volume_data.find("attachmentSet"):
                instance_id = attachment_data.findtext("instanceId")
                device = attachment_data.findtext("device")
                status = attachment_data.findtext("status")
                attach_time = attachment_data.findtext("attachTime")
                attach_time = datetime.strptime(
                    attach_time[:19], "%Y-%m-%dT%H:%M:%S")
                attachment = model.Attachment(
                    instance_id, device, status, attach_time)
                volume.attachments.append(attachment)
        return result

    def create_volume(self, xml_bytes):
        """Parse the XML returned by the C{CreateVolume} function.

        @param xml_bytes: XML bytes with a C{CreateVolumeResponse} root
            element.
        @return: The L{Volume} instance created.
        """
        root = XML(xml_bytes)
        volume_id = root.findtext("volumeId")
        size = int(root.findtext("size"))
        snapshot_id = root.findtext("snapshotId")
        availability_zone = root.findtext("availabilityZone")
        status = root.findtext("status")
        create_time = root.findtext("createTime")
        create_time = datetime.strptime(
            create_time[:19], "%Y-%m-%dT%H:%M:%S")
        volume = model.Volume(
            volume_id, size, status, create_time, availability_zone,
            snapshot_id)
        return volume

    def snapshots(self, xml_bytes):
        """Parse the XML returned by the C{DescribeSnapshots} function.

        @param xml_bytes: XML bytes with a C{DescribeSnapshotsResponse} root
            element.
        @return: A list of L{Snapshot} instances.

        TODO: ownersSet, restorableBySet, ownerId, volumeSize, description,
              ownerAlias.
        """
        root = XML(xml_bytes)
        result = []
        for snapshot_data in root.find("snapshotSet"):
            snapshot_id = snapshot_data.findtext("snapshotId")
            volume_id = snapshot_data.findtext("volumeId")
            status = snapshot_data.findtext("status")
            start_time = snapshot_data.findtext("startTime")
            start_time = datetime.strptime(
                start_time[:19], "%Y-%m-%dT%H:%M:%S")
            progress = snapshot_data.findtext("progress")[:-1]
            progress = float(progress or "0") / 100.
            snapshot = model.Snapshot(
                snapshot_id, volume_id, status, start_time, progress)
            result.append(snapshot)
        return result

    def create_snapshot(self, xml_bytes):
        """Parse the XML returned by the C{CreateSnapshot} function.

        @param xml_bytes: XML bytes with a C{CreateSnapshotResponse} root
            element.
        @return: The L{Snapshot} instance created.

        TODO: ownerId, volumeSize, description.
        """
        root = XML(xml_bytes)
        snapshot_id = root.findtext("snapshotId")
        volume_id = root.findtext("volumeId")
        status = root.findtext("status")
        start_time = root.findtext("startTime")
        start_time = datetime.strptime(
            start_time[:19], "%Y-%m-%dT%H:%M:%S")
        progress = root.findtext("progress")[:-1]
        progress = float(progress or "0") / 100.
        return model.Snapshot(
            snapshot_id, volume_id, status, start_time, progress)

    def attach_volume(self, xml_bytes):
        """Parse the XML returned by the C{AttachVolume} function.

        @param xml_bytes: XML bytes with a C{AttachVolumeResponse} root
            element.
        @return: a C{dict} with status and attach_time keys.

        TODO: volumeId, instanceId, device
        """
        root = XML(xml_bytes)
        status = root.findtext("status")
        attach_time = root.findtext("attachTime")
        attach_time = datetime.strptime(
            attach_time[:19], "%Y-%m-%dT%H:%M:%S")
        return {"status": status, "attach_time": attach_time}

    def describe_keypairs(self, xml_bytes):
        """Parse the XML returned by the C{DescribeKeyPairs} function.

        @param xml_bytes: XML bytes with a C{DescribeKeyPairsResponse} root
            element.
        @return: a C{list} of L{Keypair}.
        """
        results = []
        root = XML(xml_bytes)
        keypairs = root.find("keySet")
        if keypairs is None:
            return results
        for keypair_data in keypairs:
            key_name = keypair_data.findtext("keyName")
            key_fingerprint = keypair_data.findtext("keyFingerprint")
            results.append(model.Keypair(key_name, key_fingerprint))
        return results

    def create_keypair(self, xml_bytes):
        """Parse the XML returned by the C{CreateKeyPair} function.

        @param xml_bytes: XML bytes with a C{CreateKeyPairResponse} root
            element.
        @return: The L{Keypair} instance created.
        """
        keypair_data = XML(xml_bytes)
        key_name = keypair_data.findtext("keyName")
        key_fingerprint = keypair_data.findtext("keyFingerprint")
        key_material = keypair_data.findtext("keyMaterial")
        return model.Keypair(key_name, key_fingerprint, key_material)

    def import_keypair(self, xml_bytes, key_material):
        """Extract the key name and the fingerprint from the result.

        TODO: there is no corresponding method in the 2009-11-30 version
             of the ec2 wsdl. Delete this?
        """
        keypair_data = XML(xml_bytes)
        key_name = keypair_data.findtext("keyName")
        key_fingerprint = keypair_data.findtext("keyFingerprint")
        return model.Keypair(key_name, key_fingerprint, key_material)

    def allocate_address(self, xml_bytes):
        """Parse the XML returned by the C{AllocateAddress} function.

        @param xml_bytes: XML bytes with a C{AllocateAddress} root element.
        @return: The public ip address as a string.
        """
        address_data = XML(xml_bytes)
        return address_data.findtext("publicIp")

    def describe_addresses(self, xml_bytes):
        """Parse the XML returned by the C{DescribeAddresses} function.

        @param xml_bytes: XML bytes with a C{DescribeAddressesResponse} root
            element.
        @return: a C{list} of L{tuple} of (publicIp, instancId).
        """
        results = []
        root = XML(xml_bytes)
        for address_data in root.find("addressesSet"):
            address = address_data.findtext("publicIp")
            instance_id = address_data.findtext("instanceId")
            results.append((address, instance_id))
        return results

    def describe_availability_zones(self, xml_bytes):
        """Parse the XML returned by the C{DescribeAvailibilityZones} function.

        @param xml_bytes: XML bytes with a C{DescribeAvailibilityZonesResponse}
            root element.
        @return: a C{list} of L{AvailabilityZone}.

        TODO: regionName, messageSet
        """
        results = []
        root = XML(xml_bytes)
        for zone_data in root.find("availabilityZoneInfo"):
            zone_name = zone_data.findtext("zoneName")
            zone_state = zone_data.findtext("zoneState")
            results.append(model.AvailabilityZone(zone_name, zone_state))
        return results

class Query(BaseQuery):
    """A query that may be submitted to EC2."""

    timeout = 30

    def __init__(self, other_params=None, time_tuple=None, api_version=None,
                 *args, **kwargs):
        """Create a Query to submit to EC2."""
        super(Query, self).__init__(*args, **kwargs)
        # Currently, txAWS only supports version 2009-11-30
        if api_version is None:
            api_version = version.ec2_api
        self.params = {
            "Version": api_version,
            "SignatureVersion": "2",
            "Action": self.action,
            "AWSAccessKeyId": self.creds.access_key,
            }
        if other_params is None or "Expires" not in other_params:
            # Only add a Timestamp parameter, if Expires isn't used,
            # since both can't be used in the same request.
            self.params["Timestamp"] = iso8601time(time_tuple)
        if other_params:
            self.params.update(other_params)
        self.signature = Signature(self.creds, self.endpoint, self.params)

    def sign(self, hash_type="sha256"):
        """Sign this query using its built in credentials.

        @param hash_type: if the SignatureVersion is 2, specify the type of
            hash to use, either "sha1" or "sha256". It defaults to the latter.

        This prepares it to be sent, and should be done as the last step before
        submitting the query. Signing is done automatically - this is a public
        method to facilitate testing.
        """
        version = self.params["SignatureVersion"]
        if version == "2":
            self.params["SignatureMethod"] = "Hmac%s" % hash_type.upper()
        self.params["Signature"] = self.signature.compute()

    def submit(self):
        """Submit this query.

        @return: A deferred from get_page
        """
        self.sign()
        url = self.endpoint.get_uri()
        method = self.endpoint.method
        params = self.signature.get_canonical_query_params()
        headers = {}
        kwargs = {"method": method}
        if method == "POST":
            headers["Content-Type"] = "application/x-www-form-urlencoded"
            kwargs["postdata"] = params
        else:
            url += "?%s" % params
        if self.endpoint.get_host() != self.endpoint.get_canonical_host():
            headers["Host"] = self.endpoint.get_canonical_host()
        if headers:
            kwargs["headers"] = headers
        if self.timeout:
            kwargs["timeout"] = self.timeout
        d = self.get_page(url, **kwargs)
        return d.addErrback(ec2_error_wrapper)

class Signature(object):
    """Compute EC2-compliant signatures for requests.

    @ivar creds: The L{AWSCredentials} to use to compute the signature.
    @ivar endpoint: The {AWSServiceEndpoint} to consider.
    @ivar params: A C{dict} of parameters to consider. They should be byte
        strings, but unicode strings are supported and will be encoded in
        UTF-8.
    @ivar signature_method: The signature method to use.
    @ivar signature_version: The version of the AWS signature system to use.
    """

    def __init__(self, creds, endpoint, params,
                 signature_method=None, signature_version=None):
        """Create a Query to submit to EC2."""
        self.creds = creds
        self.endpoint = endpoint
        self.params = params
        self.signature_method = signature_method
        self.signature_version = signature_version

    def compute(self):
        """Compute and return the signature according to the given data."""
        if "Signature" in self.params:
            raise RuntimeError("Existing signature in parameters")
        if self.signature_version is not None:
            version = self.signature_version
        else:
            version = self.params["SignatureVersion"]
        if str(version) == "1":
            bytes = self.old_signing_text()
            hash_type = "sha1"
        elif str(version) == "2":
            bytes = self.signing_text()
            if self.signature_method is not None:
                signature_method = self.signature_method
            else:
                signature_method = self.params["SignatureMethod"]
            hash_type = signature_method[len("Hmac"):].lower()
        else:
            raise RuntimeError("Unsupported SignatureVersion: '%s'" % version)
        return self.creds.sign(bytes, hash_type)

    def old_signing_text(self):
        """Return the text needed for signing using SignatureVersion 1."""
        result = []
        lower_cmp = lambda x, y: cmp(x[0].lower(), y[0].lower())
        for key, value in sorted(self.params.items(), cmp=lower_cmp):
            result.append("%s%s" % (key, value))
        return "".join(result)

    def signing_text(self):
        """Return the text to be signed when signing the query."""
        result = "%s\n%s\n%s\n%s" % (self.endpoint.method,
                                     self.endpoint.get_canonical_host(),
                                     self.endpoint.path,
                                     self.get_canonical_query_params())
        return result

    def get_canonical_query_params(self):
        """Return the canonical query params (used in signing)."""
        result = []
        for key, value in self.sorted_params():
            result.append("%s=%s" % (self.encode(key), self.encode(value)))
        return "&".join(result)

    def encode(self, string):
        """Encode a_string as per the canonicalisation encoding rules.

        See the AWS dev reference page 186 (2009-11-30 version).
        @return: a_string encoded.
        """
        if isinstance(string, unicode):
            string = string.encode("utf-8")
        return quote(string, safe="~")

    def sorted_params(self):
        """Return the query parameters sorted appropriately for signing."""
        return sorted(self.params.items())

redpajama_set_name: RedPajamaGithub


text:
The Inaugural Edition Of The Philadelphia Fine Art Fair Runs April 4 To 7, 2019
The inaugural edition of the Philadelphia Fine Art Fair takes place from April 4 to 7, 2019 at the 23rd Street City Troop Armory. The show will include 35 galleries and over 300 artists. The show will feature both Modern and contemporary artists with an emphasis on the City of Brotherly love.
Among the galleries participating are Louis K. Meisel Gallery, The Hunt Gallery, Galerie Isabelle Lesmeister, Cernuda Arte, Arcadia Contemporary, Bridgette Mayer Gallery and Birch Contemporary (to name a few).
The Armory, erected in 1901, houses the First Troop Philadelphia City Cavalry. The show is produced by Show Hamptons. The fair's executive director is Rick Friedman.
Hermann Nitsch Solo Exhibition At Marc Strauss (New York) September 9 To October 15, 2017
Hermann Nitsch solo exhibition at Marc Strauss (299 Grand Street New York) September 9 To October 15, 2017. There is an opening reception 6 to 8 p.m. Saturday, September 9. This is the artist's second solo exhibition by the Viennese artist.
Nitsch, born in Vienna, is a co-founder of the Viennese Actionist Movement and creator of Das Origien Mysterien Theater. His work combines theater, drawing, painting, ,music, printmaking and philosophy in the work. He has been performing "painting actions" since 1960. These pieces are part of his 75tj action. He lives and works at Prinzendorf Castle in Lower Austria.
NADA New York Has A New Location, Skylight Clarkson Square, For 2018
NADA New York has a new location, Skylight Clarkson Square (550 Washington Street), for 2018. The fair takes place March 8-11. The fair's mission to support and cultivate new voices in contemporary art now has a 60,000 square foot venue to pursue that goal. The area has three separate studios.
This is the seventh edition of NADA New York.
Monica Jahan Bose's Debut In Philadelphia, "Rising" Takes Place Sunday October 25, 2015 At Drexel University
Monica Jahan Bose's artistic debut in Philadelphia takes place beginning 5:30 p.m. Sunday, October 25, 2015 at the Urban Center Annex to the Westphal College of Media Arts and Design at Drexel University, 3401 Filbert St, Philadelphia.
The piece, Rising, is curated by Twelve Gates Gallery. The piece is about climate change and sea level rise. Bose will be assisted by Shaam-e-Ali Nayeem. The piece includes a 216 foot sari, water and video projections. The saris, and video, used are from the ongoing Storytelling With Saris, a long term collaboration with twelve woman from coastal Bangladesh who have been displaced by climate change.
Other artists will be presented including Shahzia Sikander, Sumit Baudh and Qasim Riza Shaheen.
Art Miami New York's Premiere Show Takes Place May 14 To 17, 2015 At Pier 94
Art Miami New York's debut show runs from May 14 to 17, 2015 at Pier 94.
The show will feature 85 top international contemporary galleries. Their fair during Art Week Miami is always a highlight and this will feature similar sorts of art--whether you are a serious collector or someone who just wants to see the best in contemporary art? This is an event worth attending.
There will be a VIP preview from 2 to 5 p.m.Thursday May 14 (which also offers access to Frieze). This will be for VIP cardholders and press.
Regular hours--
Thursday May 14, 5-9pm
Friday May 15, 12-8pm
Saturday May 16,12-8pm
Sunday May 17 12-6pm
The Chicago Artist's Coalition Announces Galleries For Edition Chicago 2014 (September 19-21, 2014)
The Chicago Artist's Coalition's Edition Chicago have announced the galleries for the upcoming event (September 19-21, 2014). Edition Chicago takes place at the same time as Expo Chicago, the city's exposition of contemporary and modern art. As I have mentioned before this holding of multiple events simultaneously makes Chicago more of an art destination.
Galleries at this year's Edition include; Chicago Artists Coalition (Chicago), Gallery 19 (Chicago), Steven Harvey (New York City), Museum of Contemporary Photography (Chicago), Ochi Shop (Ketchum, Idaho), David Peterson Gallery (Minneapolis), The Pitch Project (Milwaukee), Andrew Rafacz Gallery (Chicago), Segura Arts Studio (South Bend, Indiana),THE MISSION (Chicago), Vertical Gallery (Chicago) and Yaupetec (Mexico City).
For more information head to chicagoartistscoalition.org.
One Of A Kind Show And Sale (Chicago) From December 4 To 7, 2014
One Of A Kind Show and Sale, once again, comes to Chicago. The event takes place December 4 to 7 at Merchandise Mart (222 Merchandise Mart Plaza 7th fl.). Hours are 11 a.m to 8 p.m. December 4 and 5 and 10 a.m. to 7 p.m. December 6 and 10 a.m. to 5 p.m. December 7.
The show consists of works from artists from all over North America. This is a great place to get a truly unique, special holiday gift. All the work is handmade and encompasses numerous types of art and artists. There is a juried part of the show featuring fine artists as well as a group of 40 artists who primarily sell via Etsy.
Tickets to the show are $12 with kids under 12 free. A portion of ticket sales proceeds benefit the Ann & Robert Lurie Children's Hospital.
Aqua Miami, Now Presented By Art Miami, Offer A A Mixing Of Fine Art And Entertainment At Aqua Hotel In Miami Beach
Aqua Miami--now presented by Art Miami--takes place at the Aqua Hotel, 1530 Collins Ave., Miami Beach.
Aqua Miami has been around since 2005 at the hotel of the same name. This year 47 different exhibitors will be on hand. Innovative galleries and multimedia programs join hands here to bridge the gap between fine art and entertainment via music meet art installations.
One of these, Sound Vision, presented by NYC's Lyons Wier Gallery and Audiophile plus is a curated program of recording artists and DJs juxtaposed against artists shown by Lyons Wier.
And that is just one example of the art and events that await at Aqua. Find out more at WWW.AQUAARTMIAMI.COM.
Minako Abe Paintings Shown By Base Gallery (Tokyo) At Expo Chicago 2013
Scene No.40
photos by Boyd Ogle II
Minako Abe's stylized landscapes seem to warp in the frame as you look at them. The images bend, they move and they even confuse viewers. One woman looking from a distance thought the images were digital and doctored.
They are, in fact, oil on canvas.
The pieces were shown by Base Gallery (Tokyo).
Abe is a graduade of Tokyo Zokei University's Painting Department. He has regularly exhibited in his home country and also in Vienna. The large tryptych sold at Expo Chicago.
Scene No. 22
Locks Gallery (Philadelphia) Showed These Lovely Paintings By Ena Swansea At Expo Chicago 2013
21st Century Haystack 3 by Ena Swansea
Art shows like Expo Chicago are a great place to find new artists--or new to YOU artists. For me, this year I found Ena Swansea, shown by Locks Gallery (Philadelphia).
Here 21st Century Haystack 3 combines realism with...well...unrealism (is that a word?). There is movement, there is depth and there is a "feeling of a place" you often only find in a photograph. She is a wonderful artist.
Below is another of her pieces, 14th St. Pile Field 4. I am not sure how I was unaware of this artist until now.
14th St. Pile Field 4 by Ena Swansea

redpajama_set_name: RedPajamaCommonCrawl


text:
>Front Page>Articles>Feature> August 13, 2020
Top Stories for August 13, 2020
» Free Orchestra Concert Aug. 20
» Orton steps down as Cowboy coach
» Valley Buffalos win annual Jeff Wood Classic
Orton steps down as Cowboy coach
by Dennis Brunner
Bucky Orton with school board member Lex Chamberlain holding the 2005 1A State Championship trophy.
Although he says he will never stop coaching, Kanab High School football head coach Bucky Orton retired from the position he's held for the last 17 years. Orton said, "It's what's best for the program, and my family needs me right now."
He has four children and he said he only got to watch two of his daughter's volleyball games last year. This year he intends to see them all.
Orton and his wife Katie have four kids – Dawson, who is in Ventura, California, serving an LDS mission, Maddie, a sophomore, eight grader Wyatt and Brennan, who will be in the fifth grade.
Orton, a physical education teacher at Kanab High, also has a sprinkler business, "Buck the System," that he operates during the summer installing and repairing sprinklers.
He said he had the chance to spend a lot of time with his family this summer camping, fishing, four-wheeling and really had a lot of fun.
Not that he won't coach again. He said he will always coach … until the day he dies … but now he will help coach his own boys while he still has the opportunity.
Orton said he got into sports watching his two older brothers, Wade and Allen, play ball. Both were great athletes at Kanab High School and went on to play college football at Weber State University.
Bucky was a natural, a three-sport athlete at Kanab High. He excelled in both baseball and basketball, but football was where his heart was.
In his senior year, he led the Cowboys to the 2A state title in an exciting 9-6 victory over San Juan at Rice Eccles Stadium in Salt Lake City. He scored the only Kanab touchdown of the game on a 13 yard scramble. Orton intercepted a San Juan pass in the end zone in the final seconds of the game to preserve the three-point Cowboy victory. He was the team's leader at quarterback and safety ... and the game's MVP.
This led to a college career, where he shifted to playing tight end. He played at Dixie College, when it was a two-year college, and moved on to play and graduate from Utah State University.
His playing career over, he naturally started coaching to stay involved in the game he loved. It has been in his blood his whole life.
After one-year assistant coaching stints at Kanab, Altamont and Parowan high schools, he got his dream job, the Kanab Cowboy head coach, in 2003. After 17 years, including six championship game appearances, with two 1A championship titles, he hung up his whistle.
When asked about some of the highlights he remembers while coaching, Orton said he had plenty. His first year at Kanab was special when he led them to the 2A state semifinal game against Juan Diego. Orton said he will always remember when his Cowboys scored four special teams touchdowns against the private school. Even though they lost that game, he had fond memories of his initial season.
Another highlight he relished was coaching his nephews, Wade's sons Evan and Justin, and Allen's son Sam. He said that was very special.
And of course, he will always remember the two state titles that Kanab won under his leadership.
A quote after the 2005 1A State Championship win over Enterprise from coach Orton, "I'm so proud of these guys. It feels even better to win as a coach than it did as a player, just because I know how much it means to these players."
And one from his 2007 champions over Rich,"This championship means a lot to the whole team, especially my seniors," said Orton, after getting his second championship in three years.
Even one title game that the Cowboys didn't win, Orton was especially proud of. After taking the lead late in the game, the Cowboys lost in the last minute to a dominant Diamond Ranch Academy team. I saw that game and think it was Orton's best coaching job of his career. The Cowboys should have never been in the game, but they almost pulled off a huge upset.
When asked about some of the low times or heartbreaking losses, he said there were a few, especially the four 1A state title games that got away. The loss to Duchesne was difficult for Orton, who thought he was out-coached in that game.
Orton said he will really miss the bond he shared with the players and coaches he worked with. He loved every player he coached, especially those who needed a little extra help from him. He loved the kids and he loves Kanab football.


Please note: This line is for full blog design clients ONLY. Print work and blog add-ons/web graphics are handled individually and separately from this line.
Interested in a new blog design? Request a quote!
Please contact me at veronica@vkdesigncompany.com with any questions.


text:
Love Island's Samira Mighty "really nervous" about meeting Frankie
The dancer decided to leave the villa after Frankie was eliminated from the competition in Monday's episode
Former Love Island contestant Samira Mighty has revealed she is excited and "really nervous" about meeting up with former partner Frankie Foster after she decided to leave the ITV2 show in order to be with him.
The dancer decided to leave the villa after Frankie was eliminated from the competition in Monday's episode.
During Thursday's episode she told her fellow islanders that she had not felt a connection with any of the boys since Frankie's exit and she was missing him.
She explained: "I woke up this morning and I thought, it's my time to go. I have to do this for me. I've never felt so strong."
Following her departure Samira stuck by her decision and admitted she was hopeful she and Frankie were able to build on the relationship they began on the show.
Samira chats to Dani.
Samira Mighty has left the Love Island villa
She said: "I feel very confident and very proud of myself.
"I'm obviously devastated to be leaving my best friends but this is the perfect time.
"I've found what I wanted to find and it was Frankie so I'm going to go and get him.
"I do see a future with Frankie, I've never met anyone like him before.
"He is gorgeous and we did have a genuine connection.
"I am really excited and also really nervous.
"All we know is our villa relationship so I'm really excited to see how it'll work on the outside world and if we are compatible and do like each other as much as we said.
"I'm a bit nervous but mostly excited.
"Just before he left he said 'see you in three weeks, see you soon' and he said to a few of the boys that he was going to wait for me so it feels positive at the moment and good."
TV tales
Sextortion documentary
Samira leaves Love Island
BBC's top 12 earners
Cheapest football deals
Search for jobs, motors and property, or place an advert or family notice here
Coventry's Covid infection rate drops below 500 for first time since start of January
Coventry NewsThe rate crept above 500 on January 3


Decision making about the future of work now demands knowledge rooted in real world experience. Knowledge that addresses the impact of new technology on all stakeholders, presented without fear or favour so that we can all understand and prepare for change.
A selection of reports published by KCP partners in collaboration with academic and corporate partners on aspects of enterprise transformation and performance.
Thoughts from KCP partners on issues of the day where our knowledge and experience and the case study information we have gathered might be pertinent.


If you live in the United States, in a member state of the EU or in New Zealand, many of Weta's products can be shipped from a warehouse in your customs zone.
However, if you live in other countries it will be an international delivery subject to customs clearance.
Items shipped from a warehouse outside your country may be subject to import duties, taxes and brokerage fees, which are levied once a shipment reaches your country. Unfortunately, Weta has no control over such fees, nor can we be held responsible for them.
The same goes for the time it may take for shipments to go through the customs procedure.
Customs policies and processes vary widely from country to country and you should contact your local customs office for further information. You must also make sure that you comply with any other laws that may apply when importing something to your country.
You can see in your shopping cart before you place your order from which warehouse each product will be shipped.
Swiss Federal Customs' recently introduced rules about importation of precious metals mean we cannot guarantee delivery to Swiss addresses of products made from Gold, Sterling Silver or gold plated Tungsten Carbide.
By ordering, you accept responsibility not only for importation taxes and duties but also for non-delivery, return costs and potential confiscation by Swiss Federal Customs. This is all outside Weta Workshop's control and we regret having to make these adjustments to our sales terms. We recommend you consider using a shipping address outside Switzerland.


We are pleased to invite you to apply to the 2013 STARTALK Summer Institute for Teachers of Russian, which will take place at Middlebury College's Katheryn Wasserman Davis School of Russian in July 2013. Funded by STARTALK, a component program of the National Security Language Initiative (NSLI), the program is designed for current and future teachers of Russian at the high school and university level who are looking to develop and expand their teaching strategies and understanding of second language acquisition.
Participants will spend 16 days in Middlebury's immersion program taking part in seminars and hands-on workshops, observing intensive language classes at various levels, visiting with guest speakers and developing a teaching portfolio with peer-reviewed teaching materials, classroom activities and effective assessment practices. In addition, participants will be part of the Russian School intensive language program, which requires that only the target language be spoken, and will have the opportunity to formally and informally meet with Russian School instructors and students of all levels.
By the end of the program, participants will have learned and practiced second language acquisition theory, approaches to teaching Russian in a specifically communicative, learner-focused way and, material design, lesson planning, and testing strategies.
Lodging expenses, textbooks, and classroom materials will be paid by grant funding, and each participant will receive a travel reimbursement for up to $250. Participants will receive one unit of graduate credit.
To apply, please go to http://www.middlebury.edu/ls/russian/startalkrussian and download the application (in the right hand column) and email toedengub@middlebury.edu by May 1st with the subject line "Startalk Application." Graduate students are especially encouraged to apply.
If you have questions, please visit our website at http://www.middlebury.edu/ls/russian/startalkrussian or contact Program Director Dr. Evgeny Dengub atedengub@middlebury.edu.


We are an executive residence in pattaya providing short and long term room rent. Our pattaya apartments can be rented daily or monthly.
environment.So you can relax, recharge and function at peak performance as you would in your own home.
(c) 2006 Luxe Residence and its suppliers. All rights reserved.


After the latest airbag recall involving 34 million vehicles, we might be forgiven for thinking that the Takata Corporation has always only been in the airbag business. Not so. It was founded in 1933 in Japan and it at first made lifelines for parachutes. After World War II, the company started to produce seatbelts. It was the company that staged the very first public seat belt test crashes with dummies.
Then in the 1980s, the company decided to jump into the growing airbag business. Airbags had become optional equipment on some cars, and would soon become standard equipment on virtually every car and truck made.
Takata made billions of dollars off of being one of three airbag producers in the world, but there have been several recalls in recent years. Some of their airbags have injured and even killed car occupants.
As it turns out, a properly functioning airbag is not just reliant on the quality of the airbag material: The pyrotechnics that release the airbag are extremely critical.
The company has had problems with its airbags being released too quickly, leading to head trauma, facial injuries and even death in some cases. There are at least four cases of Takata airbags killing car occupants because the bags are deployed too quickly by the propellants.
Experts say that the company has struggled with quality control problems on its airbags and with the ammonium nitrate propellants used in airbag deployment.
After the latest recall, the company finally admitted that it does have a quality problem and is working to solve their airbag problems.
Thousands of people are hurt by faulty products every year. If you have ever been injured by a product that has been recalled, you should talk to a personal injury attorney to see what your rights are. Large corporations have strong teams of lawyers and insurance companies that are dedicated to paying out as little as possible to people who have been injured by the product.
We hope that Takata is finally taking its airbag problems more seriously and will improve quality so that no more needless injuries and deaths occur.


Easy accessible to Cleveland, Akron and Youngstown markets.
Click herefor a map of the Existing Land Use.
Currently, there are three major industrial parks available for new construction in the Village.
Click on property images to enlarge.
Glenwillow has many existing industrial facilities of all sizes. These facilities are available for re-development and may be eligible for tax incentives as well. Many of these facilities are located on Bond Street, a street with recent improvements to handle truck traffic of all types. All utilities are also available in this area.
For more information on these properties, please contact the developers directly or Mayor Cegelka at 440-232-8788. Mayor Cegelka will be happy to assemble his Economic Development Team to offer your company the best incentive package available.
Click above image to enlarge.


text:
Posts Tagged "Haas F1 Team"
Haas F1 Team Tag
Posted On June 23, 2019By Luis TorresIn Breaking News, Formula One, Headline News, Opinion
By Luis Torres, Staff Writer One third of the Formula One season down, and it's been the Mercedes show in all the first seven races, but it doesn't mean there's been a lack of headlines. Such as Ferrari being the only team that's tried contesting with them, but they've either choked during the race or the FIA Race Stewards reprimanded them out of a grand prix victory. Even the battle for "Best of the Rest" has seen some twists and turns people didn't expect. Although it may be a Silver ArrowRead More
Posted On May 16, 2019By Joey BarnesIn Breaking News, Formula One, Headline News
WATCH: Haas F1 Discusses All-American Approach
By Joey Barnes, Editor-in-Chief It is hard to find a better start for a team in the Formula One history books than Haas F1. During their tenure, they have consistently found themselves battling for points and continue to strive for the next step of wins and podiums. Guenther Steiner, team principal of Haas F1, was joined by drivers Romain Grosjean and Kevin Magnussen to discus the inner workings and how the passion of their boss, who also owns one of the most successful teams in the Monster Energy NASCAR Cup Series.Read More
Posted On February 19, 2019By Motorsports TribuneIn Breaking News, Formula One, Headline News
Leclerc Keeps Ferrari on Top as Gasly Crashes Out
By FIA After Sebastian Vettel topped the first day of pre-season F1 testing yesterday, new Ferrari recruit Charles Leclerc kept the Italian team at the top of the timesheets, finishing three tenths of a second clear of McLaren rookie Lando Norris. The session came to an early end for Pierre Gasly, however, when the new Red Bull Racing driver lost control of his RB15 and spun into the barriers. The French driver had spend most of the afternoon on short, data-gathering runs but as the final hour of the session nearedRead More
Posted On November 26, 2018By Luis TorresIn Breaking News, Formula One, Headline News, Opinion
Heptagon Viewpoints: Seven Takeaways from the Final Third of the F1 Season
By Luis Torres, Staff Writer The 69th season of Formula One has officially concluded, and Mercedes continued to steamroll the competition in the Hybrid Era. Lewis Hamilton once again proved to have risen on the occasion as Sebastian Vettel faded away due to Ferrari's costly errors that eliminated him from the championship race. Not only that, the 2019 season is starting to look clear with key names having no choice but to become a test driver or find different ventures in favor of even younger drivers and even the comeback for theRead More
Posted On November 24, 2018By Motorsports TribuneIn Breaking News, Formula One, Headline News
Hamilton Soars to Pole for Abu Dhabi Grand Prix
By FIA Lewis Hamilton took his 11th pole position of the 2018 FIA Formula 1 World Championship as he headed a Mercedes 1-2 i in qualifying for the final race of the season at Abu Dhabi's Yas Marina Circuit. Hamilton edged team-mate Valtteri Bottas by 0.162s in the final runs of Q3, with the Finn only losing out to the five-time champion due to a small error in the final sector of the 5.554km circuit. Ferrari's Sebastian Vettel will start from third place on the grid, ahead of team-mate Kimi Räikkönen,Read More
Posted On November 9, 2018By Joey BarnesIn Breaking News, Formula One, Headline News
Haas F1 Team Confirms Pietro Fittipaldi as Test Driver for 2019
By Joey Barnes, Editor-in-Chief Pietro Fittipaldi has been confirmed as the official test driver for Haas F1 Team for the 2019 Formula One season. He will also get behind the wheel of a Formula One car for the first time later this month when he takes part in the Pirelli tire test on November 27 at Yas Marina Circuit in Abu Dhabi. The 22-year-old Miami-born Brazilian is a grandson of the two-time Formula One champion / two-time Indianapolis 500 winner, Emerson Fittipaldi. "I'm pleased to welcome Pietro Fittipaldi to HaasRead More
Posted On November 1, 2018By Luis TorresIn Breaking News, Formula One, Headline News
Louis Deletraz to Test for Haas F1 at Pirelli Tire Test in Abu Dhabi
By Luis Torres, Staff Writer Formula 2 driver Louis Deletraz of Switzerland will get the opportunity of a lifetime, test driving for the Haas F1 Team in a two-day Pirelli Tire Test at the Yas Marina Circuit in Abu Dhabi Nov. 27-28. Supported by the ADSS Group, the test will be held two days after the Formula One World Championship finale, and will also serve as a Haas F1's sponsor that weekend. In order to get used to the team personnel and operations, the son of three-time F1 race starter Jean-DenisRead More
Posted On October 25, 2018By Joey BarnesIn Breaking News, Formula One, Headline News
Haas F1 Team Announce Rich Energy as 2019 Title Partner
By Joey Barnes, Editor-in-Chief The Haas Formula One team has confirmed that Rich Energy will become the official title sponsor beginning in 2019. The multi-year deal will see the team become Rich Energy Haas F1 Team and will undergo a significant livery change that share the black and gold colors of the British energy drink company. "Securing a title partner in Rich Energy is another milestone moment in the development of Haas F1 Team," Haas team principal Guenther Steiner said. "We welcome their commitment to us as an organization, andRead More
Grosjean Receives Mexican GP Grid Penalty
By Joey Barnes, Editor-in-Chief AUSTIN, Texas — The 2018 United States Grand Prix will be one to forget for the Haas F1 Team after Romain Grosjean suffered an early retirement and was given a grid penalty for the upcoming Formula One race in Mexico. While running through Turn 12 on the opening lap of the race, Grosjean, locked up briefly and made contact with Charles Leclerc, which sent the Alfa Romeo Sauber into a spin and ultimately, retirement after 31 laps due to the damage. For Grosjean, who started eighth onRead More
Magnussen, Ocon Disqualified from U.S. GP
By Joey Barnes, Editor-in-Chief AUSTIN, Texas — Kevin Magnussen and Esteban Ocon have been disqualified from the United States Grand Prix due to separate violations of the Formula One fuel regulations. The result cost both drivers valuable points as Ocon and Magnussen provisionally finished the 56-lap race eighth and ninth, respectively. However, the No. 31 Racing Point Force India of Ocon was in breach of the fuel-flow limit, which is set to 100 kg/hr on the opening lap. Team boss Otmar Szafnauer gave his take over the subject. "What happened was,Read More


Travel time around 8 – 10 Hrs.
Bekal Tal 9,875 ft to Brahmatal lake 10,800 ft.
1. Stay: Stay at Kathgodam on the last day of the trek is not included in the trek fee.
2. Transport: Transport from Kathgodam to Lohajung and return from can be arranged by us at an extra cost. Transport is arranged in Sumo kind of vehicles. A vehicle carries 5-7 people. Sometimes the number of people in a vehicle can be even 4 or 8. Participants are expected to share the cost likewise. The amount is to be paid directly to our transporter. No service tax is applicable on transport cost. Roads & Journeys only arranges the vehicle pick up and is not responsible for any issues during transport.
3. Pick-up: Participants are picked up from the Kathgodam Railway Station. Pickups from the station start from 6.00 am. Most participants opt for the Roads & Journeys pick up and we assume the same in your case. In case you do not want the Roads & Journeys pick up you will have to make your own travel arrangements.
Read Essentials to know more details.
"Had the time of my life. It is kinda addictive. I have to go for one treak every year now! Go for it without a second thought. what are you waiting for?"


What goals do you have for the upcoming new year?
If you have a fitness goal you would like to share with me or one of the trainers, please do so by emailing us.
a giant fitness dream: perhaps we can configure a way to break it down into a few smaller steps.


text:
The `info` command is used to obtain information about a particular package.

## Usage

```sh
etlas info [FLAGS] PACKAGES`
```

- `[FLAGS]` can be any of the flags listed below.
- `PACKAGES` can be a list of search terms for the packages

## Etlas Info Flags

| Flags                        | Flag Type            | Description                                                             |
| ---------------------------- | :------------------: | ----------------------------------------------------                    |
| `-h`, `--help`               | `switch-flag`        | Show the help text                                                      |
| `-v`, `--verbose`            | `verbosity`          | Control verbosity, higher verbosity emits more messages                 |
| `--package-db`               | `package-db`         | Append the given package database to the list of package databases used |


text:
Often asked: Attack On Titan Manga Ending?
If you have read the manga, then you know Attack on Titan ends with Eren Yeager's death. His end comes around after he led a rogue army against the whole world. "After it all ends, Mikasa returns to her hometown and embraces her feelings for Eren who was sacrificed, and Armin decides to have a talk with the others."
1 Has manga Attack on Titan ended?
2 Will isayama change the ending?
3 Who does Mikasa marry?
4 Why did Eren laugh when Sasha died?
5 Why AOT ending is bad?
6 Will AOT have a Season 5?
7 Why did AOT go to Mappa?
8 Who is Eren's wife?
9 Who is Eren's girlfriend?
10 Who killed Eren?
11 Is Eren the final villain?
12 What was Sasha's last words AOT?
13 Why did Mikasa cut her hair?
Has manga Attack on Titan ended?
Manga. Attack on Titan is written and illustrated by Hajime Isayama. The manga was finished after an eleven-year publication run with the release of its 139th chapter on April 9, 2021.
Will isayama change the ending?
The Attack on Titan manga written by Hajime Isayama had already reached its end after 11 years last April 9, 2021. This criticism even led to Isayama tweaking the ending himself by adding 8 extra pages in the final volume, which left the possibility of the manga having a sequel.
Who does Mikasa marry?
Eventually, Mikasa and Jean end up together, alone. They start talking. It's been 2 years since RtS and so many things have happened. Jean talks about how important it is to keep Eren alive.
You might be interested: Readers ask: Sell Scrap Metal In Victoria Australia?
Why did Eren laugh when Sasha died?
The first one is that Eren laughs at the fact about Sasha's last word, "Meat". It may cause him to burst into laughter since Sasha only cared about meat even during her last breath. Because, in fact, Eren feels guilty of the loss of his friend — just like when he lost Hannes in Season 2.
Why AOT ending is bad?
The finale rubbed some the wrong way, whether it was because of the clumsy political implications, the unanswered questions, or unsatisfactory character arcs. While not the worst conclusion ever, Attack On Titan's ending will surely inspire debates for years on end, but not for the expected reasons.
Will AOT have a Season 5?
No, there will no Attack on Titan Season 5. It has been confirmed that part 2 for season 4 will be airing in the winter of 2022.
Why did AOT go to Mappa?
In order to incorporate smoother action sequences as opposed to the original 3D models and layouts the show is used to having, MAPPA was called in to take the mantle. However, fans didn't take this decision well as they went onto express heavy criticism of the show's animation on social media.
Who is Eren's wife?
Dina Yeager, neé Fritz, also known as the Smiling Titan, is a minor yet pivotal antagonist in the anime/manga series Attack on Titan.
Who is Eren's girlfriend?
Yes, Eren does love Mikasa as she is definitely the most important woman in his life after his mother. Despite this, it is possible for Eren and Historia to marry — more out of duty and obligation than love.
You might be interested: FAQ: Access Denied For User 'root'@'localhost'?
Who killed Eren?
Attack on Titan, a series that continued for 11 years has ended. After Mikasa kills Eren, the world becomes a world without Titans.
Is Eren the final villain?
When Attack on Titan first began, Eren Yeager was the main protagonist of the series. However, over the years, he became an antagonist, being the final enemy that many other main characters—including his friends—would fight against.
What was Sasha's last words AOT?
The distraught soldier tells his friends Sasha's last word was "meat" — and there is more to read behind that final parting then you may realize. Yes, meat may be delicious, but it also signifies an important wish for freedom that Sasha held until the day she died.
Why did Mikasa cut her hair?
Q: Why did Mikasa cut her hair short? A: She used to keep it long and make a ponytail. Due to some unexpected accident, the knot part was chopped off.
PreviousPrevious post: Often asked: Install Visual Studio Code Ubuntu?
NextNext post:Question: Xxl Freshman 2017 List?


Tell us about your experience with National Legal Professional Associates, and we will publish it!
National Legal Professional Associates offers the most comprehensive range of legal services: representation in courts and any proceedings, pre-trial settlement, etc. National Legal Professional Associates located in Marco Island, Florida. You can find other courts and attorneys near you.


text:
Turkish Stream Pipeline is Almost Finished, Gazprom Says
TurkStream should be online by the end of the year.
By Ben Aris for bne IntelliNews
Gazprom expects to export 198- 201bcm of gas this year to Europe and could supply half of Turkey's demand. bne IntelliNews
Last week Russian state-owned gas giant Gazprom invited bank analysts on a trip to inspect the construction work on the Turkish Stream (aka TurkStream) pipeline. They report work on the pipeline is on schedule and that it is 90% complete and should go online before the end of the year.
Turkish Stream is part of a trident of pipelines running from Russia to Europe to supply gas to Gazprom's main customers. Turkish Stream expands the southern route and is an addition to the Druzhba pipeline that runs through Ukraine and has been the main conduit since Soviet times. More controversial is the construction of the 55 billion cubic metre (bcm) Nord Stream 2 pipeline that runs under the Baltics and has been the subject of bitter political wrangling.
Opponents of Nord Stream 2 claim that it will make Europe more dependent on Russian gas, but while the same argument could be made against Turkish Stream, little has been said about the latter as most of the pipeline runs through Turkey, which is not an European Union (EU) member and so the EU has no leverage over Ankara's decision to go ahead with the pipeline.
Analysts visited the Russkaya compressor station in Russia last week, the starting point of the Turkish Stream pipeline, and the receiving Turkish Stream terminal in Turkey.
Gazprom also held a roundtable discussion at which the company and independent experts (IHS Markit, S&P Global Platts and EPPEN Consulting) provided their views and outlooks on the European gas market.
The capacity of the Turkish Stream project is 31.5bcm. It will deliver gas from Russia's southern region to Turkey and then on to southern Europe.
"Gazprom has already finished construction of both the Russkaya compressor station and two offshore pipelines, while construction works at the receiving terminal in Turkey were 86% completed as of June 21," VTB Capital (VTBC) analysts who went on the trip said in a note.
Gazprom reiterated its plan to bring the pipeline online by the end of this year.
Gazprom Dismisses Fears of EU Gas Apocalypse This Winter
The company also reaffirmed the capital expenditure forecasts for the project: the subsea section will cost more than €7 billion, while investments into onshore pipelines are to reach €400 million for the pipeline section in Turkey and €1.4 billion for the Serbian section (€770 million is to be spent this year), according to VTBC.
While the pipeline gives Gazprom better access to the European market and increases its capacity to bypass Ukraine completely, the main customer on the route is Turkey itself.
One of the most populous countries in Europe, Turkey has next to nothing in the way of energy resources and is heavily dependent on Russian gas. Moreover, as Turkey also has next to nothing in the way of gas storage facilities (the largest part of Gazprom's storage facilities were built in Ukraine during Soviet times), it is also dependent on Moscow to supply gas on a demand basis through the existing pipelines. One of the advantages of the new pipeline for Turkey is the constant flow of gas over its territory on its way to Europe that will improve Turkey's energy security.
The Turkish gas market suffered a setback last year, hurt by economic turmoil in the country, reports VTBC. "As a result, Turkish gas consumption fell 8.4% year-on-year to 49.3bcm in 2018, with Gazprom's gas exports to the country falling 17% year-on-year to 24bcm," VTBC reports.
"EPPEN Consulting, a Turkish consulting firm, expects gas demand in Turkey to fluctuate in a 48-50bcm range in 2019-2023. In the longer term, though, gas demand might be potentially supported by the increased competitiveness of Turkish industry due to lira depreciation, with WoodMac forecasting more than 60bcm gas demand by 2035. In this case, if both direct pipelines to Turkey (Blue Stream And Turkish Stream) were fully utilised, Gazprom might be able to satisfy up to 53% of Turkish gas demand."
As Rumors Swirl That Russia's Gazprom Will Replace Its Chief, the Company's Value Keeps Growing
While gas prices in Europe have shrunk to a multi-year low, the demand remains strong. Last year Gazprom exported almost 200bcm to Europe, and has kept its forecast for this year at 198-201bcm and $230/thousand cubic meters average price.
At the same time Ukraine's national gas company told bne IntelliNews that it expects to be cut off from Russian gas completely on January 1, 2020 and is also building up reserves. If Russia breaks its supply and transit agreement with Ukraine at the end of this year then that could lead to shortages in Europe, hence the build-up of reserves going on now.
European gas demand grew 1.1% year-on-year (or +3.1bcm) in the first half of this year, supported by increased demand in power generation (+13% year-on-year or 10.7bcm), VTBC reports.
"Nevertheless, Gazprom's export volumes were pressured by the increased LNG export to Europe, which are up 93.7% year-on-year, or 29.9bcm. in 1H19, falling 5.9% year-on-year to 95.3bcm over the period," VTBC said.
Analysts are less optimistic about Gazprom's exports to Europe and are forecasting 191bcm export and $223/thousand cubic meters average price. However, if Gazprom does cut Ukraine off then exports to Europe could fall significantly.
This article first appeared in bne IntelliNews.
Ben Aris for bne IntelliNews
Ben is the founder and editor of bne IntelliNews, a specialist business, economics and finance outlet covering Russia and Eastern Europe.	@bneeditor
Read more about: Gas , Turkey , Gazprom
HOLDING BACK
Gazprom Rejects Gas Transit Increase as Europe Faces Winter Fuel Crisis
The state-controlled company is coming under fire as gas prices surge in Europe.
GAS GO-AHEAD
Denmark Approves Nord Stream 2 Pipeline
Gazprom's new pipeline overcomes final hurdle with green light from Denmark.
Russia's Biggest Companies Account for More Than 80% of GDP
The RBC 500 shows concentration increased at the top in 2018.
Putin Seeks to Increase Arctic Gas Deliveries to China
The president has requested that state natural gas company Gazprom find the necessary resources in the Yamal gas fields.


text:
Earth, Sea and Sky
Natural Hazards and Disasters
Story: Earthquakes
What causes earthquakes?
Seismic activity in New Zealand
Living with earthquakes
Building for earthquake resistance
Extent of shaking, 1942 Wairarapa earthquake
Unlike the magnitude scale, which measures the size of an earthquake at its epicentre, the modified Mercalli scale measures the intensity of shaking felt by people on the ground. From observations gathered after an earthquake, it is possible to draw lines of intensity that radiate outwards from the epicentre. These lines, known as isoseismals, are represented by numbers from 1 to 12.
This map shows isoseismal lines drawn from observations of the 1942 Wairarapa earthquake. The greatest effects (measuring 8) were felt at the epicentre, reducing to 4 at the outer margins. The places where people reported the different intensities are also numbered 1–9. The earthquake did extensive damage to buildings in Wairarapa townships. In Wellington windows were shattered, masonry fell from buildings, and 20,000 chimneys were damaged.
Te Ara - The Encyclopedia of New Zealand
This item has been provided for private study purposes (such as school projects, family and local history research) and any published reproduction (print or electronic) may infringe copyright law. It is the responsibility of the user of any material to obtain clearance from the copyright holder.
Source: G. L. Downes, Atlas of isoseismal maps of New Zealand earthquakes. Lower Hutt: Institute of Geological and Nuclear Sciences, 1995.
The earthquake magnitude scale
Liquefaction in Christchurch, 2011
Eileen McSaveney, 'Earthquakes - What causes earthquakes?', Te Ara - the Encyclopedia of New Zealand, http://www.TeAra.govt.nz/en/interactive/4407/extent-of-shaking-1942-wairarapa-earthquake (accessed 22 January 2022)
Story by Eileen McSaveney, published 12 Jun 2006, reviewed & revised 1 Aug 2017