import operator
import sqlalchemy as sa
from sqlalchemy_utils import primary_keys, identity
from .utils import tx_column_name, end_tx_column_name

def eq(tuple_):
    return tuple_[0] == tuple_[1]

def parent_identity(obj_or_class):
    return tuple(
        getattr(obj_or_class, column.name)
        for column in primary_keys(obj_or_class)
        if column.name != tx_column_name(obj_or_class)
    )

class VersionObjectFetcher(object):
    def __init__(self, manager):
        self.manager = manager

    def previous(self, obj):
        """
        Returns the previous version relative to this version in the version
        history. If current version is the first version this method returns
        None.
        """
        return self.previous_query(obj).first()

    def index(self, obj):
        """
        Return the index of this version in the version history.
        """
        session = sa.orm.object_session(obj)
        return session.execute(self._index_query(obj)).fetchone()[0]

    def next(self, obj):
        """
        Returns the next version relative to this version in the version
        history. If current version is the last version this method returns
        None.
        """
        return self.next_query(obj).first()

    def parent_identity_correlation(self, obj):
        return map(
            eq,
            zip(
                parent_identity(obj.__class__),
                parent_identity(obj)
            )
        )

    def _transaction_id_subquery(self, obj, next_or_prev='next'):
        if next_or_prev == 'next':
            op = operator.gt
            func = sa.func.min
        else:
            op = operator.lt
            func = sa.func.max

        alias = sa.orm.aliased(obj)
        query = (
            sa.select(
                [func(
                    getattr(alias, tx_column_name(obj))
                )],
                from_obj=[alias.__table__]
            )
            .where(
                sa.and_(
                    op(
                        getattr(alias, tx_column_name(obj)),
                        getattr(obj, tx_column_name(obj))
                    ),
                    *map(eq, zip(parent_identity(alias), parent_identity(obj)))
                )
            )
            .correlate(alias.__table__)
        )
        return query

    def _next_prev_query(self, obj, next_or_prev='next'):
        session = sa.orm.object_session(obj)

        return (
            session.query(obj.__class__)
            .filter(
                sa.and_(
                    getattr(
                        obj.__class__,
                        tx_column_name(obj)
                    )
                    ==
                    self._transaction_id_subquery(
                        obj, next_or_prev=next_or_prev
                    ),
                    *self.parent_identity_correlation(obj)
                )
            )
        )

    def _index_query(self, obj):
        """
        Returns the query needed for fetching the index of this record relative
        to version history.
        """
        alias = sa.orm.aliased(obj)

        subquery = (
            sa.select([sa.func.count('1')], from_obj=[alias.__table__])
            .where(
                getattr(alias, tx_column_name(obj))
                <
                getattr(obj, tx_column_name(obj))
            )
            .correlate(alias.__table__)
            .label('position')
        )
        query = (
            sa.select([subquery], from_obj=[obj.__table__])
            .where(
                sa.and_(
                    *map(eq, zip(identity(obj.__class__), identity(obj)))
                )
            )
            .order_by(
                getattr(obj.__class__, tx_column_name(obj))
            )
        )
        return query

class SubqueryFetcher(VersionObjectFetcher):
    def previous_query(self, obj):
        """
        Returns the query that fetches the previous version relative to this
        version in the version history.
        """
        return self._next_prev_query(obj, 'previous')

    def next_query(self, obj):
        """
        Returns the query that fetches the next version relative to this
        version in the version history.
        """
        return self._next_prev_query(obj, 'next')

class ValidityFetcher(VersionObjectFetcher):
    def next_query(self, obj):
        """
        Returns the query that fetches the next version relative to this
        version in the version history.
        """
        session = sa.orm.object_session(obj)

        return (
            session.query(obj.__class__)
            .filter(
                sa.and_(
                    getattr(obj.__class__, tx_column_name(obj))
                    ==
                    getattr(obj, end_tx_column_name(obj)),
                    *self.parent_identity_correlation(obj)
                )
            )
        )

    def previous_query(self, obj):
        """
        Returns the query that fetches the previous version relative to this
        version in the version history.
        """
        session = sa.orm.object_session(obj)

        return (
            session.query(obj.__class__)
            .filter(
                sa.and_(
                    getattr(obj.__class__, end_tx_column_name(obj))
                    ==
                    getattr(obj, tx_column_name(obj)),
                    *self.parent_identity_correlation(obj)
                )
            )
        )


import logging, hashlib, time, copy
from tname.core.execution import SplitSearch
from tname.core.cms import WordPressCLI
from tname.core.model import WebDriver, Verifier, Configuration, plain_html, check_frequency
from tname.core.model import dimensionality_reduction
from tname.core.visual_oracle.visual_oracle import VisualOracle
from tname.core.visual_oracle.screenshot import Screenshot
from tname.core.analyser import Analyser
from tname.core.timer import Timer

