<commit_before>#!/usr/bin/env python
__author__ = 'Sergei F. Kliver'

import os
import re
from math import sqrt
from copy import deepcopy
from collections import OrderedDict, Iterable

import numpy as np

import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt

from Bio import SeqIO
from Bio.SeqFeature import SeqFeature, FeatureLocation

from MACE.Parsers.Abstract import Record, Collection, Metadata, Header

class RecordSNPeff(Record):
    def __init__(self, pos, ref, alt_list, filter_list, effect, effect_impact, functional_class, codon_change,
                 amino_acid_change, amino_acid_len, gene_name, transcript_biotype, gene_coding, transcript_id, exon_rank,
                 genotype_number, errors, warnings, gene_name_alias_list=[], gene_function_list=[], gene_description=None,
                 info_dict=None, flags=None, biochemical_pathway=None):

        Record.__init__(self, pos=pos, info_dict=info_dict, flags=flags)

        self.ref = ref
        self.alt_list = alt_list
        self.filter_list = filter_list
        self.effect = effect
        self.effect_impact = effect_impact
        self.functional_class = functional_class
        self.codon_change = codon_change
        self.amino_acid_change = amino_acid_change
        self.amino_acid_len = amino_acid_len
        self.gene_name = gene_name
        self.gene_name_alias_list = gene_name_alias_list
        self.gene_function_list = gene_function_list
        self.gene_description = gene_description
        self.transcript_biotype = transcript_biotype
        self.gene_coding = gene_coding
        self.transcript_id = transcript_id
        self.exon_rank = exon_rank
        self.genotype_number = genotype_number
        self.errors = errors
        self.warnings = warnings
        self.biochemical_pathway = biochemical_pathway

    def __str__(self):
        vcf_part = "%i\t%s\t%s\t%s" % (self.pos, self.ref, ",".join(self.alt_list) if self.alt_list else ".",
                                       ",".join(self.filter_list) if self.filter_list else ".")
        snpeff_part = ("\t" + "%s\t" * 17 + "%s") % (self.effect,
                                                     self.effect_impact,
                                                     self.functional_class,
                                                     self.codon_change,
                                                     self.amino_acid_change,
                                                     str(self.amino_acid_len) if self.amino_acid_len else ".",
                                                     self.gene_name,
                                                     self.transcript_biotype,
                                                     self.gene_coding,
                                                     self.transcript_id,
                                                     self.exon_rank,
                                                     self.genotype_number,
                                                     self.errors,
                                                     self.warnings,
                                                     ",".join(self.gene_name_alias_list) if self.gene_name_alias_list else ".",
                                                     ",".join(self.gene_function_list) if self.gene_function_list else ".",
                                                     ",".join(self.gene_description) if self.gene_description else ".",
                                                     self.biochemical_pathway if self.biochemical_pathway else ".")

        return vcf_part + snpeff_part

class HeaderSNPeff(list, Header):
    """
    HeaderVCF class
    """

    def __str__(self):
        """
        :return: tab-separatd representation of header
        """
        return "#" + "\t".join(self)

class CollectionSNPeff(Collection):

    def __init__(self, metadata=None, records_dict=None, header=None, input_file=None, from_file=False, filetype='tab'):
        # metadata should be Metadata class
        # header should be Header class
        # record_list should be list of Record class
        if from_file:
            self.read(input_file, filetype=filetype)
        else:
            self.records = OrderedDict() if records_dict is None else records_dict
            self.header = header
            self.metadata = metadata
        self.scaffold_list = self.scaffolds()
        self.scaffold_length = self.scaffold_len()
        self.number_of_scaffolds = len(self.scaffold_list)
        self.record_index = self.rec_index()

    def read(self, in_file, filetype='tab'):
        """
        Reads collection from vcf file
        :param in_file: path to file
        :param external_metadata: external(not from input file) metadata that could be used to parse records
        :return: None
        """
        self.metadata = None
        self.records = OrderedDict({})
        with open(in_file, "r") as fd:
            if filetype == 'tab':
                self.header = HeaderSNPeff(fd.readline()[1:].strip().split("\t"))
                gene_alias_presence = "Gene_Name_Aliases" in self.header
                gene_function_presence = "Gene_Functions" in self.header
                gene_description_presence = "Gene_Description" in self.header
                biochemical_pathway_presence = "Biochemical_Pathway" in self.header
                for line in fd:
                    scaffold, record = self.add_record_from_tab_line(line,
                                                                     gene_alias_presence=gene_alias_presence,
                                                                     gene_function_presence=gene_function_presence,
                                                                     gene_description_presence=gene_description_presence,
                                                                     biochemical_pathway_presence=biochemical_pathway_presence)
                    if scaffold not in self.records:
                        self.records[scaffold] = []
                    self.records[scaffold].append(record)

            elif filetype == 'vcf':
                raise ValueError("Parsing from vcf file annotated by SNPeff is not supported yet")

            else:
                raise ValueError("Wrong filetype for parsing")

        if not gene_alias_presence:
            self.header.append("Gene_Name_Aliases")
        if not gene_function_presence:
            self.header.append("Gene_Functions")
        if not gene_description_presence:
            self.header.append("Gene_Description")
        if not biochemical_pathway_presence:
            self.header.append("Biochemical_Pathway")

    def add_record_from_tab_line(self, line,
                                 gene_alias_presence,
                                 gene_function_presence,
                                 gene_description_presence,
                                 biochemical_pathway_presence):
        """
        Adds record to collection from line
        :param line: record line from tab file
        :return: None
        """
        line_list = line.strip().split("\t")
        pos = int(line_list[1])
        ref = line_list[2]
        alt_list = line_list[3].split(",")
        filter_list = line_list[4].split(",")
        effect = line_list[5]
        effect_impact = line_list[6]
        functional_class = line_list[7]
        codon_change = line_list[8]
        amino_acid_change = line_list[9]
        amino_acid_len = int(line_list[10]) if line_list[10] != "." else None
        gene_name = line_list[11]
        transcript_biotype = line_list[12]
        gene_coding = line_list[13]
        transcript_id = line_list[14]
        exon_rank = line_list[15]
        genotype_number = line_list[16]
        errors = line_list[17]
        warnings = line_list[18]
        gene_name_alias_list = line_list[self.header.index("Gene_Name_Aliases")].split(",") if gene_alias_presence else []
        gene_function_list = line_list[self.header.index("Gene_Functions")].split(",") if gene_function_presence else []
        gene_description = line_list[self.header.index("Gene_Description")].split(",") if gene_description_presence else []
        biochemical_pathway = line_list[self.header.index("Biochemical_Pathway")].split(",") if biochemical_pathway_presence else []

        return line_list[0], RecordSNPeff(pos, ref, alt_list, filter_list, effect, effect_impact,
                                          functional_class, codon_change,
                                          amino_acid_change, amino_acid_len, gene_name, transcript_biotype,
                                          gene_coding, transcript_id, exon_rank,
                                          genotype_number, errors, warnings,
                                          gene_name_alias_list=gene_name_alias_list,
                                          gene_function_list=gene_function_list,
                                          gene_description=gene_description,
                                          info_dict=None, flags=None,
                                          biochemical_pathway=biochemical_pathway)

    def add_gene_name_aliases(self, alias_dict):
        for scaffold in self.records:
            for record in self.records[scaffold]:
                if record.gene_name in alias_dict:
                    tmp_aliases = [alias_dict[record.gene_name]] if isinstance(alias_dict[record.gene_name], str) else alias_dict[record.gene_name]
                    new_aliases = []
                    for alias in tmp_aliases:
                        if alias != ".":
                            new_aliases.append(alias)
                    record.gene_name_alias_list = (record.gene_name_alias_list + new_aliases) if record.gene_name_alias_list else new_aliases

    def add_gene_functions(self, function_dict):
        for scaffold in self.records:
            for record in self.records[scaffold]:
                for gene_name in [record.gene_name] + record.gene_name_alias_list:
                    if gene_name in function_dict:
                        new_functions = [function_dict[record.gene_name]] if isinstance(function_dict[record.gene_name], str) else function_dict[record.gene_name]
                        record.gene_function_list = (record.gene_function_list + new_functions) if record.gene_function_list else new_functions
                        break

    def add_gene_description(self, description_dict):
        for scaffold in self.records:
            for record in self.records[scaffold]:
                for gene_name in [record.gene_name] + record.gene_name_alias_list:
                    if gene_name in description_dict:
                        new_descriptions = [description_dict[record.gene_name]] if isinstance(description_dict[record.gene_name], str) else description_dict[record.gene_name]
                        record.gene_description = (record.gene_description + new_descriptions) if record.gene_description else new_descriptions
                        break

    def add_biochemical_pathway(self, biochemical_pathway_dict):
        for scaffold in self.records:
            for record in self.records[scaffold]:
                for gene_name in [record.gene_name] + record.gene_name_alias_list:
                    if gene_name in biochemical_pathway_dict:
                        new_pathway = [biochemical_pathway_dict[record.gene_name]] if isinstance(biochemical_pathway_dict[record.gene_name], str) else biochemical_pathway_dict[record.gene_name]
                        record.biochemical_pathway = (record.biochemical_pathway + new_pathway) if record.biochemical_pathway else new_pathway
                        break
<commit_msg>added  set_repeat_filters<commit_after>#!/usr/bin/env python
__author__ = 'Sergei F. Kliver'

import os
import re
from math import sqrt
from copy import deepcopy
from collections import OrderedDict, Iterable

import numpy as np

import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt

from Bio import SeqIO
from Bio.SeqFeature import SeqFeature, FeatureLocation

from MACE.Parsers.Abstract import Record, Collection, Metadata, Header

class RecordSNPeff(Record):
    def __init__(self, pos, ref, alt_list, filter_list, effect, effect_impact, functional_class, codon_change,
                 amino_acid_change, amino_acid_len, gene_name, transcript_biotype, gene_coding, transcript_id, exon_rank,
                 genotype_number, errors, warnings, gene_name_alias_list=[], gene_function_list=[], gene_description=None,
                 info_dict=None, flags=None, biochemical_pathway=None):

        Record.__init__(self, pos=pos, info_dict=info_dict, flags=flags)

        self.ref = ref
        self.alt_list = alt_list
        self.filter_list = filter_list
        self.effect = effect
        self.effect_impact = effect_impact
        self.functional_class = functional_class
        self.codon_change = codon_change
        self.amino_acid_change = amino_acid_change
        self.amino_acid_len = amino_acid_len
        self.gene_name = gene_name
        self.gene_name_alias_list = gene_name_alias_list
        self.gene_function_list = gene_function_list
        self.gene_description = gene_description
        self.transcript_biotype = transcript_biotype
        self.gene_coding = gene_coding
        self.transcript_id = transcript_id
        self.exon_rank = exon_rank
        self.genotype_number = genotype_number
        self.errors = errors
        self.warnings = warnings
        self.biochemical_pathway = biochemical_pathway

    def __str__(self):
        vcf_part = "%i\t%s\t%s\t%s" % (self.pos, self.ref, ",".join(self.alt_list) if self.alt_list else ".",
                                       ",".join(self.filter_list) if self.filter_list else ".")
        snpeff_part = ("\t" + "%s\t" * 17 + "%s") % (self.effect,
                                                     self.effect_impact,
                                                     self.functional_class,
                                                     self.codon_change,
                                                     self.amino_acid_change,
                                                     str(self.amino_acid_len) if self.amino_acid_len else ".",
                                                     self.gene_name,
                                                     self.transcript_biotype,
                                                     self.gene_coding,
                                                     self.transcript_id,
                                                     self.exon_rank,
                                                     self.genotype_number,
                                                     self.errors,
                                                     self.warnings,
                                                     ",".join(self.gene_name_alias_list) if self.gene_name_alias_list else ".",
                                                     ",".join(self.gene_function_list) if self.gene_function_list else ".",
                                                     ",".join(self.gene_description) if self.gene_description else ".",
                                                     self.biochemical_pathway if self.biochemical_pathway else ".")

        return vcf_part + snpeff_part