class PENA:
    def __init__(self, cms_path, url="http://localhost", heuristic=plain_html, debug=True):
        """
        Params:
            cms_path: path for wordpress folder
            url: wordpress index page url (default is http://localhost)
            heuristic: type of heuristic used to check page conflict
            debug: test pages will not been removed after execution (check ~/tmp/ folder)
        """
        self.cli = WordPressCLI(home=cms_path)
        self.web_driver = WebDriver(url=url)
        self.heuristic = heuristic
        self.debug = debug
        self.verifier = Verifier(self.cli, self.web_driver, self.heuristic)
        self.input_plugins = []
        self.mode = SplitSearch
        self.generate_configuration = False
        self.show_lines = True
        self.skip_dimensionality = False
        self.remove_spurious = True
        self.visual_oracle = True

    def set_input_plugins(self, input_plugins):
        """
            Description:
                Sets a list of plugins into a configuration
            Params:
                input_plugins: list of plugins name
            Return:
                None
        """
        self.input_plugins = input_plugins

    def set_mode(self, mode):
        """
            Description:
                Set a Search technnique to check conflicts
            Params:
                mode: SplitSearch | Pairwise (class)
            Return:
                None
        """
        self.mode = mode

    def set_generate_configuration(self, generate):
        """
            Description:
                Set true to generate a dictionary of conflicts
            Params:
                generate: boolean
            Return:
                None
        """
        self.generate_configuration = generate

    def set_show_lines(self, show_lines):
        """
            Description:
                Shows unexpected code added/removed from a plugin in log
            Params:
                show_lines: boolean
            Return:
                None
        """
        self.show_lines = show_lines

    def set_skip_dimensionality(self, skip):
        """
            Description:
                Set true to run input_plugins without dimensionality reduction
            Params:
                skip: boolean
            Return:
                None
        """
        self.skip_dimensionality = skip

    def run_only_dimensionality(self, input_plugins=[]):
        """
        Description:
            Runs only dimensionality reduction to generate a set of plugins with differents equivalence class
        Params:
            input_plugins: list of plugins name
        Return:
            list of plugins after dimensionality reduction
        """
        reduced_plugins = dimensionality_reduction(input_plugins, self.web_driver, self.cli)
        if len(reduced_plugins):
            logging.debug("Plugins after dimensionality reduction")
            [logging.debug(" {}".format(plugin)) for plugin in reduced_plugins]
        return reduced_plugins

    def run(self):
        """
        Description:
            PENA Runner
        Params:
            None
        Return:
            None
        """
        if len(self.input_plugins) < 2:
            raise Exception('Plugin list invalid. The plugin list must have lenght 2 or more.')

        # deactivate plugins
        self.cli.deactivate(self.input_plugins)

        logging.info('RUNNING {}'.format(str(self.mode)))
        logging.info('RUNNING N={}'.format(len(self.input_plugins)))

        logging.info("Run Started")

        technnique = self.mode(self.verifier)

        # STEP 1 - DIMENSIONALITY REDUCTION
        reduced_plugins = dimensionality_reduction(
            self.input_plugins,
            self.web_driver,
            self.cli,
            skip=self.skip_dimensionality
        )

        # STEP 2 and 3 - SEARCH FOR CONFLICTS AND CLUSTERING
        if len(reduced_plugins) > 1:
            configuration = Configuration(reduced_plugins)
            conflicts = technnique.run(configuration)
            logging.info("Delta timer (in minutes): {}".format(Timer.get_delta()))

            if self.generate_configuration:
                ''' generate nodes of conflicts '''
                self.generate_nodes(reduced_plugins, conflicts)
                return

            if conflicts:
                all_conflicts = copy.copy(conflicts)

                # REPORT CONFLICTS FOUND BEFORE CLEANSING
                logging.debug("Conflicts found after searching: {}".format(len(all_conflicts)))
                for conflict in all_conflicts:
                    logging.debug("Conflict - {}".format(conflict))
                    changes = self.verifier.conflicting_changes(conflict)
                    unexpected_added, unexpected_removed = changes[0], changes[1]
                    if len(unexpected_added):
                        added = [("    {}".format(line)) for line in unexpected_added]
                        logging.info("{}: {}".format("Unexpected added", added))
                    if len(unexpected_removed):
                        removed = [("    {}".format(line)) for line in unexpected_removed]
                        logging.info("{}: {}".format("Unexpected removed", removed))

                # STEP 4 - REPORT CLEANSING
                if self.remove_spurious:
                    logging.info("Cleansing started")

                    self.cleansing_data = {
                        "non-determinism": [],
                        "minify": [],
                        "union_tags": [],
                        "optional_closing": []
                    }

                    temp_conflicts = copy.copy(all_conflicts)
                    for conflict in all_conflicts:
                        changes = self.verifier.conflicting_changes(conflict)
                        unexpected_added, unexpected_removed, typeof_removal = Analyser(changes[0], changes[1]).run()
                        if (not unexpected_added) and (not unexpected_removed):
                            self.cleansing_data[typeof_removal].append(conflict)
                            temp_conflicts.remove(conflict)
                        else:
                            self.verifier.update_changes(conflict, unexpected_added, unexpected_removed)

                    remaining_conflicts = self.cleansing(temp_conflicts)

                    self.report_cleansing_data(len(temp_conflicts) - len(remaining_conflicts))
                    logging.info("Cleansing finished")
                    all_conflicts = remaining_conflicts

                # STEP 5 - VISUAL ORACLE
                if self.visual_oracle:
                    logging.info("Checking visual conflicts started")
                    visual_conflicts = self.get_visual_conflicts(all_conflicts)
                    for conflict in visual_conflicts:
                        logging.info('Visual conflict identified: {}'.format(conflict))
                    logging.info("Checking visual conflicts finished")

                # REPORT CONFLICTS
                logging.debug("===== FINAL REPORT =====")
                for conflict in all_conflicts:
                    logging.debug(conflict)
                    logging.debug("Conflicting config - {}".format(conflict))

                    if self.show_lines:
                        changes = self.verifier.conflicting_changes(conflict)
                        unexpected_added, unexpected_removed = changes[0], changes[1]
                        if len(unexpected_added):
                            added = [("    {}".format(line)) for line in unexpected_added]
                            logging.info("{}: {}".format("Unexpected added", added))
                        if len(unexpected_removed):
                            removed = [("    {}".format(line)) for line in unexpected_removed]
                            logging.info("{}: {}".format("Unexpected removed", removed))

                freq = check_frequency(all_conflicts)
                [logging.debug("{}: {}".format(k, v)) for k, v in freq.items()]

                self.cli.deactivate(self.input_plugins)

        logging.info("Run Finished!")

        if not self.debug:
            self.web_driver.cleanup()

    def generate_nodes(self, plugins, conflicts):
        """
        Description:
            Generate a data structure (Dictionary) with all conflicts found by PENA
        Params:
            plugins: list of plugins name
            conflicts: all conflicts found by Pena
        """
        _file = 'configuration-{}-{}.txt'.format(len(plugins), time.asctime())
        configuration = {}
        for i in plugins:
            # start all keys with empty list
            configuration[i] = []

        while len(conflicts) != 0:
            conflict_list = conflicts.pop()
            first, second = conflict_list[0], conflict_list[1]

            if second not in configuration[first]:
                configuration[first].append(second)
            if first not in configuration[second]:
                configuration[second].append(first)

        with open(_file, 'w') as f:
            f.write(str(configuration))

    def rerun(self, conflicts):
        """
        Description:
            Remove conflicts non-deterministics found by PENA
        Params:
            conflicts: list of conflicts
        Return:
            List of conflicts without non-deterministics conflicts
        """
        if not conflicts:
            return conflicts

        self.cli.deactivate(self.input_plugins)

        non_deterministic_conflicts = []
        for conflict in conflicts:
            try:
                self.cli.activate(conflict)
                plugins_on_html = self.web_driver.fetch()
                hash_value = hashlib.md5(plugins_on_html).hexdigest()
                self.cli.deactivate(conflict)

                self.cli.activate(conflict)
                plugins_on_html = self.web_driver.fetch()
                self.cli.deactivate(conflict)
            except Exception as e:
                self.cli.deactivate(conflict)

            if hash_value != hashlib.md5(plugins_on_html).hexdigest():
                self.cleansing_data["non-determinism"].append(conflict)
                non_deterministic_conflicts.append(conflict)

        [conflicts.remove(conflict) for conflict in non_deterministic_conflicts]
        return conflicts

    def cleansing(self, conflicts):
        """
        Description:
            Remove spurious from final report
        Params:
            conflicts: array of conflicts
        Optional Params:
            None
        Return:
            Conflicts list after remove spurious
        """
        cleansing_conflicts = [conflict for conflict in conflicts]

        # remove conflicts with minify keyword
        for conflict in conflicts:
            if 'minify' in str(conflict):
                self.cleansing_data["minify"].append(conflict)
                cleansing_conflicts.remove(conflict)

        # rerun to check non-determinism conflicts

        cleansing_conflicts = self.rerun(cleansing_conflicts)

        return cleansing_conflicts

    def get_visual_conflicts(self, conflicts):
        """
        Description:
            Check visual discrepance between plugins
        Params:
            conflicts: array of plugins
        Optional Params:
            None
        Return:
            array of conflicts that shows visual conflicts
        """
        screenshot = Screenshot()
        visual_oracle = VisualOracle(conflicts, self.cli, screenshot)
        visual_conflicts = visual_oracle.run()
        return visual_conflicts

    def report_cleansing_data(self, total_conflicts):
        logging.info("Conflicts removed by cleansing: {}".format(total_conflicts))
        logging.info("non-determinism conflicts: {}".format(len(self.cleansing_data["non-determinism"])))
        logging.info("{}".format(self.cleansing_data["non-determinism"]))
        logging.info("minify conflicts: {}".format(len(self.cleansing_data["minify"])))
        logging.info("{}".format(self.cleansing_data["minify"]))
        logging.info("union conflicts: {}".format(len(self.cleansing_data["union_tags"])))
        logging.info("{}".format(self.cleansing_data["union_tags"]))
        logging.info("optional closing conflicts: {}".format(len(self.cleansing_data["optional_closing"])))
        logging.info("{}".format(self.cleansing_data["optional_closing"]))


from typing import List, Union, Dict, Tuple, Any

from type import Type
from tokenizer import TK, Token
from utils import debug, warning

from parser.parseutils import *
from parser.tokenparser import TokenParser
from parser.subexpressionparser import SubExpressionParser

class ExpressionParser(TokenParser, SubExpressionParser):
    # expression

    def primary_expression(self) -> Node:
        try:
            return self.constant()
        except ParseError:
            pass
        if self.consume('('):
            expression = self.expression()
            self.consume_must(')')
            return expression
        raise ParseError

    def postfix_expression(self) -> Node:
        postfix = self.primary_expression()

        while True:

            if self.consume('['):
                exp = self.expression()
                self.consume_must(']')
                if not postfix.type.is_ptr and not exp.type.is_ptr:
                    TypeError('ポインタ以外を配列参照しています')
                node = self.sub_add(postfix, exp)
                postfix = Node(ND.REF, type=node.type.ptr_to, lhs=node)
                continue
            if self.consume('('):
                if postfix.type.ty == '.unknown':
                    warning(postfix.val, '関数の暗黙的宣言(返り値をlongにします)')
                    postfix.type = Type('.func', func_call_to=Type('long', signed=True))
                args = self.sep_repeat(self.assignment_expression, ',', True)
                self.consume_must(')')
                if not postfix.type.is_func:
                    raise TypeError('関数以外を呼び出そうとしています')
                postfix = Node(ND.CALL, type=postfix.type.func_call_to, call=postfix, call_args=args)
                continue

            if self.consume('++'):
                if not postfix.type.is_real_num():
                    raise TypeError('後置++に実数型以外が指定されています')
                if postfix.type.const:
                    raise TypeError('後置++にconst変数が指定されています')
                postfix = Node('++', type=postfix.type, lhs=postfix)
                continue
            if self.consume('--'):
                if not postfix.type.is_real_num():
                    raise TypeError('後置--に実数型以外が指定されています')
                if postfix.type.const:
                    raise TypeError('後置--にconst変数が指定されています')
                postfix = Node('--', type=postfix.type, lhs=postfix)
                continue

            break

        if postfix.type.ty == '.unknown':
            raise TypeError('変数が宣言されていません')

        return postfix

    def unary_expression(self) -> Node:
        unary = None

        while True:
            if self.consume('++'):
                node = self.unary_expression()
                if not node.type.is_real_num():
                    raise TypeError('前置++に実数型以外が指定されています')
                if node.type.const:
                    raise TypeError('前置++にconst変数が指定されています')
                unary = Node('+', lhs=node, rhs=n_signed_int_plus1, type=node.type)
                continue
            if self.consume('--'):
                node = self.unary_expression()
                if not node.type.is_real_num():
                    raise TypeError('前置--に実数型以外が指定されています')
                if node.type.const:
                    raise TypeError('前置--にconst変数が指定されています')
                unary = Node('-', lhs=node, rhs=n_signed_int_plus1, type=node.type)
                continue

            if self.consume('&'):
                node = self.cast_expression()
                if node.ty == ND.INT:
                    raise TypeError('定数の参照を作成しようとしています')
                unary = Node(ND.LEA, type=Type('.ptr', ptr_to=node.type), lhs=node)
                continue
            if self.consume('*'):
                node = self.cast_expression()
                if not node.type.is_ptr:
                    raise TypeError('ポインタ以外を参照解決しています')
                unary = Node(ND.REF, type=node.type.ptr_to, lhs=node)
                continue
            if self.consume('+'):
                unary = self.cast_expression()
                if not unary.type.is_arithmetic():
                    raise TypeError('単行+に非算術型が指定されています')
                continue
            if self.consume('-'):
                node = self.cast_expression()
                if not node.type.is_arithmetic():
                    raise TypeError('単行-に非算術型が指定されています')
                unary = Node('*', type=node.type, lhs=node, rhs=n_signed_int_minus1)
                continue
            if self.consume('!'):
                node = self.cast_expression()
                unary = Node('==', type=t_signed_int, lhs=node, rhs=n_signed_int_0)
                continue

            if self.consume('sizeof'):
                node = self.unary_expression()
                return Node(ND.INT, type=t_signed_int, val=node.type.size)
            break

        if unary is None:
            unary = self.postfix_expression()
        return unary

    def cast_expression(self) -> Node:
        cast = self.unary_expression()

        while True:
            break

        return cast

    def multiple_expression(self) -> Node:
        mul = self.cast_expression()

        while True:
            if self.consume('*'):
                rhs = self.cast_expression()
                mul = self.sub_mul(mul, rhs)
                continue
            if self.consume('/'):
                rhs = self.cast_expression()
                mul = self.sub_div(mul, rhs)
                continue
            if self.consume('%'):
                rhs = self.cast_expression()
                mul = self.sub_sur(mul, rhs)
                continue
            break

        return mul

    def additive_expression(self) -> Node:
        add = self.multiple_expression()

        while True:
            if self.consume('+'):
                rhs = self.multiple_expression()
                add = self.sub_add(add, rhs)
                continue
            if self.consume('-'):
                rhs = self.multiple_expression()
                add = self.sub_sub(add, rhs)
                continue
            break
        return add

    def shift_expression(self) -> Node:
        shift = self.additive_expression()

        while True:
            if self.consume('>>'):
                rhs = self.additive_expression()
                if not Type.both_integer(shift.type, rhs.type):
                    raise TypeError('右シフト演算子>>に整数型以外が指定されています')
                shift = Node('>>', type=shift.type, lhs=shift, rhs=rhs)
                continue
            if self.consume('<<'):
                rhs = self.additive_expression()
                if not Type.both_integer(shift.type, rhs.type):
                    raise TypeError('左シフト演算子<<に整数型以外が指定されています')
                shift = Node('<<', type=shift.type, lhs=shift, rhs=rhs)
                continue
            break

        return shift

    def relational_expression(self) -> Node:
        relational = self.shift_expression()

        while True:  # todo: http://port70.net/~nsz/c/c11/n1570.html#6.5.8p2  2番目の制約
            if self.consume('<'):
                rhs = self.shift_expression()
                if not Type.both_real_num(relational.type, rhs.type):
                    raise TypeError('比較演算子<に実数型以外が指定されています')
                relational = Node('<', type=t_signed_int, lhs=relational, rhs=rhs)
                continue
            if self.consume('>'):
                rhs = self.shift_expression()
                if not Type.both_real_num(relational.type, rhs.type):
                    raise TypeError('比較演算子>に実数型以外が指定されています')
                relational = Node('<', type=t_signed_int, rhs=relational, lhs=rhs)
                continue
            if self.consume('<='):
                rhs = self.shift_expression()
                if not Type.both_real_num(relational.type, rhs.type):
                    raise TypeError('比較演算子<=に実数型以外が指定されています')
                relational = Node('<=', type=t_signed_int, lhs=relational, rhs=rhs)
                continue
            if self.consume('>='):
                rhs = self.shift_expression()
                if not Type.both_real_num(relational.type, rhs.type):
                    raise TypeError('比較演算子>=に実数型以外が指定されています')
                relational = Node('<=', type=t_signed_int, rhs=relational, lhs=rhs)
                continue
            break

        return relational

    def equality_expression(self) -> Node:
        equality = self.relational_expression()

        while True:
            if self.consume('=='):
                rhs = self.relational_expression()
                if not Type.both_arithmetic(equality.type, rhs.type):
                    raise TypeError('比較演算子==に算術型以外が指定されています')
                equality = Node('==', type=t_signed_int, lhs=equality, rhs=rhs)
                continue
            if self.consume('!='):
                rhs = self.relational_expression()
                if not Type.both_arithmetic(equality.type, rhs.type):
                    raise TypeError('比較演算子!=に算術型以外が指定されています')
                equality = Node('!=', type=t_signed_int, lhs=equality, rhs=rhs)
                continue
            break

        return equality

    def and_expression(self) -> Node:
        and_ = self.equality_expression()

        while True:
            if self.consume('&'):
                rhs = self.equality_expression()
                if not Type.both_integer(and_.type, rhs.type):
                    raise TypeError('&演算子に整数型以外が指定されています')
                and_ = Node('&', type=and_.type, lhs=and_, rhs=rhs)
                continue
            break

        return and_

    def exclusive_or_expression(self) -> Node:
        xor = self.and_expression()

        while True:
            if self.consume('^'):
                rhs = self.and_expression()
                if not Type.both_integer(xor.type, rhs.type):
                    raise TypeError('^演算子に整数型以外が指定されています')
                xor = Node('^', type=xor.type, lhs=xor, rhs=rhs)
                continue
            break

        return xor

    def or_expression(self) -> Node:
        or_ = self.exclusive_or_expression()

        while True:
            if self.consume('|'):
                rhs = self.exclusive_or_expression()
                if not Type.both_integer(or_.type, rhs.type):
                    raise TypeError('|演算子に整数型以外が指定されています')
                or_ = Node('|', type=or_.type, lhs=or_, rhs=rhs)
                continue
            break

        return or_

    def logical_and_expression(self) -> Node:
        land = self.or_expression()

        while True:
            if self.consume('&&'):
                rhs = self.or_expression()
                if not Type.both_integer(land.type, rhs.type):  # TODO: integerじゃなくてscala...
                    raise TypeError('&&演算子に整数型以外が指定されています')
                land = Node('&&', type=land.type, lhs=land, rhs=rhs)
                continue
            break

        return land

    def logical_or_expression(self) -> Node:
        lor = self.logical_and_expression()

        while True:
            if self.consume('||'):
                rhs = self.logical_and_expression()
                if not Type.both_integer(lor.type, rhs.type):
                    raise TypeError('||演算子に整数型以外が指定されています')
                lor = Node('||', type=lor.type, lhs=lor, rhs=rhs)
                continue
            break

        return lor

    def conditional_expression(self) -> Node:
        condition = self.logical_or_expression()

        while True:
            break

        return condition

    def assignment_expression(self) -> Node:
        assign = self.conditional_expression()

        while True:
            if self.consume('='):
                if assign.type.const:
                    raise TypeError('const変数に代入しています')
                assign = Node('=', type=assign.type, lhs=assign, rhs=self.assignment_expression())
                continue

            if self.consume('*='):
                if assign.type.const:
                    raise TypeError('const変数に代入しています')
                assign = Node('=', lhs=assign,
                              rhs=Node('*', lhs=assign, rhs=self.assignment_expression(), type=assign.type),
                              type=assign.type
                              )
                continue

            if self.consume('/='):
                if assign.type.const:
                    raise TypeError('const変数に代入しています')
                assign = Node('=', lhs=assign,
                              rhs=Node('/', lhs=assign, rhs=self.assignment_expression(), type=assign.type),
                              type=assign.type
                              )
                continue

            if self.consume('%='):
                if assign.type.const:
                    raise TypeError('const変数に代入しています')
                assign = Node('=', lhs=assign,
                              rhs=Node('%', lhs=assign, rhs=self.assignment_expression(), type=assign.type),
                              type=assign.type
                              )
                continue

            if self.consume('+='):
                if assign.type.const:
                    raise TypeError('const変数に代入しています')
                assign = Node('=', lhs=assign,
                              rhs=Node('+', lhs=assign, rhs=self.assignment_expression(), type=assign.type),
                              type=assign.type
                              )
                continue

            if self.consume('-='):
                if assign.type.const:
                    raise TypeError('const変数に代入しています')
                assign = Node('=', lhs=assign,
                              rhs=Node('-', lhs=assign, rhs=self.assignment_expression(), type=assign.type),
                              type=assign.type
                              )
                continue

            if self.consume('<<='):
                if assign.type.const:
                    raise TypeError('const変数に代入しています')
                assign = Node('=', lhs=assign,
                              rhs=Node('<<', lhs=assign, rhs=self.assignment_expression(), type=assign.type),
                              type=assign.type
                              )
                continue

            if self.consume('>>='):
                if assign.type.const:
                    raise TypeError('const変数に代入しています')
                assign = Node('=', lhs=assign,
                              rhs=Node('>>', lhs=assign, rhs=self.assignment_expression(), type=assign.type),
                              type=assign.type
                              )
                continue

            if self.consume('&='):
                if assign.type.const:
                    raise TypeError('const変数に代入しています')
                assign = Node('=', lhs=assign,
                              rhs=Node('&', lhs=assign, rhs=self.assignment_expression(), type=assign.type),
                              type=assign.type
                              )
                continue

            if self.consume('^='):
                if assign.type.const:
                    raise TypeError('const変数に代入しています')
                assign = Node('=', lhs=assign,
                              rhs=Node('^', lhs=assign, rhs=self.assignment_expression(), type=assign.type),
                              type=assign.type
                              )
                continue

            if self.consume('|='):
                if assign.type.const:
                    raise TypeError('const変数に代入しています')
                assign = Node('=', lhs=assign,
                              rhs=Node('|', lhs=assign, rhs=self.assignment_expression(), type=assign.type),
                              type=assign.type
                              )
                continue

            break

        return assign

    def expression(self) -> Node:
        exp = self.assignment_expression()

        while True:
            if self.consume(','):
                rhs = self.assignment_expression()
                exp = Node(',', type=rhs.type, lhs=exp, rhs=rhs)
                continue
            break

        return exp

    def constant_expression(self) -> Node:  # TODO: http://port70.net/~nsz/c/c11/n1570.html#6.6
        constant = self.conditional_expression()

        def gen_constant(node):
            if node.ty == ND.INT:
                return node.val
            if node.ty == '*':
                return gen_constant(node.lhs) * gen_constant(node.rhs)
            if node.ty == '/':
                return gen_constant(node.lhs) // gen_constant(node.rhs)
            if node.ty == '%':
                return gen_constant(node.lhs) % gen_constant(node.rhs)
            if node.ty == '+':
                return gen_constant(node.lhs) + gen_constant(node.rhs)
            if node.ty == '-':
                return gen_constant(node.lhs) - gen_constant(node.rhs)
            if node.ty == '<<':
                return gen_constant(node.lhs) << gen_constant(node.rhs)
            if node.ty == '>>':
                return gen_constant(node.lhs) >> gen_constant(node.rhs)
            if node.ty == '<':
                return int(gen_constant(node.lhs) < gen_constant(node.rhs))
            if node.ty == '>':
                return int(gen_constant(node.lhs) > gen_constant(node.rhs))
            if node.ty == '<=':
                return int(gen_constant(node.lhs) <= gen_constant(node.rhs))
            if node.ty == '>=':
                return int(gen_constant(node.lhs) >= gen_constant(node.rhs))
            if node.ty == '==':
                return int(gen_constant(node.lhs) == gen_constant(node.rhs))
            if node.ty == '!=':
                return int(gen_constant(node.lhs) != gen_constant(node.rhs))
            if node.ty == '&':
                return int(gen_constant(node.lhs) & gen_constant(node.rhs))
            if node.ty == '|':
                return int(gen_constant(node.lhs) | gen_constant(node.rhs))
            if node.ty == '&&':
                return 1 if gen_constant(node.lhs) and gen_constant(node.rhs) else 0
            if node.ty == '|':
                return 1 if gen_constant(node.lhs) or gen_constant(node.rhs) else 0

        res = gen_constant(constant)
        return Node(ND.INT, type=t_signed_int, val=res)