class HeaderSNPeff(list, Header):
    """
    HeaderVCF class
    """

    def __str__(self):
        """
        :return: tab-separatd representation of header
        """
        return "#" + "\t".join(self)

class CollectionSNPeff(Collection):

    def __init__(self, metadata=None, records_dict=None, header=None, input_file=None, from_file=False, filetype='tab'):
        # metadata should be Metadata class
        # header should be Header class
        # record_list should be list of Record class
        if from_file:
            self.read(input_file, filetype=filetype)
        else:
            self.records = OrderedDict() if records_dict is None else records_dict
            self.header = header
            self.metadata = metadata
        self.scaffold_list = self.scaffolds()
        self.scaffold_length = self.scaffold_len()
        self.number_of_scaffolds = len(self.scaffold_list)
        self.record_index = self.rec_index()

    def read(self, in_file, filetype='tab'):
        """
        Reads collection from vcf file
        :param in_file: path to file
        :param external_metadata: external(not from input file) metadata that could be used to parse records
        :return: None
        """
        self.metadata = None
        self.records = OrderedDict({})
        with open(in_file, "r") as fd:
            if filetype == 'tab':
                self.header = HeaderSNPeff(fd.readline()[1:].strip().split("\t"))
                gene_alias_presence = "Gene_Name_Aliases" in self.header
                gene_function_presence = "Gene_Functions" in self.header
                gene_description_presence = "Gene_Description" in self.header
                biochemical_pathway_presence = "Biochemical_Pathway" in self.header
                for line in fd:
                    scaffold, record = self.add_record_from_tab_line(line,
                                                                     gene_alias_presence=gene_alias_presence,
                                                                     gene_function_presence=gene_function_presence,
                                                                     gene_description_presence=gene_description_presence,
                                                                     biochemical_pathway_presence=biochemical_pathway_presence)
                    if scaffold not in self.records:
                        self.records[scaffold] = []
                    self.records[scaffold].append(record)

            elif filetype == 'vcf':
                raise ValueError("Parsing from vcf file annotated by SNPeff is not supported yet")

            else:
                raise ValueError("Wrong filetype for parsing")

        if not gene_alias_presence:
            self.header.append("Gene_Name_Aliases")
        if not gene_function_presence:
            self.header.append("Gene_Functions")
        if not gene_description_presence:
            self.header.append("Gene_Description")
        if not biochemical_pathway_presence:
            self.header.append("Biochemical_Pathway")

    def add_record_from_tab_line(self, line,
                                 gene_alias_presence,
                                 gene_function_presence,
                                 gene_description_presence,
                                 biochemical_pathway_presence):
        """
        Adds record to collection from line
        :param line: record line from tab file
        :return: None
        """
        line_list = line.strip().split("\t")
        pos = int(line_list[1])
        ref = line_list[2]
        alt_list = line_list[3].split(",")
        filter_list = line_list[4].split(",")
        effect = line_list[5]
        effect_impact = line_list[6]
        functional_class = line_list[7]
        codon_change = line_list[8]
        amino_acid_change = line_list[9]
        amino_acid_len = int(line_list[10]) if line_list[10] != "." else None
        gene_name = line_list[11]
        transcript_biotype = line_list[12]
        gene_coding = line_list[13]
        transcript_id = line_list[14]
        exon_rank = line_list[15]
        genotype_number = line_list[16]
        errors = line_list[17]
        warnings = line_list[18]
        gene_name_alias_list = line_list[self.header.index("Gene_Name_Aliases")].split(",") if gene_alias_presence else []
        gene_function_list = line_list[self.header.index("Gene_Functions")].split(",") if gene_function_presence else []
        gene_description = line_list[self.header.index("Gene_Description")].split(",") if gene_description_presence else []
        biochemical_pathway = line_list[self.header.index("Biochemical_Pathway")].split(",") if biochemical_pathway_presence else []

        return line_list[0], RecordSNPeff(pos, ref, alt_list, filter_list, effect, effect_impact,
                                          functional_class, codon_change,
                                          amino_acid_change, amino_acid_len, gene_name, transcript_biotype,
                                          gene_coding, transcript_id, exon_rank,
                                          genotype_number, errors, warnings,
                                          gene_name_alias_list=gene_name_alias_list,
                                          gene_function_list=gene_function_list,
                                          gene_description=gene_description,
                                          info_dict=None, flags=None,
                                          biochemical_pathway=biochemical_pathway)

    def add_gene_name_aliases(self, alias_dict):
        for scaffold in self.records:
            for record in self.records[scaffold]:
                if record.gene_name in alias_dict:
                    tmp_aliases = [alias_dict[record.gene_name]] if isinstance(alias_dict[record.gene_name], str) else alias_dict[record.gene_name]
                    new_aliases = []
                    for alias in tmp_aliases:
                        if alias != ".":
                            new_aliases.append(alias)
                    record.gene_name_alias_list = (record.gene_name_alias_list + new_aliases) if record.gene_name_alias_list else new_aliases

    def add_gene_functions(self, function_dict):
        for scaffold in self.records:
            for record in self.records[scaffold]:
                for gene_name in [record.gene_name] + record.gene_name_alias_list:
                    if gene_name in function_dict:
                        new_functions = [function_dict[gene_name]] if isinstance(function_dict[gene_name], str) else function_dict[gene_name]
                        record.gene_function_list = (record.gene_function_list + new_functions) if record.gene_function_list else new_functions
                        break

    def add_gene_description(self, description_dict):
        for scaffold in self.records:
            for record in self.records[scaffold]:
                for gene_name in [record.gene_name] + record.gene_name_alias_list:
                    if gene_name in description_dict:
                        new_descriptions = [description_dict[gene_name]] if isinstance(description_dict[gene_name], str) else description_dict[gene_name]
                        record.gene_description = (record.gene_description + new_descriptions) if record.gene_description else new_descriptions
                        break

    def add_biochemical_pathway(self, biochemical_pathway_dict):
        for scaffold in self.records:
            for record in self.records[scaffold]:
                for gene_name in [record.gene_name] + record.gene_name_alias_list:
                    if gene_name in biochemical_pathway_dict:
                        new_pathway = [biochemical_pathway_dict[gene_name]] if isinstance(biochemical_pathway_dict[gene_name], str) else biochemical_pathway_dict[gene_name]
                        record.biochemical_pathway = (record.biochemical_pathway + new_pathway) if record.biochemical_pathway else new_pathway
                        break
<|endoftext|>


<commit_before>import netCDF4_classic
import numpy

__version__ = "0.5"

# Classes Dataset, Dimension and Variable allow handling a multi-file dataset.

class Dataset(netCDF4_classic.Dataset):

    def __init__(self, cdfFiles):
        """Open a Dataset spanning multiple files,
        making it look as if it was a single file.

        Arguments:
          cdfFiles      sequence of CDF files; the first one will become the
                        "master" file, defining all the record variables which may
                        span subsequent files

        Returns:
          None

        The files are always opened in read-only mode.
                                                        """

        # Open the master file in the base class, so that the CDFMF instance
        # can be used like a CDF instance.
        master = cdfFiles[0]

        # Open the master again, this time as a classic CDF instance. This will avoid
        # calling methods of the CDFMF subclass when querying the master file.
        cdfm = netCDF4_classic.Dataset(master)
        # copy attributes from master.
        for name, value in cdfm.__dict__.items():
            self.__dict__[name] = value

        # Make sure the master defines an unlimited dimension.
        unlimDimId = None
        for dimname,dim in cdfm.dimensions.items():
            if dim.isunlimited():
                unlimDimId = dim
                unlimDimName = dimname
        if unlimDimId is None:
            raise MFDatasetError("master dataset %s does not have an unlimited dimension" % master)

        # Get info on all record variables defined in the master.
        # Make sure the master defines at least one record variable.
        masterRecVar = {}
        for vName,v in cdfm.variables.items():
            dims = v.dimensions
            shape = v.shape
            type = v.dtype
            # Be carefull: we may deal with a scalar (dimensionless) variable.
            # Unlimited dimension always occupies index 0.
            if (len(dims) > 0 and unlimDimName == dims[0]):
                masterRecVar[vName] = (dims, shape, type)
        if len(masterRecVar) == 0:
            raise MFDatasetError("master dataset %s does not have any record variable" % master)

        # Create the following:
        #   cdf       list of Dataset instances
        #   cdfVLen   list unlimited dimension lengths in each CDF instance
        #   cdfRecVar dictionnary indexed by the record var names; each key holds
        #             a list of the corresponding Variable instance, one for each
        #             cdf file of the file set
        cdf = [cdfm]
        self._cdf = cdf        # Store this now, because dim() method needs it
        cdfVLen = [len(unlimDimId)]
        cdfRecVar = {}
        for v in masterRecVar.keys():
            cdfRecVar[v] = [cdfm.variables[v]]

        # Open each remaining file in read-only mode.
        # Make sure each file defines the same record variables as the master
        # and that the variables are defined in the same way (name, shape and type)
        for f in cdfFiles[1:]:
            part = netCDF4_classic.Dataset(f)
            varInfo = part.variables
            for v in masterRecVar.keys():
                # Make sure master rec var is also defined here.
                if v not in varInfo.keys():
                    raise MFDatasetError("record variable %s not defined in %s" % (v, f))

                # Make sure it is a record var.
                vInst = part.variables[v]
                if not part.dimensions[vInst.dimensions[0]].isunlimited():
                    raise MFDataset("variable %s is not a record var inside %s" % (v, f))

                masterDims, masterShape, masterType = masterRecVar[v][:3]
                extDims, extShape, extType = varInfo[v][:3]
                extDims = varInfo[v].dimensions
                extShape = varInfo[v].shape
                extType = varInfo[v].dtype
                # Check that dimension names are identical.
                if masterDims != extDims:
                    raise MFDatasetError("variable %s : dimensions mismatch between "
                                   "master %s (%s) and extension %s (%s)" %
                                   (v, master, masterDims, f, extDims))

                # Check that the ranks are identical, and the dimension lengths are
                # identical (except for that of the unlimited dimension, which of
                # course may vary.
                if len(masterShape) != len(extShape):
                    raise MFDatasetError("variable %s : rank mismatch between "
                                   "master %s (%s) and extension %s (%s)" %
                                   (v, master, len(masterShape), f, len(extShape)))
                if masterShape[1:] != extShape[1:]:
                    raise MFDatasetError("variable %s : shape mismatch between "
                                   "master %s (%s) and extension %s (%s)" %
                                   (v, master, masterShape, f, extShape))

                # Check that the data types are identical.
                if masterType != extType:
                    raise MFDatasetError("variable %s : data type mismatch between "
                                   "master %s (%s) and extension %s (%s)" %
                                   (v, master, masterType, f, extType))

                # Everythig ok.
                cdfRecVar[v].append(vInst)

            cdf.append(part)
            cdfVLen.append(len(part.dimensions[unlimDimName]))

        # Attach attributes to the MFDataset.Dataset instance.
        # A local __setattr__() method is required for them.
        self._cdfFiles = cdfFiles            # list of cdf file names in the set
        self._cdfVLen = cdfVLen              # list of unlimited lengths
        self._cdfTLen = reduce(lambda x, y: x + y, cdfVLen) # total length
        self._cdfRecVar = cdfRecVar          # dictionary of Variable instances for all
                                             # the record variables
        self._dims = cdfm.dimensions
        for dimname, dim in self._dims.items():
            if dim.isunlimited():
                self._dims[dimname] = Dimension(dimname, dim, self._cdfVLen, self._cdfTLen)
        self._vars = cdfm.variables
        for varname,var in self._vars.items():
            if varname in self._cdfRecVar.keys():
                self._vars[varname] = Variable(self, varname, var, unlimDimName)
        self._file_format = []
        for dset in self._cdf:
            self._file_format.append(dset.file_format)

    def __setattr__(self, name, value):
        """override base class attribute creation"""
        self.__dict__[name] = value

    def __getattribute__(self, name):
        if name in ['variables','dimensions','file_format']:
            if name == 'dimensions': return self._dims
            if name == 'variables': return self._vars
            if name == 'file_format': return self._file_format
        else:
            return netCDF4_classic.Dataset.__getattribute__(self, name)

    def ncattrs(self):
        return self._cdf[0].__dict__.keys()

class Dimension(object):
    def __init__(self, dimname, dim, dimlens, dimtotlen):
        self.dimlens = dimlens
        self.dimtotlen = dimtotlen
    def __len__(self):
        return self.dimtotlen
    def isunlimited(self):
        return True

class Variable(object):
    def __init__(self, dset, varname, var, recdimname):
        self.dimensions = var.dimensions
        self._dset = dset
        self._mastervar = var
        self._recVar = dset._cdfRecVar[varname]
        self._recdimname = recdimname
        self._recLen = dset._cdfVLen
        self.dtype = var.dtype
        # copy attributes from master.
        for name, value in var.__dict__.items():
            self.__dict__[name] = value
    def typecode(self):
        return self.dtype
    def ncattrs(self):
        return self._mastervar.__dict__.keys()
    def __getattr__(self,name):
        if name == 'shape': return self._shape()
        return self.__dict__[name]
    def _shape(self):
        recdimlen = len(self._dset.dimensions[self._recdimname])
        return (recdimlen,) + self._mastervar.shape[1:]
    def __getitem__(self, elem):
        """Get records from a concatenated set of variables."""
        # Number of variables making up the MFVariable.Variable.
        nv = len(self._recLen)
        # Parse the slicing expression, needed to properly handle
        # a possible ellipsis.
        start, count, stride = netCDF4_classic._buildStartCountStride(elem, self.shape, self.dimensions, self._dset.dimensions)
        # make sure count=-1 becomes count=1
        count = [abs(cnt) for cnt in count]
        if (numpy.array(stride) < 0).any():
            raise IndexError('negative strides not allowed when slicing MFVariable Variable instance')
        # Start, stop and step along 1st dimension, eg the unlimited
        # dimension.
        sta = start[0]
        step = stride[0]
        stop = sta + count[0] * step

        # Build a list representing the concatenated list of all records in
        # the MFVariable variable set. The list is composed of 2-elem lists
        # each holding:
        #  the record index inside the variables, from 0 to n
        #  the index of the Variable instance to which each record belongs
        idx = []    # list of record indices
        vid = []    # list of Variable indices
        for n in range(nv):
            k = self._recLen[n]     # number of records in this variable
            idx.extend(range(k))
            vid.extend([n] * k)

        # Merge the two lists to get a list of 2-elem lists.
        # Slice this list along the first dimension.
        lst = zip(idx, vid).__getitem__(slice(sta, stop, step))

        # Rebuild the slicing expression for dimensions 1 and ssq.
        newSlice = [slice(None, None, None)]
        for n in range(1, len(start)):   # skip dimension 0
            newSlice.append(slice(start[n],
                                  start[n] + count[n] * stride[n], stride[n]))

        # Apply the slicing expression to each var in turn, extracting records
        # in a list of arrays.
        lstArr = []
        for n in range(nv):
            # Get the list of indices for variable 'n'.
            idx = [i for i,numv in lst if numv == n]
            if idx:
                # Rebuild slicing expression for dimension 0.
                newSlice[0] = slice(idx[0], idx[-1] + 1, step)
                # Extract records from the var, and append them to a list
                # of arrays.
                lstArr.append(netCDF4_classic.Variable.__getitem__(self._recVar[n], tuple(newSlice)))

        # Return the extracted records as a unified array.
        if lstArr:
            lstArr = numpy.concatenate(lstArr)
        return lstArr

if __name__ == '__main__':
    from numpy.random import randint
    from numpy.testing import assert_array_equal
    import glob, datetime

    nx = 100
    ydim=5; zdim=10
    data = randint(0,10,size=(nx,ydim,zdim))
    for nfile in range(10):
        f = netCDF4_classic.Dataset('test'+repr(nfile)+'.nc','w')
        f.createDimension('x',None)
        f.createDimension('y',ydim)
        f.createDimension('z',zdim)
        f.history = 'created '+str(datetime.datetime.now())
        x = f.createVariable('x','i',('x',))
        x.units = 'zlotnys'
        dat = f.createVariable('data','i',('x','y','z',))
        dat.name = 'phony data'
        nx1 = nfile*10; nx2 = 10*(nfile+1)
        x[0:10] = numpy.arange(nfile*10,10*(nfile+1))
        dat[0:10] = data[nx1:nx2]
        f.close()

    files = glob.glob('test*.nc')
    print files
    f = Dataset(files)
    print f.variables
    print f.dimensions
    print f.ncattrs()
    print f.history
    print f.file_format
    print f.variables['data'].shape
    print f.variables['x'].shape
    print f.variables['x'][:]
    assert_array_equal(numpy.arange(0,nx),f.variables['x'][:])
    datin = f.variables['data'][4:-4:4,3:5,2:8]
    assert_array_equal(datin,data[4:-4:4,3:5,2:8])
<commit_msg>updated test code with __name__ == '__main__'<commit_after>import netCDF4_classic
import numpy

__version__ = "0.5"

# Classes Dataset, Dimension and Variable allow handling a multi-file dataset.

class Dataset(netCDF4_classic.Dataset):

    def __init__(self, cdfFiles):
        """Open a Dataset spanning multiple files,
        making it look as if it was a single file.

        Arguments:
          cdfFiles      sequence of CDF files; the first one will become the
                        "master" file, defining all the record variables which may
                        span subsequent files

        Returns:
          None

        The files are always opened in read-only mode.
                                                        """

        # Open the master file in the base class, so that the CDFMF instance
        # can be used like a CDF instance.
        master = cdfFiles[0]

        # Open the master again, this time as a classic CDF instance. This will avoid
        # calling methods of the CDFMF subclass when querying the master file.
        cdfm = netCDF4_classic.Dataset(master)
        # copy attributes from master.
        for name, value in cdfm.__dict__.items():
            self.__dict__[name] = value

        # Make sure the master defines an unlimited dimension.
        unlimDimId = None
        for dimname,dim in cdfm.dimensions.items():
            if dim.isunlimited():
                unlimDimId = dim
                unlimDimName = dimname
        if unlimDimId is None:
            raise MFDatasetError("master dataset %s does not have an unlimited dimension" % master)

        # Get info on all record variables defined in the master.
        # Make sure the master defines at least one record variable.
        masterRecVar = {}
        for vName,v in cdfm.variables.items():
            dims = v.dimensions
            shape = v.shape
            type = v.dtype
            # Be carefull: we may deal with a scalar (dimensionless) variable.
            # Unlimited dimension always occupies index 0.
            if (len(dims) > 0 and unlimDimName == dims[0]):
                masterRecVar[vName] = (dims, shape, type)
        if len(masterRecVar) == 0:
            raise MFDatasetError("master dataset %s does not have any record variable" % master)

        # Create the following:
        #   cdf       list of Dataset instances
        #   cdfVLen   list unlimited dimension lengths in each CDF instance
        #   cdfRecVar dictionnary indexed by the record var names; each key holds
        #             a list of the corresponding Variable instance, one for each
        #             cdf file of the file set
        cdf = [cdfm]
        self._cdf = cdf        # Store this now, because dim() method needs it
        cdfVLen = [len(unlimDimId)]
        cdfRecVar = {}
        for v in masterRecVar.keys():
            cdfRecVar[v] = [cdfm.variables[v]]

        # Open each remaining file in read-only mode.
        # Make sure each file defines the same record variables as the master
        # and that the variables are defined in the same way (name, shape and type)
        for f in cdfFiles[1:]:
            part = netCDF4_classic.Dataset(f)
            varInfo = part.variables
            for v in masterRecVar.keys():
                # Make sure master rec var is also defined here.
                if v not in varInfo.keys():
                    raise MFDatasetError("record variable %s not defined in %s" % (v, f))

                # Make sure it is a record var.
                vInst = part.variables[v]
                if not part.dimensions[vInst.dimensions[0]].isunlimited():
                    raise MFDataset("variable %s is not a record var inside %s" % (v, f))

                masterDims, masterShape, masterType = masterRecVar[v][:3]
                extDims, extShape, extType = varInfo[v][:3]
                extDims = varInfo[v].dimensions
                extShape = varInfo[v].shape
                extType = varInfo[v].dtype
                # Check that dimension names are identical.
                if masterDims != extDims:
                    raise MFDatasetError("variable %s : dimensions mismatch between "
                                   "master %s (%s) and extension %s (%s)" %
                                   (v, master, masterDims, f, extDims))

                # Check that the ranks are identical, and the dimension lengths are
                # identical (except for that of the unlimited dimension, which of
                # course may vary.
                if len(masterShape) != len(extShape):
                    raise MFDatasetError("variable %s : rank mismatch between "
                                   "master %s (%s) and extension %s (%s)" %
                                   (v, master, len(masterShape), f, len(extShape)))
                if masterShape[1:] != extShape[1:]:
                    raise MFDatasetError("variable %s : shape mismatch between "
                                   "master %s (%s) and extension %s (%s)" %
                                   (v, master, masterShape, f, extShape))

                # Check that the data types are identical.
                if masterType != extType:
                    raise MFDatasetError("variable %s : data type mismatch between "
                                   "master %s (%s) and extension %s (%s)" %
                                   (v, master, masterType, f, extType))

                # Everythig ok.
                cdfRecVar[v].append(vInst)

            cdf.append(part)
            cdfVLen.append(len(part.dimensions[unlimDimName]))

        # Attach attributes to the MFDataset.Dataset instance.
        # A local __setattr__() method is required for them.
        self._cdfFiles = cdfFiles            # list of cdf file names in the set
        self._cdfVLen = cdfVLen              # list of unlimited lengths
        self._cdfTLen = reduce(lambda x, y: x + y, cdfVLen) # total length
        self._cdfRecVar = cdfRecVar          # dictionary of Variable instances for all
                                             # the record variables
        self._dims = cdfm.dimensions
        for dimname, dim in self._dims.items():
            if dim.isunlimited():
                self._dims[dimname] = Dimension(dimname, dim, self._cdfVLen, self._cdfTLen)
        self._vars = cdfm.variables
        for varname,var in self._vars.items():
            if varname in self._cdfRecVar.keys():
                self._vars[varname] = Variable(self, varname, var, unlimDimName)
        self._file_format = []
        for dset in self._cdf:
            self._file_format.append(dset.file_format)

    def __setattr__(self, name, value):
        """override base class attribute creation"""
        self.__dict__[name] = value

    def __getattribute__(self, name):
        if name in ['variables','dimensions','file_format']:
            if name == 'dimensions': return self._dims
            if name == 'variables': return self._vars
            if name == 'file_format': return self._file_format
        else:
            return netCDF4_classic.Dataset.__getattribute__(self, name)

    def ncattrs(self):
        return self._cdf[0].__dict__.keys()