import pygame
import random
import math

pygame.font.init()
pygame.mixer.init()

WIDTH, HEIGHT = 450, 800
WIN = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Jelly Game")

WHITE = (255, 255, 255)
BLACK = (0, 0, 0)

RED = (255, 0, 0)
YELLOW = (255, 255, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)

BORDER = pygame.Rect(0, 0, WIDTH, HEIGHT)

STANDARD_FONT = pygame.font.SysFont('comicsans', 40)

FPS = 60

def draw_window(board, score):

    # clear screen
    pygame.draw.rect(WIN, BLACK, BORDER)

    # Draw score
    score_text = STANDARD_FONT.render("Score: " + str(score), 1, WHITE)
    WIN.blit(score_text, (10, 760))

    # Draw game board with cubes

    draw_board(board)

    pygame.display.update()

def draw_board(board):
    game_block = pygame.Rect(0, 0, 50, 50)
    pygame.draw.rect(WIN, YELLOW, game_block)

    temp_height = 0
    temp_width = 0
    for row in board:
        temp_width = 0
        for block in row:

            game_block = pygame.Rect(temp_width, temp_height, 50, 50)

            if (block == 1):
                color = YELLOW
            elif (block == 2):
                color = BLUE
            elif (block == 3):
                color = RED
            elif (block == 4):
                color = GREEN
            else:
                color = BLACK
            pygame.draw.rect(WIN, color, game_block)

            temp_width = temp_width + 50
        temp_height = temp_height + 50
        #pygame.draw.rect(WIN, YELLOW, bullet)