class Dimension(object):
    def __init__(self, dimname, dim, dimlens, dimtotlen):
        self.dimlens = dimlens
        self.dimtotlen = dimtotlen
    def __len__(self):
        return self.dimtotlen
    def isunlimited(self):
        return True

class Variable(object):
    def __init__(self, dset, varname, var, recdimname):
        self.dimensions = var.dimensions
        self._dset = dset
        self._mastervar = var
        self._recVar = dset._cdfRecVar[varname]
        self._recdimname = recdimname
        self._recLen = dset._cdfVLen
        self.dtype = var.dtype
        # copy attributes from master.
        for name, value in var.__dict__.items():
            self.__dict__[name] = value
    def typecode(self):
        return self.dtype
    def ncattrs(self):
        return self._mastervar.__dict__.keys()
    def __getattr__(self,name):
        if name == 'shape': return self._shape()
        return self.__dict__[name]
    def _shape(self):
        recdimlen = len(self._dset.dimensions[self._recdimname])
        return (recdimlen,) + self._mastervar.shape[1:]
    def __getitem__(self, elem):
        """Get records from a concatenated set of variables."""
        # Number of variables making up the MFVariable.Variable.
        nv = len(self._recLen)
        # Parse the slicing expression, needed to properly handle
        # a possible ellipsis.
        start, count, stride = netCDF4_classic._buildStartCountStride(elem, self.shape, self.dimensions, self._dset.dimensions)
        # make sure count=-1 becomes count=1
        count = [abs(cnt) for cnt in count]
        if (numpy.array(stride) < 0).any():
            raise IndexError('negative strides not allowed when slicing MFVariable Variable instance')
        # Start, stop and step along 1st dimension, eg the unlimited
        # dimension.
        sta = start[0]
        step = stride[0]
        stop = sta + count[0] * step

        # Build a list representing the concatenated list of all records in
        # the MFVariable variable set. The list is composed of 2-elem lists
        # each holding:
        #  the record index inside the variables, from 0 to n
        #  the index of the Variable instance to which each record belongs
        idx = []    # list of record indices
        vid = []    # list of Variable indices
        for n in range(nv):
            k = self._recLen[n]     # number of records in this variable
            idx.extend(range(k))
            vid.extend([n] * k)

        # Merge the two lists to get a list of 2-elem lists.
        # Slice this list along the first dimension.
        lst = zip(idx, vid).__getitem__(slice(sta, stop, step))

        # Rebuild the slicing expression for dimensions 1 and ssq.
        newSlice = [slice(None, None, None)]
        for n in range(1, len(start)):   # skip dimension 0
            newSlice.append(slice(start[n],
                                  start[n] + count[n] * stride[n], stride[n]))

        # Apply the slicing expression to each var in turn, extracting records
        # in a list of arrays.
        lstArr = []
        for n in range(nv):
            # Get the list of indices for variable 'n'.
            idx = [i for i,numv in lst if numv == n]
            if idx:
                # Rebuild slicing expression for dimension 0.
                newSlice[0] = slice(idx[0], idx[-1] + 1, step)
                # Extract records from the var, and append them to a list
                # of arrays.
                lstArr.append(netCDF4_classic.Variable.__getitem__(self._recVar[n], tuple(newSlice)))

        # Return the extracted records as a unified array.
        if lstArr:
            lstArr = numpy.concatenate(lstArr)
        return lstArr

if __name__ == '__main__':
    from numpy.random import randint
    from numpy.testing import assert_array_equal
    import glob, datetime

    nx = 100
    ydim=5; zdim=10
    data = randint(0,10,size=(nx,ydim,zdim))
    for nfile in range(10):
        if nfile == 0:
            f = netCDF4_classic.Dataset('test'+repr(nfile)+'.nc','w',format='NETCDF3_CLASSIC')
        else:
            f = netCDF4_classic.Dataset('test'+repr(nfile)+'.nc','w')
        f.createDimension('x',None)
        f.createDimension('y',ydim)
        f.createDimension('z',zdim)
        f.history = 'created '+str(datetime.datetime.now())
        x = f.createVariable('x','i',('x',))
        x.units = 'zlotnys'
        dat = f.createVariable('data','i',('x','y','z',))
        dat.name = 'phony data'
        nx1 = nfile*10; nx2 = 10*(nfile+1)
        x[0:10] = numpy.arange(nfile*10,10*(nfile+1))
        dat[0:10] = data[nx1:nx2]
        f.close()

    files = glob.glob('test*.nc')
    print files
    f = Dataset(files)
    print f.variables
    print f.dimensions
    print f.ncattrs()
    print f.history
    print f.file_format
    print f.variables['data'].shape
    print f.variables['x'].shape
    print f.variables['x'][:]
    print f.variables['x'].ncattrs()
    print f.variables['x'].units
    assert_array_equal(numpy.arange(0,nx),f.variables['x'][:])
    datin = f.variables['data'][4:-4:4,3:5,2:8]
    assert_array_equal(datin,data[4:-4:4,3:5,2:8])
<|endoftext|>


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

# (c) 2012, Marco Vito Moscaritolo <marco@agavee.com>
#     modified by Tomas Karasek <tomas.karasek@digile.fi>
#
# This file is part of Ansible,
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible.  If not, see <http://www.gnu.org/licenses/>.

import sys
import re
import os
import argparse
import subprocess
import yaml
import time
import md5
import itertools
import novaclient.client
import ansible.module_utils.openstack

try:
    import json
except ImportError:
    import simplejson as json

# This is a script getting dynamic inventory from Nova. Features:
# - you can refer to instances by their nova name in ansible{-playbook} calls
# - you can refer to single tenants, regions and openstack environments in
#   ansible{-playbook} calls
# - you can refer to a hostgroup when you pass the arbitrary --meta group=
#   in "nova boot"
# - it caches the state of the cloud
# - it tries to guess ansible_ssh_user based on name of image
#   ('\cubuntu' -> 'ubuntu', '\ccentos' -> 'cloud-user', ...)
# - allows to access machines by their private ip *
# - it will work with no additional configuration, just handling single tenant
#   from set OS_* environment variables (just like python-novaclient).
# - you can choose to heavy-configure it for multiple environments
# - it's configured from simple YAML (I dislike ConfigParser). See nova.yml
# - Nodes can be listed in inventory either by DNS name or IP address based
#   on setting.
#
# * I took few ideas and some code from other pull requests
# - https://github.com/ansible/ansible/pull/8657 by Monty Taylor
# - https://github.com/ansible/ansible/pull/7444 by Carson Gee
#
# If Ansible fails to parse JSON, please run this with --list and observe.
#
# HOW CACHING WORKS:
# Cache of list of servers is kept per combination of (auth_url, region_name,
# project_id). Default max age is 300 seconds. You can set the age per section
# (openstack envrionment) in config.
#
# If you want to build the cache from cron, consider:
# */5 * * * * . /home/tomk/os/openrc.sh && \
#               ANSIBLE_NOVA_CONFIG=/home/tomk/.nova.yml \
#               /home/tomk/ansible/plugins/inventory/nova.py --refresh-cache
#
# HOW IS NOVA INVENTORY CONFIGURED:
# (Note: if you have env vars set from openrc.sh, you can run this without
# writing the config file. Defaults are sane. The values in the config file
# will rewrite the defaults.)
#
# To load configuration from a file, you must have the config file path in
# environment variable ANSIBLE_NOVA_CONFIG.
#
# IN THE CONFIG FILE:
# The keys in the top level dict are names for different OS environments.
# The keys in a dict for OS environment can be:
#  - auth_url
#  - region_name (can be a list)
#  - project_id (can be a list)
#  - username
#  - api_key
#  - service_type
#  - auth_system
#  - prefer_private (connect using private IPs)
#  - cache_max_age (how long to consider cached data. In seconds)
#  - resolve_ips (translate IP addresses to domain names)
#
# If you have a list in region and/or project, all the combinations of
# will be listed.
#
# If you don't have configfile, there will be one cloud section created called
# 'openstack'.
#
# WHAT IS AVAILABLE AS A GROUP FOR ANSIBLE CALLS (how are nodes grouped):
# tenants, regions, clouds (top config section), groups by metadata key (nova
# boot --meta group=<name>).

CONFIG_ENV_VAR_NAME = 'ANSIBLE_NOVA_CONFIG'

NOVA_DEFAULTS = {
    'auth_system': os.environ.get('OS_AUTH_SYSTEM'),
    'service_type': 'compute',
    'username': os.environ.get('OS_USERNAME'),
    'api_key': os.environ.get('OS_PASSWORD'),
    'auth_url': os.environ.get('OS_AUTH_URL'),
    'project_id': os.environ.get('OS_TENANT_NAME'),
    'region_name': os.environ.get('OS_REGION_NAME'),
    'prefer_private': False,
    'version': '2',
    'cache_max_age': 300,
    'resolve_ips': True,
}

DEFAULT_CONFIG_KEY = 'openstack'

CACHE_DIR = '~/.ansible/tmp'

CONFIG = {}

def load_config():
    global CONFIG
    _config_file = os.environ.get(CONFIG_ENV_VAR_NAME)
    if _config_file:
        with open(_config_file) as f:
            CONFIG = yaml.load(f.read())
    if not CONFIG:
        CONFIG = {DEFAULT_CONFIG_KEY: {}}
    for section in CONFIG.values():
        for key in NOVA_DEFAULTS:
            if (key not in section):
                section[key] = NOVA_DEFAULTS[key]

def push(data, key, element):
    ''' Assist in items to a dictionary of lists '''
    if (not element) or (not key):
        return

    if key in data:
        data[key].append(element)
    else:
        data[key] = [element]

def to_safe(word):
    '''
    Converts 'bad' characters in a string to underscores so they can
    be used as Ansible groups
    '''
    return re.sub(r"[^A-Za-z0-9\-]", "_", word)

def get_access_ip(server, prefer_private):
    ''' Find an IP for Ansible SSH for a host. '''
    private = ansible.module_utils.openstack.openstack_find_nova_addresses(
        getattr(server, 'addresses'), 'fixed', 'private')
    public = ansible.module_utils.openstack.openstack_find_nova_addresses(
        getattr(server, 'addresses'), 'floating', 'public')
    if prefer_private:
        return private[0]
    if server.accessIPv4:
        return server.accessIPv4
    if public:
        return public[0]
    else:
        return private[0]

def get_metadata(server):
    ''' Returns dictionary of all host metadata '''
    results = {}
    for key in vars(server):
        # Extract value
        value = getattr(server, key)

        # Generate sanitized key
        key = 'os_' + re.sub(r"[^A-Za-z0-9\-]", "_", key).lower()

        # Att value to instance result (exclude manager class)
        # TODO: maybe use value.__class__ or similar inside of key_name
        if key != 'os_manager':
            results[key] = value
    return results

def get_ssh_user(server, nova_client):
    ''' Try to guess ansible_ssh_user based on image name. '''
    def getNumber(s):
        try:
            return float(s)
        except ValueError:
            return None

    try:
        image_name = nova_client.images.get(server.image['id']).name
        if 'ubuntu' in image_name.lower():
            return 'ubuntu'
        if 'centos' in image_name.lower():
            return 'cloud-user' if getNumber(image_name[image.name.lower().rfind('centos'):].lower().replace('-', ' '
                                    ).replace('_', ' ').split('centos')[1].strip().split()[0]) <= 7 else 'centos'
        if 'debian' in image_name.lower():
            return 'debian'
        if 'coreos' in image_name.lower():
            return 'coreos'
    except:
        pass

def get_nova_client(combination):
    '''
    There is a bit more info in the combination than we need for nova client,
    so we need to create a copy and delete keys that are not relevant.
    '''
    kwargs = dict(combination)
    del kwargs['name']
    del kwargs['prefer_private']
    del kwargs['cache_max_age']
    del kwargs['resolve_ips']
    return novaclient.client.Client(**kwargs)

def merge_update_to_result(result, update):
    '''
    This will merge data from a nova servers.list call (in update) into
    aggregating dict (in result)
    '''
    for host, specs in update['_meta']['hostvars'].items():
        # Can same host be in two differnt listings? I hope not.
        result['_meta']['hostvars'][host] = dict(specs)
    # groups must be copied if not present, otherwise merged
    for group in update:
        if group == '_meta':
            continue
        if group not in result:
            # copy the list over
            result[group] = update[group][:]
        else:
            result[group] = list(set(update[group]) | set(result[group]))

def get_name(ip):
    ''' Gets the shortest domain name for IP address'''
    # I first did this with gethostbyaddr but that did not return all the names
    # Also, this won't work on Windows. But it can be turned of by setting
    # resolve_ips to false
    command = "host %s" % ip
    p = subprocess.Popen(command.split(), stdout=subprocess.PIPE,
                         stderr=subprocess.PIPE)
    stdout, _ = p.communicate()
    if p.returncode != 0:
        return None
    names = []
    for l in stdout.split('\n'):
        if 'domain name pointer' not in l:
            continue
        names.append(l.split()[-1])
    return min(names, key=len)

def get_update(call_params):
    '''
    Fetch host dicts and groups from single nova_client.servers.list call.
    This is called for each element in "cartesian product" of openstack e
    environments, tenants and regions.
    '''
    update = {'_meta': {'hostvars': {}}}
    # Cycle on servers
    nova_client = get_nova_client(call_params)
    for server in nova_client.servers.list():
        access_ip = get_access_ip(server, call_params['prefer_private'])
        access_identifier = access_ip
        if call_params['resolve_ips']:
            dns_name = get_name(access_ip)
            if dns_name:
                access_identifier = dns_name

        # Push to a group for its name. This way we can use the nova name as
        # a target for ansible{-playbook}
        push(update, server.name, access_identifier)

        # Run through each metadata item and add instance to it
        for key, value in server.metadata.iteritems():
            composed_key = to_safe('tag_{0}_{1}'.format(key, value))
            push(update, composed_key, access_identifier)

        # Do special handling of group for backwards compat
        # inventory update
        group = 'undefined'
        if 'group' in server.metadata:
            group = server.metadata['group']
        push(update, group, access_identifier)

        # Add vars to _meta key for performance optimization in
        # Ansible 1.3+
        update['_meta']['hostvars'][access_identifier] = get_metadata(server)

        # guess username based on image name
        ssh_user = get_ssh_user(server, nova_client)
        if ssh_user:
            host_record = update['_meta']['hostvars'][access_identifier]
            host_record['ansible_ssh_user'] = ssh_user

        push(update, call_params['name'], access_identifier)
        push(update, call_params['project_id'], access_identifier)
        if call_params['region_name']:
            push(update, call_params['region_name'], access_identifier)
    return update

def expand_to_product(d):
    '''
    this will transform
    {1: [2, 3, 4], 5: [6, 7]}
    to
    [{1: 2, 5: 6}, {1: 2, 5: 7}, {1: 3, 5: 6}, {1: 3, 5: 7}, {1: 4, 5: 6},
     {1: 4, 5: 7}]
    '''
    return (dict(itertools.izip(d, x)) for x in
            itertools.product(*d.itervalues()))

def get_list_of_kwarg_combinations():
    '''
    This will transfrom
    CONFIG = {'openstack':{version:'2', project_id:['tenant1', tenant2'],...},
              'openstack_dev':{version:'2', project_id:'tenant3',...},
    into
    [{'name':'openstack', version:'2', project_id: 'tenant1', ...},
     {'name':'openstack', version:'2', project_id: 'tenant2', ...},
     {'name':'openstack_dev', version:'2', project_id: 'tenant3', ...}]

    The elements in the returned list can be (with little customization) used
    as **kwargs for nova client.
    '''
    l = []
    for section in CONFIG:
        d = dict(CONFIG[section])
        d['name'] = section
        for key in d:
            # all single elements must become list for the product to work
            if type(d[key]) is not list:
                d[key] = [d[key]]
        for one_call_kwargs in expand_to_product(d):
            l.append(one_call_kwargs)
    return l

def get_cache_filename(call_params):
    '''
    cache filename is
    ~/.ansible/tmp/<md5(auth_url,project_id,region_name)>.nova.json
    '''
    id_to_hash = ("region_name: %(region_name)s, auth_url:%(auth_url)s,"
                  "project_id: %(project_id)s, resolve_ips: %(resolve_ips)s"
                  % call_params)
    return os.path.join(os.path.expanduser(CACHE_DIR),
                        md5.new(id_to_hash).hexdigest() + ".nova.json")

def cache_valid(call_params):
    ''' cache file is specific for (auth_url, project_id, region_name) '''
    cache_path = get_cache_filename(call_params)
    if os.path.isfile(cache_path):
        mod_time = os.path.getmtime(cache_path)
        current_time = time.time()
        if (mod_time + call_params['cache_max_age']) > current_time:
            return True
    return False

def update_cache(call_params):
    fn = get_cache_filename(call_params)
    content = get_update(call_params)
    with open(fn, 'w') as f:
        f.write(json.dumps(content, sort_keys=True, indent=2))

def load_from_cache(call_params):
    fn = get_cache_filename(call_params)
    with open(fn) as f:
        return json.loads(f.read())

def get_args(args_list):
    parser = argparse.ArgumentParser(
        description='Nova dynamic inventory for Ansible')
    g = parser.add_mutually_exclusive_group()
    g.add_argument('--list', action='store_true', default=True,
                   help='List instances (default: True)')
    g.add_argument('--host', action='store',
                   help='Get all the variables about a specific instance')
    parser.add_argument('--refresh-cache', action='store_true', default=False,
                        help=('Force refresh of cache by making API requests to'
                              'Nova (default: False - use cache files)'))
    return parser.parse_args(args_list)

def main(args_list):
    load_config()

    args = get_args(args_list)

    if args.host:
        print(json.dumps({}))
        return 0

    if args.list:

        output = {'_meta': {'hostvars': {}}}

        # we have to deal with every combination of # (cloud, region, project).
        for c in get_list_of_kwarg_combinations():
            if args.refresh_cache or (not cache_valid(c)):
                update_cache(c)
            update = load_from_cache(c)
            merge_update_to_result(output, update)

        print(json.dumps(output, sort_keys=True, indent=2))
    return 0

if __name__ == "__main__":
    sys.exit(main(sys.argv[1:]))
<commit_msg>Fixed typo.<commit_after>#!/usr/bin/env python

# (c) 2012, Marco Vito Moscaritolo <marco@agavee.com>
#     modified by Tomas Karasek <tomas.karasek@digile.fi>
#
# This file is part of Ansible,
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible.  If not, see <http://www.gnu.org/licenses/>.

import sys
import re
import os
import argparse
import subprocess
import yaml
import time
import md5
import itertools
import novaclient.client
import ansible.module_utils.openstack

try:
    import json
except ImportError:
    import simplejson as json

# This is a script getting dynamic inventory from Nova. Features:
# - you can refer to instances by their nova name in ansible{-playbook} calls
# - you can refer to single tenants, regions and openstack environments in
#   ansible{-playbook} calls
# - you can refer to a hostgroup when you pass the arbitrary --meta group=
#   in "nova boot"
# - it caches the state of the cloud
# - it tries to guess ansible_ssh_user based on name of image
#   ('\cubuntu' -> 'ubuntu', '\ccentos' -> 'cloud-user', ...)
# - allows to access machines by their private ip *
# - it will work with no additional configuration, just handling single tenant
#   from set OS_* environment variables (just like python-novaclient).
# - you can choose to heavy-configure it for multiple environments
# - it's configured from simple YAML (I dislike ConfigParser). See nova.yml
# - Nodes can be listed in inventory either by DNS name or IP address based
#   on setting.
#
# * I took few ideas and some code from other pull requests
# - https://github.com/ansible/ansible/pull/8657 by Monty Taylor
# - https://github.com/ansible/ansible/pull/7444 by Carson Gee
#
# If Ansible fails to parse JSON, please run this with --list and observe.
#
# HOW CACHING WORKS:
# Cache of list of servers is kept per combination of (auth_url, region_name,
# project_id). Default max age is 300 seconds. You can set the age per section
# (openstack envrionment) in config.
#
# If you want to build the cache from cron, consider:
# */5 * * * * . /home/tomk/os/openrc.sh && \
#               ANSIBLE_NOVA_CONFIG=/home/tomk/.nova.yml \
#               /home/tomk/ansible/plugins/inventory/nova.py --refresh-cache
#
# HOW IS NOVA INVENTORY CONFIGURED:
# (Note: if you have env vars set from openrc.sh, you can run this without
# writing the config file. Defaults are sane. The values in the config file
# will rewrite the defaults.)
#
# To load configuration from a file, you must have the config file path in
# environment variable ANSIBLE_NOVA_CONFIG.
#
# IN THE CONFIG FILE:
# The keys in the top level dict are names for different OS environments.
# The keys in a dict for OS environment can be:
#  - auth_url
#  - region_name (can be a list)
#  - project_id (can be a list)
#  - username
#  - api_key
#  - service_type
#  - auth_system
#  - prefer_private (connect using private IPs)
#  - cache_max_age (how long to consider cached data. In seconds)
#  - resolve_ips (translate IP addresses to domain names)
#
# If you have a list in region and/or project, all the combinations of
# will be listed.
#
# If you don't have configfile, there will be one cloud section created called
# 'openstack'.
#
# WHAT IS AVAILABLE AS A GROUP FOR ANSIBLE CALLS (how are nodes grouped):
# tenants, regions, clouds (top config section), groups by metadata key (nova
# boot --meta group=<name>).