def end_game(text):
    draw_text = STANDARD_FONT.render(text, 1, WHITE)
    WIN.blit(draw_text, (WIDTH/2 - draw_text.get_width() / 2, HEIGHT/2 - draw_text.get_height()/2))
    pygame.display.update()
    pygame.time.delay(5000)

def generate_board():

    board = []
    for x in range(15):
        row = []
        for y in range(9):
            row.append(random.randint(1, 4))
        board.append(row)

    return board

def nearby_same_color(color_number, up_color, down_color, left_color, right_color):
    if color_number == up_color or color_number == down_color or color_number == left_color or color_number == right_color:
        return True
    return False

def get_valid_moves(board):
    valid_moves = []

    row = 0

    for rows in board:
        column = 0
        for block in rows:

            if(block == 0):
                column = column + 1
                continue

            up_color = 0
            if(row > 0):
                up_color = board[row - 1][column]

            down_color = 0
            if(row < 14):
                down_color = board[row + 1][column]

            left_color = 0
            if(column > 0):
                left_color = board[row][column-1]

            right_color = 0
            if(column < 8):
                right_color = board[row][column+1]

            if nearby_same_color(block, up_color, down_color, left_color, right_color):

                valid_moves.append([row, column])

            column = column + 1

        row = row + 1

    return valid_moves

def clear_all_nearby(deleting_color, row, column, board, number_of_clears):

    if(row == -1):
        return board, number_of_clears

    if(row == 15):
        return board, number_of_clears

    if(column == -1):
        return board, number_of_clears

    if(column == 9):
        return board, number_of_clears

    if deleting_color == board[row][column]:
        board[row][column] = 0
        number_of_clears = number_of_clears + 1
        board, number_of_clears = clear_all_nearby(deleting_color, row+1, column, board, number_of_clears)
        board, number_of_clears = clear_all_nearby(deleting_color, row-1, column, board, number_of_clears)
        board, number_of_clears = clear_all_nearby(deleting_color, row, column-1, board, number_of_clears)
        board, number_of_clears = clear_all_nearby(deleting_color, row, column+1, board, number_of_clears)
    return board, number_of_clears

def add_score(number_of_clears):
    if(number_of_clears==2):
        return 2
    elif(number_of_clears==3):
        return 5
    elif(number_of_clears==4):
        return 8
    elif(number_of_clears==5):
        return 13
    elif(number_of_clears==6):
        return 18
    elif(number_of_clears==7):
        return 25
    elif(number_of_clears==8):
        return 32
    elif(number_of_clears==9):
        return 41
    elif(number_of_clears==10):
        return 50        # THIS IS FAKED
    elif(number_of_clears==11):
        return 61
    elif(number_of_clears==12):
        return 70        # THIS IS FAKED
    elif(number_of_clears==13):
        return 85
    elif(number_of_clears==14):
        return 90        # THIS IS FAKED
    elif(number_of_clears==15):
        return 100        # THIS IS FAKED
    elif(number_of_clears==16):
        return 110        # THIS IS FAKED
    else:
        return 999999     # THIS IS FAKED

def are_there_colors_above(colors, row, column, board):
    if(row == 0):
        return colors

    if (board[row][column] == 0):
        return colors

    if (board[row][column] != 0):
        colors.append([row, column])
        colors = are_there_colors_above(colors, row-1, column, board)
    return colors

def fill_board(board):

    row = 0
    for rows in board:
        column = 0
        for block in rows:
            if board[14][column] == 0:
                break

            if(block == 0):
                board[row][column] = random.randint(1, 4)

            column = column + 1
        row = row + 1
    return board

def remove_column(column, board):
    del board[0][column]
    del board[1][column]
    del board[2][column]
    del board[3][column]
    del board[4][column]
    del board[5][column]
    del board[6][column]
    del board[7][column]
    del board[8][column]
    del board[9][column]
    del board[10][column]
    del board[11][column]
    del board[12][column]
    del board[13][column]
    del board[14][column]
    return board

def add_column(board):
    board[0].append(0)
    board[1].append(0)
    board[2].append(0)
    board[3].append(0)
    board[4].append(0)
    board[5].append(0)
    board[6].append(0)
    board[7].append(0)
    board[8].append(0)
    board[9].append(0)
    board[10].append(0)
    board[11].append(0)
    board[12].append(0)
    board[13].append(0)
    board[14].append(0)
    return board

def execute_column_break(board, active_columns):

    should_board_be_filled = False
    columns_to_remove = []
    for column in range(active_columns):
        if(board[14][column] == 0):
            columns_to_remove.append(column)
            active_columns = active_columns -1

    if(len(columns_to_remove)>0):
        should_board_be_filled = True
        reversed_list = columns_to_remove[::-1]
        for reversed in reversed_list:
            board = add_column(board)
            board = remove_column(reversed, board)

    return board, active_columns, should_board_be_filled

def execute_gravity(board):

    row = 0
    for rows in board:
        column = 0
        for block in rows:

            if(block == 0):
                column = column + 1
                continue

            if(row > 13):
                break

            # Can block move down?
            if(board[row + 1][column] == 0):
                board[row + 1][column] = board[row][column]
                board[row][column] = 0
                # Run it again
                execute_gravity(board)

            column = column + 1

        row = row + 1

    return board

def main():

    score = 0

    board = generate_board()
    active_columns = 9
    clock = pygame.time.Clock()
    run = True
    while run:
        clock.tick(FPS)

        valid_moves = get_valid_moves(board)

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                run = False
                pygame.quit()

            if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:

                print(len(valid_moves))

                x, y = pygame.mouse.get_pos()
                column = math.floor(x/50)
                row = math.floor(y/50)
                print(row,column, board[row][column])
                if ([row, column] in valid_moves and board[row][column] != 0):
                    board, number_of_clears = clear_all_nearby(board[row][column], row, column, board, 0)
                    score = score + add_score(number_of_clears)
                    board = execute_gravity(board)
                    board, active_columns, should_board_be_filled = execute_column_break(board, active_columns)
                    if (should_board_be_filled):
                        board = fill_board(board)

        if len(valid_moves) == 0:
            end_game('You lost')
            break

        keys_pressed = pygame.key.get_pressed()

        if keys_pressed[pygame.K_b]:
            print("\n\r")
            for row in board:
                print(row)

        if keys_pressed[pygame.K_c]:
            print(valid_moves)

        if keys_pressed[pygame.K_a]:
            score = score + 10
            print('A is pressed')

        draw_window(board, score)

    main()

if __name__ == "__main__":
    main()


import numpy as np
import dnstwisterAPI as dnstwisterAPI

import datetime