CONFIG_ENV_VAR_NAME = 'ANSIBLE_NOVA_CONFIG'

NOVA_DEFAULTS = {
    'auth_system': os.environ.get('OS_AUTH_SYSTEM'),
    'service_type': 'compute',
    'username': os.environ.get('OS_USERNAME'),
    'api_key': os.environ.get('OS_PASSWORD'),
    'auth_url': os.environ.get('OS_AUTH_URL'),
    'project_id': os.environ.get('OS_TENANT_NAME'),
    'region_name': os.environ.get('OS_REGION_NAME'),
    'prefer_private': False,
    'version': '2',
    'cache_max_age': 300,
    'resolve_ips': True,
}

DEFAULT_CONFIG_KEY = 'openstack'

CACHE_DIR = '~/.ansible/tmp'

CONFIG = {}

def load_config():
    global CONFIG
    _config_file = os.environ.get(CONFIG_ENV_VAR_NAME)
    if _config_file:
        with open(_config_file) as f:
            CONFIG = yaml.load(f.read())
    if not CONFIG:
        CONFIG = {DEFAULT_CONFIG_KEY: {}}
    for section in CONFIG.values():
        for key in NOVA_DEFAULTS:
            if (key not in section):
                section[key] = NOVA_DEFAULTS[key]

def push(data, key, element):
    ''' Assist in items to a dictionary of lists '''
    if (not element) or (not key):
        return

    if key in data:
        data[key].append(element)
    else:
        data[key] = [element]

def to_safe(word):
    '''
    Converts 'bad' characters in a string to underscores so they can
    be used as Ansible groups
    '''
    return re.sub(r"[^A-Za-z0-9\-]", "_", word)

def get_access_ip(server, prefer_private):
    ''' Find an IP for Ansible SSH for a host. '''
    private = ansible.module_utils.openstack.openstack_find_nova_addresses(
        getattr(server, 'addresses'), 'fixed', 'private')
    public = ansible.module_utils.openstack.openstack_find_nova_addresses(
        getattr(server, 'addresses'), 'floating', 'public')
    if prefer_private:
        return private[0]
    if server.accessIPv4:
        return server.accessIPv4
    if public:
        return public[0]
    else:
        return private[0]

def get_metadata(server):
    ''' Returns dictionary of all host metadata '''
    results = {}
    for key in vars(server):
        # Extract value
        value = getattr(server, key)

        # Generate sanitized key
        key = 'os_' + re.sub(r"[^A-Za-z0-9\-]", "_", key).lower()

        # Att value to instance result (exclude manager class)
        # TODO: maybe use value.__class__ or similar inside of key_name
        if key != 'os_manager':
            results[key] = value
    return results

def get_ssh_user(server, nova_client):
    ''' Try to guess ansible_ssh_user based on image name. '''
    def getNumber(s):
        try:
            return float(s)
        except ValueError:
            return None

    try:
        image_name = nova_client.images.get(server.image['id']).name
        if 'ubuntu' in image_name.lower():
            return 'ubuntu'
        if 'centos' in image_name.lower():
            return 'cloud-user' if getNumber(image_name[image_name.lower().rfind('centos'):].lower().replace('-', ' '
                                    ).replace('_', ' ').split('centos')[1].strip().split()[0]) <= 7 else 'centos'
        if 'debian' in image_name.lower():
            return 'debian'
        if 'coreos' in image_name.lower():
            return 'coreos'
    except:
        pass

def get_nova_client(combination):
    '''
    There is a bit more info in the combination than we need for nova client,
    so we need to create a copy and delete keys that are not relevant.
    '''
    kwargs = dict(combination)
    del kwargs['name']
    del kwargs['prefer_private']
    del kwargs['cache_max_age']
    del kwargs['resolve_ips']
    return novaclient.client.Client(**kwargs)

def merge_update_to_result(result, update):
    '''
    This will merge data from a nova servers.list call (in update) into
    aggregating dict (in result)
    '''
    for host, specs in update['_meta']['hostvars'].items():
        # Can same host be in two differnt listings? I hope not.
        result['_meta']['hostvars'][host] = dict(specs)
    # groups must be copied if not present, otherwise merged
    for group in update:
        if group == '_meta':
            continue
        if group not in result:
            # copy the list over
            result[group] = update[group][:]
        else:
            result[group] = list(set(update[group]) | set(result[group]))

def get_name(ip):
    ''' Gets the shortest domain name for IP address'''
    # I first did this with gethostbyaddr but that did not return all the names
    # Also, this won't work on Windows. But it can be turned of by setting
    # resolve_ips to false
    command = "host %s" % ip
    p = subprocess.Popen(command.split(), stdout=subprocess.PIPE,
                         stderr=subprocess.PIPE)
    stdout, _ = p.communicate()
    if p.returncode != 0:
        return None
    names = []
    for l in stdout.split('\n'):
        if 'domain name pointer' not in l:
            continue
        names.append(l.split()[-1])
    return min(names, key=len)

def get_update(call_params):
    '''
    Fetch host dicts and groups from single nova_client.servers.list call.
    This is called for each element in "cartesian product" of openstack e
    environments, tenants and regions.
    '''
    update = {'_meta': {'hostvars': {}}}
    # Cycle on servers
    nova_client = get_nova_client(call_params)
    for server in nova_client.servers.list():
        access_ip = get_access_ip(server, call_params['prefer_private'])
        access_identifier = access_ip
        if call_params['resolve_ips']:
            dns_name = get_name(access_ip)
            if dns_name:
                access_identifier = dns_name

        # Push to a group for its name. This way we can use the nova name as
        # a target for ansible{-playbook}
        push(update, server.name, access_identifier)

        # Run through each metadata item and add instance to it
        for key, value in server.metadata.iteritems():
            composed_key = to_safe('tag_{0}_{1}'.format(key, value))
            push(update, composed_key, access_identifier)

        # Do special handling of group for backwards compat
        # inventory update
        group = 'undefined'
        if 'group' in server.metadata:
            group = server.metadata['group']
        push(update, group, access_identifier)

        # Add vars to _meta key for performance optimization in
        # Ansible 1.3+
        update['_meta']['hostvars'][access_identifier] = get_metadata(server)

        # guess username based on image name
        ssh_user = get_ssh_user(server, nova_client)
        if ssh_user:
            host_record = update['_meta']['hostvars'][access_identifier]
            host_record['ansible_ssh_user'] = ssh_user

        push(update, call_params['name'], access_identifier)
        push(update, call_params['project_id'], access_identifier)
        if call_params['region_name']:
            push(update, call_params['region_name'], access_identifier)
    return update

def expand_to_product(d):
    '''
    this will transform
    {1: [2, 3, 4], 5: [6, 7]}
    to
    [{1: 2, 5: 6}, {1: 2, 5: 7}, {1: 3, 5: 6}, {1: 3, 5: 7}, {1: 4, 5: 6},
     {1: 4, 5: 7}]
    '''
    return (dict(itertools.izip(d, x)) for x in
            itertools.product(*d.itervalues()))

def get_list_of_kwarg_combinations():
    '''
    This will transfrom
    CONFIG = {'openstack':{version:'2', project_id:['tenant1', tenant2'],...},
              'openstack_dev':{version:'2', project_id:'tenant3',...},
    into
    [{'name':'openstack', version:'2', project_id: 'tenant1', ...},
     {'name':'openstack', version:'2', project_id: 'tenant2', ...},
     {'name':'openstack_dev', version:'2', project_id: 'tenant3', ...}]

    The elements in the returned list can be (with little customization) used
    as **kwargs for nova client.
    '''
    l = []
    for section in CONFIG:
        d = dict(CONFIG[section])
        d['name'] = section
        for key in d:
            # all single elements must become list for the product to work
            if type(d[key]) is not list:
                d[key] = [d[key]]
        for one_call_kwargs in expand_to_product(d):
            l.append(one_call_kwargs)
    return l

def get_cache_filename(call_params):
    '''
    cache filename is
    ~/.ansible/tmp/<md5(auth_url,project_id,region_name)>.nova.json
    '''
    id_to_hash = ("region_name: %(region_name)s, auth_url:%(auth_url)s,"
                  "project_id: %(project_id)s, resolve_ips: %(resolve_ips)s"
                  % call_params)
    return os.path.join(os.path.expanduser(CACHE_DIR),
                        md5.new(id_to_hash).hexdigest() + ".nova.json")

def cache_valid(call_params):
    ''' cache file is specific for (auth_url, project_id, region_name) '''
    cache_path = get_cache_filename(call_params)
    if os.path.isfile(cache_path):
        mod_time = os.path.getmtime(cache_path)
        current_time = time.time()
        if (mod_time + call_params['cache_max_age']) > current_time:
            return True
    return False

def update_cache(call_params):
    fn = get_cache_filename(call_params)
    content = get_update(call_params)
    with open(fn, 'w') as f:
        f.write(json.dumps(content, sort_keys=True, indent=2))

def load_from_cache(call_params):
    fn = get_cache_filename(call_params)
    with open(fn) as f:
        return json.loads(f.read())

def get_args(args_list):
    parser = argparse.ArgumentParser(
        description='Nova dynamic inventory for Ansible')
    g = parser.add_mutually_exclusive_group()
    g.add_argument('--list', action='store_true', default=True,
                   help='List instances (default: True)')
    g.add_argument('--host', action='store',
                   help='Get all the variables about a specific instance')
    parser.add_argument('--refresh-cache', action='store_true', default=False,
                        help=('Force refresh of cache by making API requests to'
                              'Nova (default: False - use cache files)'))
    return parser.parse_args(args_list)

def main(args_list):
    load_config()

    args = get_args(args_list)

    if args.host:
        print(json.dumps({}))
        return 0

    if args.list:

        output = {'_meta': {'hostvars': {}}}

        # we have to deal with every combination of # (cloud, region, project).
        for c in get_list_of_kwarg_combinations():
            if args.refresh_cache or (not cache_valid(c)):
                update_cache(c)
            update = load_from_cache(c)
            merge_update_to_result(output, update)

        print(json.dumps(output, sort_keys=True, indent=2))
    return 0

if __name__ == "__main__":
    sys.exit(main(sys.argv[1:]))
<|endoftext|>


<commit_before>"""
spaceshooter.py
Author: David Wilson
Credit: Mr. Dennison ("Space War Source Code" and "Advanced Graphics with Classes")

Assignment:
Write and submit a program that implements the spacewar game:
https://github.com/HHS-IntroProgramming/Spacewar
"""

from ggame import App, Sprite, ImageAsset, Frame
from math import sqrt, sin, cos, radians, degrees, pi
from random import randint

SCREEN_WIDTH = 1000
SCREEN_HEIGHT = 600
SCREEN_DIAG = sqrt(SCREEN_WIDTH**2+SCREEN_HEIGHT**2)

class StarBack(Sprite):

    asset = ImageAsset("images/starfield.jpg", Frame(0,0,SCREEN_WIDTH/2,SCREEN_HEIGHT/2))

    def __init__(self, position):
        super().__init__(StarBack.asset, position)
        self.scale = 2

class SpaceShip(Sprite):

    def __init__(self, asset, position):
        super().__init__(asset, position)
        self.rotSpd = 0.1
        self.fxcenter = self.fycenter = 0.5

    def shoot(self, event):
        Bullet((self.x,self.y))

class Player(SpaceShip):

    asset = ImageAsset("images/four_spaceship_by_albertov_with_thrust.png",
        Frame(0,0,85,125), 4, 'vertical')

    def __init__(self, position):
        super().__init__(Player.asset, position)
        self.thrust = 0
        self.thrustframe = 0
        SpaceGame.listenKeyEvent("keydown", "right arrow", self.rotateRight)
        SpaceGame.listenKeyEvent("keydown", "left arrow", self.rotateLeft)
        SpaceGame.listenKeyEvent("keydown", "up arrow", self.thrustOn)
        SpaceGame.listenKeyEvent("keyup", "up arrow", self.thrustOff)
        SpaceGame.listenKeyEvent("keydown", "space", self.shoot)

    def rotateRight(self, event):
        self.rotation -= self.rotSpd

    def rotateLeft(self, event):
        self.rotation += self.rotSpd

    def thrustOn(self, event):
        self.thrust = 1
        self.x -= 5*sin(self.rotation)
        self.y -= 5*cos(self.rotation)

    def thrustOff(self, event):
        self.thrust = 0

    def step(self):
        if self.thrust == 1:
            if self.thrustframe == 3:
                self.thrustframe = 1
            else:
                self.thrustframe += 1
            self.setImage(self.thrustframe)
        else:
            self.setImage(0)

class Enemy(Sprite):

    asset = ImageAsset("images/four_spaceship_by_albertov_with_thrust.png",
        Frame(227,0,292-227,125), 4, 'vertical')

    def __init__(self, position):
        super().__init__(Enemy.asset, position)
        self.speed = 1
        self.rotation = randint(0,1000)/500*pi
        self.velx = -1*self.speed*sin(self.rotation)
        self.vely = -1*self.speed*cos(self.rotation)
        self.fxcenter = self.fycenter = 0.5
        self.dist = 0
        self.frame = 1

    def velocitySet(self):
        self.velx = -1*self.speed*sin(self.rotation)
        self.vely = -1*self.speed*cos(self.rotation)

    def changeDirec(self):
        self.rotation = randint(0,1000)/500*pi
        self.velocitySet()
        self.dist = 0

    def step(self):
        collides = self.collidingWithSprites(Bullet)
        if len(collides) > 0:
            print("hello")
            self.visibile = False
        self.x += self.velx
        self.y += self.vely
        if self.frame == 3:
            self.frame = 1
        else:
            self.frame += 1
        self.setImage(self.frame)
        if self.x > SCREEN_WIDTH or self.x < 0 or self.y > SCREEN_HEIGHT or self.y < 0:
            self.rotation += pi
            self.velocitySet()
        elif self.dist > SCREEN_DIAG/5 and randint(0,20) == 0:
            self.changeDirec()
        self.dist += self.speed

class Bullet(Sprite):

    asset = ImageAsset("images/blast.png", Frame(0,0,8,8))

    def __init__(self, position):
        super().__init__(Bullet.asset, position)
        self.fxcenter = self.fycenter = 0.5
        for x in SpaceGame.getSpritesbyClass(Player):
            self.rotation = x.rotation
        self.velx = 5*sin(self.rotation)
        self.vely = 5*cos(self.rotation)

    def step(self):
        if 0 <= self.x <= SCREEN_WIDTH and 0 <= self.y <= SCREEN_HEIGHT:
            self.velx = -5*sin(self.rotation)
            self.vely = -5*cos(self.rotation)
            self.x += self.velx
            self.y += self.vely
        else:
            self.destroy()

class SpaceGame(App):

    def __init__(self):
        super().__init__()
        StarBack((0,0))
        Player((SCREEN_WIDTH/2,SCREEN_HEIGHT/2))
        Enemy((100,100))
        self.step()

    def step(self):
        for x in self.getSpritesbyClass(Player):
            x.step()
        for x in self.getSpritesbyClass(Bullet):
            x.step()
        for x in self.getSpritesbyClass(Enemy):
            x.step()

myapp = SpaceGame()
myapp.run()
<commit_msg>Updated from Brython Server: 3/23/2016 1:29:34 PM<commit_after>"""
spaceshooter.py
Author: David Wilson
Credit: Mr. Dennison ("Space War Source Code" and "Advanced Graphics with Classes")

Assignment:
Write and submit a program that implements the spacewar game:
https://github.com/HHS-IntroProgramming/Spacewar
"""

from ggame import App, Sprite, ImageAsset, Frame
from math import sqrt, sin, cos, radians, degrees, pi
from random import randint

SCREEN_WIDTH = 1000
SCREEN_HEIGHT = 600
SCREEN_DIAG = sqrt(SCREEN_WIDTH**2+SCREEN_HEIGHT**2)

class StarBack(Sprite):

    asset = ImageAsset("images/starfield.jpg", Frame(0,0,SCREEN_WIDTH/2,SCREEN_HEIGHT/2))

    def __init__(self, position):
        super().__init__(StarBack.asset, position)
        self.scale = 2

class SpaceShip(Sprite):

    def __init__(self, asset, position):
        super().__init__(asset, position)
        self.rotSpd = 0.1
        self.fxcenter = self.fycenter = 0.5

    def shoot(self, event):
        Bullet((self.x,self.y))

class Player(SpaceShip):

    asset = ImageAsset("images/four_spaceship_by_albertov_with_thrust.png",
        Frame(0,0,85,125), 4, 'vertical')

    def __init__(self, position):
        super().__init__(Player.asset, position)
        self.thrust = 0
        self.thrustframe = 0
        SpaceGame.listenKeyEvent("keydown", "right arrow", self.rotateRight)
        SpaceGame.listenKeyEvent("keydown", "left arrow", self.rotateLeft)
        SpaceGame.listenKeyEvent("keydown", "up arrow", self.thrustOn)
        SpaceGame.listenKeyEvent("keyup", "up arrow", self.thrustOff)
        SpaceGame.listenKeyEvent("keydown", "space", self.shoot)

    def rotateRight(self, event):
        self.rotation -= self.rotSpd

    def rotateLeft(self, event):
        self.rotation += self.rotSpd

    def thrustOn(self, event):
        self.thrust = 1
        self.x -= 5*sin(self.rotation)
        self.y -= 5*cos(self.rotation)

    def thrustOff(self, event):
        self.thrust = 0

    def step(self):
        if self.thrust == 1:
            if self.thrustframe == 3:
                self.thrustframe = 1
            else:
                self.thrustframe += 1
            self.setImage(self.thrustframe)
        else:
            self.setImage(0)

class Enemy(Sprite):

    asset = ImageAsset("images/four_spaceship_by_albertov_with_thrust.png",
        Frame(227,0,292-227,125), 4, 'vertical')

    def __init__(self, position):
        super().__init__(Enemy.asset, position)
        self.speed = 1
        self.rotation = randint(0,1000)/500*pi
        self.velx = -1*self.speed*sin(self.rotation)
        self.vely = -1*self.speed*cos(self.rotation)
        self.fxcenter = self.fycenter = 0.5
        self.dist = 0
        self.frame = 1

    def velocitySet(self):
        self.velx = -1*self.speed*sin(self.rotation)
        self.vely = -1*self.speed*cos(self.rotation)

    def changeDirec(self):
        self.rotation = randint(0,1000)/500*pi
        self.velocitySet()
        self.dist = 0

    def step(self):
        collides = self.collidingWithSprites(Bullet)
        if len(collides) > 0:
            print("hello")
            self.destroy()
        self.x += self.velx
        self.y += self.vely
        if self.frame == 3:
            self.frame = 1
        else:
            self.frame += 1
        self.setImage(self.frame)
        if self.x > SCREEN_WIDTH or self.x < 0 or self.y > SCREEN_HEIGHT or self.y < 0:
            self.rotation += pi
            self.velocitySet()
        elif self.dist > SCREEN_DIAG/5 and randint(0,20) == 0:
            self.changeDirec()
        self.dist += self.speed

class Bullet(Sprite):

    asset = ImageAsset("images/blast.png", Frame(0,0,8,8))

    def __init__(self, position):
        super().__init__(Bullet.asset, position)
        self.fxcenter = self.fycenter = 0.5
        for x in SpaceGame.getSpritesbyClass(Player):
            self.rotation = x.rotation
        self.velx = 5*sin(self.rotation)
        self.vely = 5*cos(self.rotation)

    def step(self):
        if 0 <= self.x <= SCREEN_WIDTH and 0 <= self.y <= SCREEN_HEIGHT:
            self.velx = -5*sin(self.rotation)
            self.vely = -5*cos(self.rotation)
            self.x += self.velx
            self.y += self.vely
        else:
            self.destroy()

class SpaceGame(App):

    def __init__(self):
        super().__init__()
        StarBack((0,0))
        Player((SCREEN_WIDTH/2,SCREEN_HEIGHT/2))
        Enemy((100,100))
        self.step()

    def step(self):
        for x in self.getSpritesbyClass(Player):
            x.step()
        for x in self.getSpritesbyClass(Bullet):
            x.step()
        for x in self.getSpritesbyClass(Enemy):
            x.step()

myapp = SpaceGame()
myapp.run()
<|endoftext|>


<commit_before>"""
spaceshooter.py
Author: Payton
Credit: Morgan, Avery, Daniel

Assignment:
Write and submit a program that implements the spacewar game:
https://github.com/HHS-IntroProgramming/Spacewar
"""

"""
from ggame import App, RectangleAsset, ImageAsset, Sprite, LineStyle, Color, Frame
from ggame import App, Sprite, ImageAsset, Frame, SoundAsset, Sound, TextAsset, Color
import math
from time import time

class Stars(Sprite):

    asset = ImageAsset("images/starfield.jpg")
    width = 512
    height = 512

    def __init__(self, position):
        super().__init__(Stars.asset, position)

class Sun(Sprite):

    asset = ImageAsset("images/sun.png")
    width = 80
    height = 76
"""

from ggame import App, Sprite, ImageAsset, Frame
from ggame import SoundAsset, Sound, TextAsset, Color
import math
from time import time

SCREEN_WIDTH = 900
SCREEN_HEIGHT = 650

class SpaceShip(Sprite):
    """
    Animated space ship
    """
    asset = ImageAsset("images/four_spaceship_by_albertov_with_thrust.png",
        Frame(227,0,292-227,125), 4, 'vertical')

    def __init__(self, position):
        super().__init__(SpaceShip.asset, position)
        self.vx = 1
        self.vy = 1
        self.vr = 0.01
        self.thrust = 0
        self.thrustframe = 1
        SpaceGame.listenKeyEvent("keydown", "space", self.thrustOn)
        SpaceGame.listenKeyEvent("keyup", "space", self.thrustOff)
        self.fxcenter = self.fycenter = 0.5

    def step(self):
        self.x += self.vx
        self.y += self.vy
        self.rotation += self.vr
        if self.thrust == 1:
            self.setImage(self.thrustframe)
            self.thrustframe += 1
            if self.thrustframe == 4:
                self.thrustframe = 1
        else:
            self.setImage(0)

    def thrustOn(self, event):
        self.thrust = 1

    def thrustOff(self, event):
        self.thrust = 0