class Analyser():

    def split_name(self, url):
        """
        Splits an url between the name and the extension and removes the www if it exists
        Example: 'www.google.com' -> ('google', '.com')

        url: the url to split
        returns: tuple (name, extension)
        """
        url_splitted = url.split('.')
        extension = '.' + url_splitted.pop(-1)
        if (url_splitted[0] == 'www'):
            del url_splitted[0]
        name = '.'.join(url_splitted)
        return (name, extension)

    def __init__(self, url, authority):
        self.url = url
        if len(url) != 0:
            (self.name, self.extension) = self.split_name(self.url)
            if authority is not None:
                self.authority = authority.lower()
            else:
                self.authority = ""
            self.api = dnstwisterAPI.dnstwisterAPI(self.url)
        self.number_features = 16 # Number of feature we can compute

        # Taken from research paper / web-site ( https://security-soup.net/good-domains-for-bad-guys-the-riskiest-tlds-for-malware-and-phishing/)
        self.suspiciousTLDs = ['.bank', '.online', '.business', '.party', '.cc', '.pw', '.center', '.racing', '.cf',
                               '.ren', '.click', '.review', '.club', '.science', '.country', '.stream', '.download',
                               '.study', '.ga', '.support', '.gb', '.tech', '.gdn', '.tk', '.gq', '.top', '.info',
                               '.vip', '.kim', '.loan', '.work', '.men', '.win', '.ml', '.xyz', '.mom', '.xin',
                               '.jetzt', '.bid', '.ren', '.trade', '.date', '.wang', 'accountants', '.cricket', '.link',
                               '.rest', '.casa', '.tw', '.buzz', '.faith', '.ref', '.biz', '.tokyo', '.ooo',
                               '.yokohama', '.ryukyu', '.world', '.asia']

        # Original domain + country with +50M ppl
        self.nonSuspiciousTLDs = ['.com', '.org', '.net', '.eu', '.int', '.edu', '.gov', '.mil', '.cn', '.in', '.us',
                                  '.id', '.pk', '.br', '.ng', '.bd', '.ru', '.mx', '.jp', '.ph', '.cd', '.eg', '.et',
                                  '.vn', '.ir', '.tr', '.de', '.fr', '.uk', '.th', '.it', '.za', '.il', '.tz', '.mm',
                                  '.kr', '.co', '.tel', '.kw', '.jobs']

        self.suspicious_keywords_list = ['activity', 'office', 'appleid', 'outlook', 'poloniex', 'facebook',
                                         'moneygram', 'overstock', 'skype', 'alert', 'online', 'icloud', 'office365',
                                         'coinhive', 'tumblr', 'westernunion', 'alibaba', 'github', 'purchase',
                                         'recover', 'iforgot', 'microsoft', 'bithumb', 'reddit', 'bankofamerica',
                                         'aliexpress', 'authentication', 'safe', 'itunes', 'windows', 'kraken',
                                         'youtube', 'wellsfargo', 'leboncoin', 'authorize', 'secure', 'apple',
                                         'protonmail localbitcoin', 'twitter', 'paypal', 'amazon', 'netflix', 'bill',
                                         'security', 'tutanota', 'bitstamp', 'linkedin', 'citigroup', 'client',
                                         'service', 'hotmail', 'bittrex', 'instagram', 'santander support',
                                         'transaction', 'gmail', 'blockchain', 'flickr', 'morganstanley', 'unlock',
                                         'update', 'google', 'bitflyer', 'whatsapp', 'barclays', 'wallet', 'account',
                                         'outlook', 'coinbase', 'hsbc', 'form', 'login', 'yahoo', 'hitbtc', 'scottrade',
                                         'log-in', 'password', 'google', 'lakebtc', 'ameritrade', 'live', 'signin',
                                         'yandex', 'bitfinex', 'merilledge', 'manage', 'sign-in', 'bitconnect', 'bank',
                                         'verification', ' verify', 'coinsbank', 'webscr', 'invoice', 'authenticate',
                                         ' confirm', 'credential', 'customer', 'invoice', 'post', 'document', 'postal',
                                         'calculations', 'copy', 'fedex', 'statement', 'financial', 'dhl', 'usps',
                                         'notification', 'n', 'irs', 'ups', 'no', 'delivery', 'ticket', 'https', 'http',
                                         'auth', 'access', 'account', 'admin', 'agree', 'blue', 'business', 'cdn',
                                         'choose', 'claim', 'cl', 'click', 'confirm', 'confirmation', 'connect',
                                         'download', 'enroll', 'find', 'group', 'http', 'https', 'https-www', 'install',
                                         'login', 'mobile', 'mail', 'my', 'online', 'pay', 'payment', 'payments',
                                         'portal', 'recovery', 'register', 'ssl', 'safe', 'secure', 'security',
                                         'service', 'services', 'signin', 'signup', 'support', 'summary', 'update',
                                         'user', 'verify', 'verification', 'view', 'ww', 'www', 'web']

        self.suspicious_characters_list = ['@', '-']

        self.free_certificates_authorities = ["hubspot", "let's encrypt", "comodo", "cloudflare", "ssl for free",
                                              "godaddy", "geoTrust", "gogetssl", "instantssl", "basicssl", "zerossl",
                                              "certbot", "wosign", "free ssl space", "cacert", "startssl", "free ssl",
                                              "free ssl certificate", "gandi", "sectigo", "digicert",
                                              "wosign’s kuaissl"]
        self.suspicious_hosting = ["namecheap", "godaddy", "namesilo"]

    def levenshtein_distance(self, word):
        l1, l2 = len(self.name), len(word)
        d = np.zeros((l1 + 1, l2 + 1))
        subCost = 0

        for i in range(l1 + 1):
            d[i, 0] = i
        for j in range(l2 + 1):
            d[0, j] = j

        for i in range(1, l1 + 1):
            for j in range(1, l2 + 1):
                if self.name[i - 1] == word[j - 1]:
                    subCost = 0
                else:
                    subCost = 1
                d[i, j] = min(d[i - 1, j] + 1, d[i, j - 1] + 1, d[i - 1, j - 1] + subCost)

        return int(d[l1, l2])

    def levenshtein(self):
        ld = []
        for word in self.suspicious_keywords_list:
            ld.append(self.levenshtein_distance(word))
        return min(ld)
        """
        Indicates if the certificate authority is a free certificate authority

        return: bool : true if certificate authority is free, false if not free
        """

    def issued_from_free_CA(self):
        res = False
        if self.authority == "":
            return res
            for ca in self.free_certificates_authorities:
                if ca in self.authority:
                    return res
        return res

    def deeply_nested_subdomains(self):
        """
        Give the number of '.' in the name which can be an indicator of suspiciousness if it is too high (F2 property)

        returns : nb of '.' in the name
        """
        return self.name.count('.')

    def suspicious_tld(self):
        """
        Say if the domain name is suspicious, not suspicious or unknown (F4 property)

        returns: 0 if unknown, 1 if non-suspicious, 2 if suspicious
        """

        if self.extension in self.suspiciousTLDs:
            return 2
        elif self.extension in self.nonSuspiciousTLDs:
            return 1
        else:
            return 0

    def inner_tld_in_subdomain(self):
        """
        Say if there is a domain name into the name, maybe to trick a user (F5 property)

        returns: True if there is at least one, False if not
        """

        for extension in self.nonSuspiciousTLDs:
            if extension in self.name:
                return 1
        return 0

    def suspicious_keywords(self):
        """
        Checks if there is suspicious keywordsin the name (F6 property)

        returns: ture if there is at least one
        """

        for keyword in self.suspicious_keywords_list:
            if keyword in self.name:
                if keyword == self.name:
                    # google.com isn't suspicious because there is google in the name
                    return 0
                else:
                    return 1
        return 0

    def hyphens_in_subdomain(self):
        """
        Give the number of "'" in the name which can be an indicator of suspiciousness if it is too high (F8 property)

        returns : nb of "'" in the name
        """
        return self.name.count("'")

    def suspicious_domain_length(self):
        """
        Analyse the length of the name which can be an indicator of suspiciousness if it is too high (F9 property)

        returns true if length is >= 54 ( Statistical analysis )
        """
        if len(self.name) >= 54:
            return 1
        else:
            return 0

    def suspicious_characters(self):
        """
        Checks if there is suspicious characters in the name (F10 property)

        returns: true if there is at least one
        """

        for ch in self.suspicious_characters_list:
            if ch in self.name:
                return 1
        return 0

    def suspicious_age_domain(self):
        """
        Checks if the domain age is suspicious (F11 property)
        dnstwister API
        Age Of Domain <= 6 months -> Phishing
        """
        today = datetime.date.today()
        d1 = today.strftime("%Y-%m-%d")
        #print("d1 =", d1)
        date = self.api.requestDateCreation()
        if date == {}:
            return True
        if self.api.numMonth(d1,date) > 6 :
            return False
        else :
            return True

    def suspicious_date_creation(self):
        """
        date_creation in last month -> phishing
        dnstwister API date = api.requestDateCreation(self.name+self.extension)
        """
        today = datetime.date.today()
        d1 = today.strftime("%Y-%m-%d")
        #print("d1 =", d1)
        date = self.api.requestDateCreation()
        if date == {}:
            return True
        if self.api.numMonth(d1,date) == 0 :
            return True
        else :
            return False

    def suspicious_date_expiry(self):
        """
        Registry Expiry Date <= 1 year -> Phishing
        dnstwister API
        """
        today = datetime.date.today()
        d1 = today.strftime("%Y-%m-%d")
        #print("d1 =", d1)
        date = self.api.requestExpiryDate()
        if date == {}:
            return True
        if self.api.numMonth(d1,date) > 12 :
            return False
        else :
            return True

    def suspicious_valid_period_domain(self):
        """
        Registry Expiry Date - Creation Date <= 1 year -> Phishing
        dnstwister API
        """

        dateEx = self.api.requestExpiryDate()
        dateCr = self.api.requestDateCreation()
        if dateEx == {} or dateCr == {}:
            return True
        if self.api.numMonth(dateEx,dateCr) > 12 :
            return False
        else :
            return True

    def suspicious_registrant_name(self):
        """
        if Api.requestRegistrantName(self.name+self.extension) return {} is suspicious
        dnstwister API
        """

        name = self.api.requestRegistrantName()
        if name == {}:
            return True
        return False

    def suspicious_registrant_organization(self):
        """
        if Api.requestRegistrantOrganization(self.name+self.extension) return {} is suspicious
        dnstwister API
        """

        name = self.api.requestRegistrantOrganization()
        if name == {}:
            return True
        return False

    def suspicious_registrarURL(self):
        """
        if Api.requestRegistrantURL_Host(self.name+self.extension) return {} is suspicious
        dnstwister API
        """

        name = self.api.requestRegistrarURL_Host()
        if name == {}:
            return True
        for host in self.suspicious_hosting:
            if host in self.name:
                return True
        return False

    def suspicious_parkerURL(self):
        """
       return requestParkedCheckUrl(self.name + self.extension) return score  ( > 0.50 good parameter -> possibly no phishing )
       dnstwister API
       """

        if (self.suspicious_age_domain() and   self.suspicious_date_creation() and self.suspicious_date_expiry() and self.suspicious_valid_period_domain()):
            value = self.api.requestParkedCheckUrl()
            if value > 0.50 :
                return False
            else:
                return True
        else:
          return False