class SpaceGame(App):
    """
    Tutorial4 space game example.
    """
    def __init__(self, width, height):
        super().__init__(width, height)
        bg_asset = ImageAsset("images/starfield.jpg")
        bg = Sprite(bg_asset, (0,0))
        bg = Sprite(bg_asset, (0,512))
        bg = Sprite(bg_asset, (512,0))
        bg = Sprite(bg_asset, (512,512))
        sn_asset = ImageAsset("images/sun.png")
        sn = Sprite(sn_asset, (450,325))
        SpaceShip((100,100))
        SpaceShip((150,150))
        SpaceShip((200,50))

    def step(self):
        for ship in self.getSpritesbyClass(SpaceShip):
            ship.step()

myapp = SpaceGame(SCREEN_WIDTH, SCREEN_HEIGHT)
myapp.run()<commit_msg>Updated from Brython Server: 3/22/2016 2:16:23 PM<commit_after>"""
spaceshooter.py
Author: Payton
Credit: Morgan, Avery, Daniel

Assignment:
Write and submit a program that implements the spacewar game:
https://github.com/HHS-IntroProgramming/Spacewar
"""

"""
from ggame import App, RectangleAsset, ImageAsset, Sprite, LineStyle, Color, Frame
from ggame import App, Sprite, ImageAsset, Frame, SoundAsset, Sound, TextAsset, Color
import math
from time import time

class Stars(Sprite):

    asset = ImageAsset("images/starfield.jpg")
    width = 512
    height = 512

    def __init__(self, position):
        super().__init__(Stars.asset, position)

class Sun(Sprite):

    asset = ImageAsset("images/sun.png")
    width = 80
    height = 76
"""

from ggame import App, Sprite, ImageAsset, Frame
from ggame import SoundAsset, Sound, TextAsset, Color
import math
from time import time

SCREEN_WIDTH = 900
SCREEN_HEIGHT = 650

class SpaceShip(Sprite):
    """
    Animated space ship
    """
    asset = ImageAsset("images/four_spaceship_by_albertov_with_thrust.png",
        Frame(227,0,292-227,125), 4, 'vertical')

    def __init__(self, position):
        super().__init__(SpaceShip.asset, position)
        self.vx = 1
        self.vy = 1
        self.vr = 0.01
        self.thrust = 0
        self.thrustframe = 1
        SpaceGame.listenKeyEvent("keydown", "space", self.thrustOn)
        SpaceGame.listenKeyEvent("keyup", "space", self.thrustOff)
        self.fxcenter = self.fycenter = 0.5

    def step(self):
        self.x += self.vx
        self.y += self.vy
        self.rotation += self.vr
        if self.thrust == 1:
            self.setImage(self.thrustframe)
            self.thrustframe += 1
            if self.thrustframe == 4:
                self.thrustframe = 1
        else:
            self.setImage(0)

    def thrustOn(self, event):
        self.thrust = 1

    def thrustOff(self, event):
        self.thrust = 0

class SpaceGame(App):
    """
    Tutorial4 space game example.
    """
    def __init__(self, width, height):
        super().__init__(width, height)
        bg_asset = ImageAsset("images/starfield.jpg")
        bg = Sprite(bg_asset, (0,0))
        bg = Sprite(bg_asset, (0,512))
        bg = Sprite(bg_asset, (512,0))
        bg = Sprite(bg_asset, (512,512))
        sn_asset = ImageAsset("images/sun.png")
        sn = Sprite(sn_asset, (400,325))
        SpaceShip((100,100))
        SpaceShip((150,150))
        SpaceShip((200,50))

    def step(self):
        for ship in self.getSpritesbyClass(SpaceShip):
            ship.step()

myapp = SpaceGame(SCREEN_WIDTH, SCREEN_HEIGHT)
myapp.run()<|endoftext|>


<commit_before>"""
spaceshooter.py
Author: Payton
Credit: Morgan, Avery, Daniel

Assignment:
Write and submit a program that implements the ControlDwon game:
https://github.com/HHS-IntroProgramming/Spacewar
"""
from ggame import App, Sprite, ImageAsset, Frame
from ggame import SoundAsset, Sound, TextAsset, Color
import math
from time import time

SCREEN_WIDTH = 900
SCREEN_HEIGHT = 650

class SpaceShip(Sprite):
    """
    Animated space ship
    """
    asset = ImageAsset("images/four_spaceship_by_albertov_with_thrust.png",
        Frame(227,0,292-227,125), 4, 'vertical')

    def __init__(self, position):
        super().__init__(SpaceShip.asset, position)
        self.vx = 0
        self.vy = 0
        self.vr = 0.0
        self.thrust = 0
        self.thrustframe = 1
        left_location = 1
        SpaceGame.listenKeyEvent("keydown", "w", self.thrustOn)
        SpaceGame.listenKeyEvent("keyup", "w", self.thrustOff)
        SpaceGame.listenKeyEvent("keydown", "a", self.thrustOn)
        SpaceGame.listenKeyEvent("keyup", "a", self.thrustOff)
        SpaceGame.listenKeyEvent("keyup", "d", self.thrustOff)
        SpaceGame.listenKeyEvent("keydown", "d", self.thrustOn)
        SpaceGame.listenKeyEvent("keyup", "s", self.thrustOff)
        SpaceGame.listenKeyEvent("keydown", "s", self.thrustOn)
        right_location = 2
        SpaceGame.listenKeyEvent("keyup", "up arrow", self.thrustOff)
        SpaceGame.listenKeyEvent("keydown", "up arrow", self.thrustOn)
        SpaceGame.listenKeyEvent("keyup", "down arrow", self.thrustOff)
        SpaceGame.listenKeyEvent("keydown", "down arrow", self.thrustOn)
        SpaceGame.listenKeyEvent("keyup", "left arrow", self.thrustOff)
        SpaceGame.listenKeyEvent("keydown", "left arrow", self.thrustOn)
        SpaceGame.listenKeyEvent("keyup", "right arrow", self.thrustOff)
        SpaceGame.listenKeyEvent("keydown", "right arrow", self.thrustOn)
        self.fxcenter = self.fycenter = 0.5

    def step(self):
        self.x += self.vx
        self.y += self.vy
        self.rotation += self.vr
        if self.thrust == 1:
            self.setImage(self.thrustframe)
            self.thrustframe += 1
            if self.thrustframe == 4:
                self.thrustframe = 1
        else:
            self.setImage(0)

    def thrustOn(self, event):
        self.thrust = 1

    def thrustOff(self, event):
        self.thrust = 0

    #def thrustdecel(self, event):
        #self.thrust = 0.5

class SpaceGame(App):
    """
    Tutorial4 space game example.
    """
    def __init__(self, width, height):
        super().__init__(width, height)
        bg_asset = ImageAsset("images/starfield.jpg")
        bg = Sprite(bg_asset, (0,0))
        bg = Sprite(bg_asset, (0,512))
        bg = Sprite(bg_asset, (512,0))
        bg = Sprite(bg_asset, (512,512))
        sn_asset = ImageAsset("images/sun.png")
        sn = Sprite(sn_asset, (400,300))
        left_location = 1
        SpaceShip((100,100))
        right_location = 2
        SpaceShip((500,500))

    def step(self):
        for ship in self.getSpritesbyClass(SpaceShip):
            ship.step()

myapp = SpaceGame(SCREEN_WIDTH, SCREEN_HEIGHT)
myapp.run()<commit_msg>Updated from Brython Server: 3/22/2016 2:48:36 PM<commit_after>"""
spaceshooter.py
Author: Payton
Credit: Morgan, Avery, Daniel

Assignment:
Write and submit a program that implements the ControlDwon game:
https://github.com/HHS-IntroProgramming/Spacewar
"""
from ggame import App, Sprite, ImageAsset, Frame
from ggame import SoundAsset, Sound, TextAsset, Color
import math
from time import time

SCREEN_WIDTH = 900
SCREEN_HEIGHT = 650

class SpaceShip(Sprite):
    """
    Animated space ship
    """
    asset = ImageAsset("images/four_spaceship_by_albertov_with_thrust.png",
        Frame(227,0,292-227,125), 4, 'vertical')

    def __init__(self, position):
        super().__init__(SpaceShip.asset, position)
        self.vx = 0
        self.vy = 0
        self.vr = 0.0
        self.thrust = 0
        self.thrustframe = 1
        left_location = 1
        SpaceGame.listenKeyEvent("keydown", "w", self.thrustOn)
        SpaceGame.listenKeyEvent("keyup", "w", self.thrustOff)
        SpaceGame.listenKeyEvent("keydown", "a", self.rotation)
        SpaceGame.listenKeyEvent("keyup", "a", self.rotation)
        SpaceGame.listenKeyEvent("keyup", "d", self.rotation)
        SpaceGame.listenKeyEvent("keydown", "d", self.rotation)
        SpaceGame.listenKeyEvent("keyup", "s", self.rotation)
        SpaceGame.listenKeyEvent("keydown", "s", self.rotation)
        right_location = 2
        SpaceGame.listenKeyEvent("keyup", "up arrow", self.thrustOff)
        SpaceGame.listenKeyEvent("keydown", "up arrow", self.thrustOn)
        SpaceGame.listenKeyEvent("keyup", "down arrow", self.thrustOff)
        SpaceGame.listenKeyEvent("keydown", "down arrow", self.thrustOn)
        SpaceGame.listenKeyEvent("keyup", "left arrow", self.thrustOff)
        SpaceGame.listenKeyEvent("keydown", "left arrow", self.thrustOn)
        SpaceGame.listenKeyEvent("keyup", "right arrow", self.thrustOff)
        SpaceGame.listenKeyEvent("keydown", "right arrow", self.thrustOn)
        self.fxcenter = self.fycenter = 0.5

    def step(self):
        self.x += self.vx
        self.y += self.vy
        self.rotation += self.vr
        if self.thrust == 1:
            self.setImage(self.thrustframe)
            self.thrustframe += 1
            if self.thrustframe == 4:
                self.thrustframe = 1
        else:
            self.setImage(0)

    def thrustOn(self, event):
        self.thrust = 1

    def thrustOff(self, event):
        self.thrust = 0

    #def thrustdecel(self, event):
        #self.thrust = 0.5

class SpaceGame(App):
    """
    Tutorial4 space game example.
    """
    def __init__(self, width, height):
        super().__init__(width, height)
        bg_asset = ImageAsset("images/starfield.jpg")
        bg = Sprite(bg_asset, (0,0))
        bg = Sprite(bg_asset, (0,512))
        bg = Sprite(bg_asset, (512,0))
        bg = Sprite(bg_asset, (512,512))
        sn_asset = ImageAsset("images/sun.png")
        sn = Sprite(sn_asset, (400,300))
        left_location = 1
        SpaceShip((100,100))
        right_location = 2
        SpaceShip((500,500))

    def step(self):
        for ship in self.getSpritesbyClass(SpaceShip):
            ship.step()

myapp = SpaceGame(SCREEN_WIDTH, SCREEN_HEIGHT)
myapp.run()<|endoftext|>