"""
1 Gaston
3 Morgane
2-4-5-6-8 Done here
7 to discuss with the teatcher
"""


import tweepy
import credentials
import twitterSettings
from twilio.rest import Client

def returnTweets(username):
    auth = tweepy.OAuthHandler(credentials.twitter["consumer_key"], credentials.twitter["consumer_secret"])
    auth.set_access_token(credentials.twitter["access_token"], credentials.twitter["access_token_secret"])
    api = tweepy.API(auth)

    # Will be sent as a text message
    toText = []

    for tweet in tweepy.Cursor(api.user_timeline, id=username).items():
        # Twitter's api doesn't provide a way to find tweets within a certain date range, so we'll use likes
        if not tweet.favorited:
            api.create_favorite(tweet.id)
            addToText = {}
            addToText["author"] = tweet.user.name
            addToText["message"] = tweet._json["text"]
            toText.append(addToText)
        else:
            # Once we hit the first tweet that has been liked, we can stop looping over the rest
            return toText

def sendTexts(tweetQueue):
    # Since I'm paying per text, I'm sending everything in one text.
    # It's less readable, but I don't want to spend much on a side project
    client = Client(credentials.twilio["accountSID"], credentials.twilio["authToken"])
    for number in credentials.twilio["cellPhone"]:
        client.messages.create(body=str(tweetQueue), from_=credentials.twilio["twilioNumber"], to=number)

if __name__ == "__main__":
    tweetQueue = []
    for user in twitterSettings.followNames:
        tweetQueue += returnTweets(user)
    if tweetQueue:
        sendTexts(tweetQueue)


import numpy as np
from matplotlib import pyplot as plt

def cosmo():

    '''
    Construct plots for my introduction

    '''
    scaleFactor()

def scaleFactor():

    '''
    Determine the scale factor as function of time
    for three different curvatures

    H^2 = H0^2( omega_a + ...)
    H = H0( omega ...)^1/2.
    da/a *(omega)^-1/2 = H0dt

    H0t = int[ ( omega_m a^-1 + omega_r a^-2 + omega_l a^2 + (1-Omega_k) )^-1/2 da]

    Matter
    -------
    H0t = int[ a^3/2 / a da ]
    H0t = 2/3a^3/2
    a = 3H0/2 t^2/3

    H0t = int[ (a^-1 + (1-k))^{-1/2} ]

    u = x + b
    du = dx
    h0t = int[ du u^-1/2 ]
    h0t = 2 u^1/2
    h0t = 2( a + (1-k))^1/2

    '''
    h0 = 70
    #Matter dominated and flat
    a = np.arange(10000)/100.+0.0001
    t_m_flat = hubble( a, 0.3, 0., 0.7 )
    t_m_open = hubble( a, 0., 0., 0.  )
    t_m_closed = hubble( a, 1., 0., 0. )
    t_m_closed = hubble( a, 1.1, 0., 0. )

    plt.plot( t_m_flat, a,   'r.', \
              t_m_open, a,  'b.', \
              t_m_closed, a,  'g.', \
              t_m_closed, a,  'y.')

    plt.yscale('log')
    plt.xscale('log')

    plt.show()

def hubble(a, omega_m, omega_r, omega_l):

    omega_k =  omega_m + omega_r + omega_l

    t = np.zeros( len(a), 'double')
    increments = 1000
    da = a / float(increments)

    for i in xrange(increments):

        ia = da * float(i+1)

        iT = (omega_m*ia**(-1.) + \
              omega_r*ia**(-2.)  + \
              omega_l*ia**2.     + \
        (1.0 - omega_k))

        ABSiT = (np.abs(iT))**(-0.5)*da

        t += ABSiT

    return t

if __name__ == '__main__':
    cosmo()


#!/usr/bin/env python
""" """

# Standard library modules.
import unittest
import logging

# Third party modules.

# Local modules.
from pymontecarlo.testcase import TestCase
from pymontecarlo.options.beam.base import \
    Beam, convert_diameter_fwhm_to_sigma, convert_diameter_sigma_to_fwhm
from pymontecarlo.options.particle import Particle

# Globals and constants variables.

class Testbase(TestCase):

    def testconvert_diameter_fwhm_to_sigma(self):
        self.assertAlmostEqual(0.849321, convert_diameter_fwhm_to_sigma(1.0), 4)

    def testconvert_diameter_sigma_to_fwhm(self):
        self.assertAlmostEqual(1.0, convert_diameter_sigma_to_fwhm(0.849321), 4)

class TestBase(TestCase):

    def setUp(self):
        super().setUp()

        self.b = Beam(15e3, Particle.POSITRON)

    def testskeleton(self):
        self.assertAlmostEqual(15e3, self.b.energy_eV, 4)
        self.assertAlmostEqual(15.0, self.b.energy_keV, 4)
        self.assertEqual(Particle.POSITRON, self.b.particle)

    def test__eq__(self):
        b = Beam(15e3, Particle.POSITRON)
        self.assertEqual(b, self.b)

        b = Beam(15000.009, Particle.POSITRON)
        self.assertEqual(b, self.b)

    def test__ne__(self):
        b = Beam(14e3, Particle.POSITRON)
        self.assertNotEqual(b, self.b)

        b = Beam(15e3, Particle.ELECTRON)
        self.assertNotEqual(b, self.b)

if __name__ == '__main__': #pragma: no cover
    logging.getLogger().setLevel(logging.DEBUG)
    unittest.main()


# -*- coding: utf-8 -*-
from ocds.storage import (
    MainStorage
)
import argparse
import yaml
import os

def read_config(path):
    with open(path) as cfg:
        config = yaml.load(cfg)
    return config

def parse_args():
    parser = argparse.ArgumentParser('Release Packages')
    parser.add_argument('-c', '--config', required=True, help="Path to configuration file")
    return parser.parse_args()

def run():
    args = parse_args()
    config = read_config(args.config)
    path = os.path.join(config.get('path_for_release'), 'releases')
    storage = MainStorage(config, path)
    storage.save()