<commit_before>import logging
import os
import traceback
import datetime

from django.conf import settings
from django_cron import CronJobBase, Schedule

from git import Repo, Actor

import scraper.documents
import scraper.political_parties
import scraper.parliament_members

from website import settings

logger = logging.getLogger(__name__)

class CreateCommitWetsvoorstellenIDs(CronJobBase):
    RUN_AT_TIMES = ['20:00']
    schedule = Schedule(run_at_times=RUN_AT_TIMES)
    code = 'website.cron.CreateCommitWetsvoorstellenIDs'

    @staticmethod
    def dossier_ids_to_file(dossier_ids, filepath):
        with open(filepath, 'w') as fileout:
            for dossier_id in dossier_ids:
                fileout.write(dossier_id + '\n')

    def do(self):
        logger.info('BEGIN')
        try:
            repo = Repo(settings.WETSVOORSTELLEN_REPO_DIR)
            assert not repo.bare
            # origin = repo.create_remote('origin', repo.remotes.origin.url)
            # origin.pull()

            filename_iaan = 'wetsvoorstellen_dossier_ids_initiatief_aanhangig.txt'
            filepath = os.path.join(settings.WETSVOORSTELLEN_REPO_DIR, filename_iaan)
            dossier_ids = scraper.documents.get_dossier_ids_wetsvoorstellen_initiatief(filter_active=True)
            assert len(dossier_ids) > 105
            self.dossier_ids_to_file(dossier_ids, filepath)

            filename_iaf = 'wetsvoorstellen_dossier_ids_initiatief_afgedaan.txt'
            filepath = os.path.join(settings.WETSVOORSTELLEN_REPO_DIR, filename_iaf)
            dossier_ids = scraper.documents.get_dossier_ids_wetsvoorstellen_initiatief(filter_inactive=True)
            assert len(dossier_ids) > 68
            self.dossier_ids_to_file(dossier_ids, filepath)

            filename_raan = 'wetsvoorstellen_dossier_ids_regering_aanhangig.txt'
            filepath = os.path.join(settings.WETSVOORSTELLEN_REPO_DIR, filename_raan)
            dossier_ids = scraper.documents.get_dossier_ids_wetsvoorstellen_regering(filter_active=True)
            assert len(dossier_ids) > 120
            self.dossier_ids_to_file(dossier_ids, filepath)

            filename_raf = 'wetsvoorstellen_dossier_ids_regering_afgedaan.txt'
            filepath = os.path.join(settings.WETSVOORSTELLEN_REPO_DIR, filename_raf)
            dossier_ids = scraper.documents.get_dossier_ids_wetsvoorstellen_regering(filter_inactive=True)
            assert len(dossier_ids) > 1266
            self.dossier_ids_to_file(dossier_ids, filepath)

            changed_files = repo.git.diff(name_only=True)
            if not changed_files:
                logger.info('no changes')
                logger.info('END')
                return

            filepath_date = os.path.join(settings.WETSVOORSTELLEN_REPO_DIR, 'date.txt')
            with open(filepath_date, 'w') as fileout:
                fileout.write(datetime.date.today().strftime('%Y-%m-%d'))

            index = repo.index
            index.add([filename_iaan, filename_iaf, filename_raan, filename_raf, 'date.txt'])
            author = Actor(settings.GIT_AUTHOR_NAME, settings.GIT_AUTHOR_EMAIL)
            index.commit(
                message='update of wetsvoorstellen dossier ids',
                author=author
            )
            # origin.push()
        except:
            logger.error(traceback.format_exc())
            raise
        logger.info('END')

class CreateCommitPartyCSV(CronJobBase):
    RUN_AT_TIMES = ['19:00']
    schedule = Schedule(run_at_times=RUN_AT_TIMES)
    code = 'website.cron.CreateCommitPartyCSV'

    def do(self):
        logger.info('BEGIN')
        try:
            repo = Repo(settings.PARTIES_REPO_DIR)
            assert not repo.bare
            # origin = repo.create_remote('origin', repo.remotes.origin.url)
            # origin.pull()

            parties = scraper.political_parties.search_parties()
            filepath = os.path.join(settings.PARTIES_REPO_DIR, 'fracties.csv')
            scraper.political_parties.create_parties_csv(parties, filepath)

            changed_files = repo.git.diff(name_only=True)
            if not changed_files:
                logger.info('no changes')
                logger.info('END')
                return

            filepath_date = os.path.join(settings.PARTIES_REPO_DIR, 'date.txt')
            with open(filepath_date, 'w') as fileout:
                fileout.write(datetime.date.today().strftime('%Y-%m-%d'))

            index = repo.index
            index.add(['fracties.csv', 'date.txt'])
            author = Actor(settings.GIT_AUTHOR_NAME, settings.GIT_AUTHOR_EMAIL)
            index.commit(
                message='update of tweedekamer.nl fracties',
                author=author
            )
            # origin.push()
        except:
            logger.error(traceback.format_exc())
            raise
        logger.info('END')

class CreateCommitParliamentMembersCSV(CronJobBase):
    RUN_AT_TIMES = ['19:00']
    schedule = Schedule(run_at_times=RUN_AT_TIMES)
    code = 'website.cron.CreateCommitParliamentMembersCSV'

    def do(self):
        logger.info('BEGIN')
        try:
            repo = Repo(settings.MEMBERS_REPO_DIR)
            assert not repo.bare
            # origin = repo.create_remote('origin', repo.remotes.origin.url)
            # origin.pull()

            parties = scraper.parliament_members.search_members()
            filepath = os.path.join(settings.MEMBERS_REPO_DIR, 'tweedekamerleden.csv')
            scraper.parliament_members.create_members_csv(parties, filepath)

            changed_files = repo.git.diff(name_only=True)
            if not changed_files:
                logger.info('no changes')
                logger.info('END')
                return

            filepath_date = os.path.join(settings.MEMBERS_REPO_DIR, 'date.txt')
            with open(filepath_date, 'w') as fileout:
                fileout.write(datetime.date.today().strftime('%Y-%m-%d'))

            index = repo.index
            index.add(['tweedekamerleden.csv', 'date.txt'])
            author = Actor(settings.GIT_AUTHOR_NAME, settings.GIT_AUTHOR_EMAIL)
            index.commit(
                message='update of tweedekamer.nl kamerleden',
                author=author
            )
            # origin.push()
        except:
            logger.error(traceback.format_exc())
            raise
        logger.info('END')
<commit_msg>sort dossier ids before writing to file<commit_after>import logging
import os
import traceback
import datetime

from django.conf import settings
from django_cron import CronJobBase, Schedule

from git import Repo, Actor

import scraper.documents
import scraper.political_parties
import scraper.parliament_members

from website import settings

logger = logging.getLogger(__name__)

class CreateCommitWetsvoorstellenIDs(CronJobBase):
    RUN_AT_TIMES = ['20:00']
    schedule = Schedule(run_at_times=RUN_AT_TIMES)
    code = 'website.cron.CreateCommitWetsvoorstellenIDs'

    @staticmethod
    def dossier_ids_to_file(dossier_ids, filepath):
        dossier_ids = sorted(dossier_ids)
        with open(filepath, 'w') as fileout:
            for dossier_id in dossier_ids:
                fileout.write(dossier_id + '\n')

    def do(self):
        logger.info('BEGIN')
        try:
            repo = Repo(settings.WETSVOORSTELLEN_REPO_DIR)
            assert not repo.bare
            # origin = repo.create_remote('origin', repo.remotes.origin.url)
            # origin.pull()

            filename_iaan = 'wetsvoorstellen_dossier_ids_initiatief_aanhangig.txt'
            filepath = os.path.join(settings.WETSVOORSTELLEN_REPO_DIR, filename_iaan)
            dossier_ids = scraper.documents.get_dossier_ids_wetsvoorstellen_initiatief(filter_active=True)
            assert len(dossier_ids) > 105
            self.dossier_ids_to_file(dossier_ids, filepath)

            filename_iaf = 'wetsvoorstellen_dossier_ids_initiatief_afgedaan.txt'
            filepath = os.path.join(settings.WETSVOORSTELLEN_REPO_DIR, filename_iaf)
            dossier_ids = scraper.documents.get_dossier_ids_wetsvoorstellen_initiatief(filter_inactive=True)
            assert len(dossier_ids) > 68
            self.dossier_ids_to_file(dossier_ids, filepath)

            filename_raan = 'wetsvoorstellen_dossier_ids_regering_aanhangig.txt'
            filepath = os.path.join(settings.WETSVOORSTELLEN_REPO_DIR, filename_raan)
            dossier_ids = scraper.documents.get_dossier_ids_wetsvoorstellen_regering(filter_active=True)
            assert len(dossier_ids) > 120
            self.dossier_ids_to_file(dossier_ids, filepath)

            filename_raf = 'wetsvoorstellen_dossier_ids_regering_afgedaan.txt'
            filepath = os.path.join(settings.WETSVOORSTELLEN_REPO_DIR, filename_raf)
            dossier_ids = scraper.documents.get_dossier_ids_wetsvoorstellen_regering(filter_inactive=True)
            assert len(dossier_ids) > 1266
            self.dossier_ids_to_file(dossier_ids, filepath)

            changed_files = repo.git.diff(name_only=True)
            if not changed_files:
                logger.info('no changes')
                logger.info('END')
                return

            filepath_date = os.path.join(settings.WETSVOORSTELLEN_REPO_DIR, 'date.txt')
            with open(filepath_date, 'w') as fileout:
                fileout.write(datetime.date.today().strftime('%Y-%m-%d'))

            index = repo.index
            index.add([filename_iaan, filename_iaf, filename_raan, filename_raf, 'date.txt'])
            author = Actor(settings.GIT_AUTHOR_NAME, settings.GIT_AUTHOR_EMAIL)
            index.commit(
                message='update of wetsvoorstellen dossier ids',
                author=author
            )
            # origin.push()
        except:
            logger.error(traceback.format_exc())
            raise
        logger.info('END')

class CreateCommitPartyCSV(CronJobBase):
    RUN_AT_TIMES = ['19:00']
    schedule = Schedule(run_at_times=RUN_AT_TIMES)
    code = 'website.cron.CreateCommitPartyCSV'

    def do(self):
        logger.info('BEGIN')
        try:
            repo = Repo(settings.PARTIES_REPO_DIR)
            assert not repo.bare
            # origin = repo.create_remote('origin', repo.remotes.origin.url)
            # origin.pull()

            parties = scraper.political_parties.search_parties()
            filepath = os.path.join(settings.PARTIES_REPO_DIR, 'fracties.csv')
            scraper.political_parties.create_parties_csv(parties, filepath)

            changed_files = repo.git.diff(name_only=True)
            if not changed_files:
                logger.info('no changes')
                logger.info('END')
                return

            filepath_date = os.path.join(settings.PARTIES_REPO_DIR, 'date.txt')
            with open(filepath_date, 'w') as fileout:
                fileout.write(datetime.date.today().strftime('%Y-%m-%d'))

            index = repo.index
            index.add(['fracties.csv', 'date.txt'])
            author = Actor(settings.GIT_AUTHOR_NAME, settings.GIT_AUTHOR_EMAIL)
            index.commit(
                message='update of tweedekamer.nl fracties',
                author=author
            )
            # origin.push()
        except:
            logger.error(traceback.format_exc())
            raise
        logger.info('END')

class CreateCommitParliamentMembersCSV(CronJobBase):
    RUN_AT_TIMES = ['19:00']
    schedule = Schedule(run_at_times=RUN_AT_TIMES)
    code = 'website.cron.CreateCommitParliamentMembersCSV'

    def do(self):
        logger.info('BEGIN')
        try:
            repo = Repo(settings.MEMBERS_REPO_DIR)
            assert not repo.bare
            # origin = repo.create_remote('origin', repo.remotes.origin.url)
            # origin.pull()

            parties = scraper.parliament_members.search_members()
            filepath = os.path.join(settings.MEMBERS_REPO_DIR, 'tweedekamerleden.csv')
            scraper.parliament_members.create_members_csv(parties, filepath)

            changed_files = repo.git.diff(name_only=True)
            if not changed_files:
                logger.info('no changes')
                logger.info('END')
                return

            filepath_date = os.path.join(settings.MEMBERS_REPO_DIR, 'date.txt')
            with open(filepath_date, 'w') as fileout:
                fileout.write(datetime.date.today().strftime('%Y-%m-%d'))

            index = repo.index
            index.add(['tweedekamerleden.csv', 'date.txt'])
            author = Actor(settings.GIT_AUTHOR_NAME, settings.GIT_AUTHOR_EMAIL)
            index.commit(
                message='update of tweedekamer.nl kamerleden',
                author=author
            )
            # origin.push()
        except:
            logger.error(traceback.format_exc())
            raise
        logger.info('END')
<|endoftext|>


<commit_before>#!/usr/bin/env python3
"""ATS input converter from 1.2 to main development branch"""

import sys, os
try:
    amanzi_xml = os.path.join(os.environ["AMANZI_SRC_DIR"], "tools","amanzi_xml")
except KeyError:
    pass
else:
    if amanzi_xml not in sys.path:
        sys.path.append(amanzi_xml)

from amanzi_xml.utils import search as asearch
from amanzi_xml.utils import io as aio
from amanzi_xml.utils import errors as aerrors
from amanzi_xml.common import parameter

def manning_boundary_face(xml):
    matches = asearch.findall_path(xml, ["state", "field evaluators", "surface-manning_coefficient", "components"])
    for mann in matches:
        mann.setValue(["cell"])

def update(xml, seb_new=False):
    manning_boundary_face(xml)

if __name__ == "__main__":
    import argparse
    parser = argparse.ArgumentParser(description="Fix a number of changes from ATS input spec 1.2 to the development branch")
    parser.add_argument("infile", help="input filename")

    group = parser.add_mutually_exclusive_group()
    group.add_argument("-i", "--inplace", action="store_true", help="fix file in place")
    group.add_argument("-o", "--outfile", help="output filename")

    args = parser.parse_args()

    print("Converting file: %s"%args.infile)
    xml = aio.fromFile(args.infile, True)
    update(xml)
    if args.inplace:
        aio.toFile(xml, args.infile)
    else:
        aio.toFile(xml, args.outfile)
    sys.exit(0)

<commit_msg>updates converter to deal with changes from "depth key" --> "mobile depth key" in overland conductivity evals<commit_after>#!/usr/bin/env python3
"""ATS input converter from 1.2 to main development branch"""

import sys, os
try:
    amanzi_xml = os.path.join(os.environ["AMANZI_SRC_DIR"], "tools","amanzi_xml")
except KeyError:
    pass
else:
    if amanzi_xml not in sys.path:
        sys.path.append(amanzi_xml)

from amanzi_xml.utils import search as asearch
from amanzi_xml.utils import io as aio
from amanzi_xml.utils import errors as aerrors
from amanzi_xml.common import parameter

def manning_boundary_face(xml):
    matches = asearch.findall_path(xml, ["state", "field evaluators", "surface-manning_coefficient", "components"]) \
        + asearch.findall_path(xml, ["state", "field evaluators", "surface_star-manning_coefficient", "components"])
    for mann in matches:
        mann.setValue(["cell"])

def mobile_depth(xml):
    matches = asearch.findall_path(xml, ["state", "field evaluators", "surface-overland_conductivity", "depth key"]) \
        + asearch.findall_path(xml, ["state", "field evaluators", "surface_star-overland_conductivity", "depth key"])
    for md in matches:
        md.setName("mobile depth key")

def update(xml, seb_new=False):
    manning_boundary_face(xml)
    mobile_depth(xml)

if __name__ == "__main__":
    import argparse
    parser = argparse.ArgumentParser(description="Fix a number of changes from ATS input spec 1.2 to the development branch")
    parser.add_argument("infile", help="input filename")

    group = parser.add_mutually_exclusive_group()
    group.add_argument("-i", "--inplace", action="store_true", help="fix file in place")
    group.add_argument("-o", "--outfile", help="output filename")

    args = parser.parse_args()

    print("Converting file: %s"%args.infile)
    xml = aio.fromFile(args.infile, True)
    update(xml)
    if args.inplace:
        aio.toFile(xml, args.infile)
    else:
        aio.toFile(xml, args.outfile)
    sys.exit(0)

<|endoftext|>


<commit_before># This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

import argparse
import csv
import os
import sys

import requests

from .generator import SignatureGenerator
from .utils import convert_to_crash_data, parse_crashid

DESCRIPTION = """
Given one or more crash ids via command line or stdin (one per line), pulls down information from
Socorro, generates signatures, and prints signature information.
"""

EPILOG = """
Note: In order for the SignatureJitCategory rule to work, you need a valid API token from
Socorro that has "View Personally Identifiable Information" permission.
"""

# FIXME(willkg): This hits production. We might want it configurable.
API_URL = "https://crash-stats.mozilla.org/api"

class OutputBase:
    """Base class for outputter classes

    Outputter classes are context managers. If they require start/top or begin/end semantics, they
    should implement ``__enter__`` and ``__exit__``.

    Otherwise they can just implement ``data`` and should be fine.

    """

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        pass

    def warning(self, line):
        """Prints out a warning line to stderr

        :arg str line: the line to print to stderr

        """
        print("WARNING: %s" % line, file=sys.stderr)

    def data(self, crash_id, old_sig, result, verbose):
        """Outputs a data point

        :arg str crash_id: the crash id for the signature generated
        :arg str old_sig: the old signature retrieved in the processed crash
        :arg Result result: the signature result
        :arg boolean verbose: whether or not to be verbose

        """
        pass

class TextOutput(OutputBase):
    def data(self, crash_id, old_sig, result, verbose):
        print("Crash id: %s" % crash_id)
        print("Original: %s" % old_sig)
        print("New:      %s" % result.signature)
        print("Same?:    %s" % (old_sig == result.signature))

        if result.notes:
            print("Notes:    (%d)" % len(result.notes))
            for note in result.notes:
                print("          %s" % note)
        if verbose and result.debug_log:
            print("Debug:    (%d)" % len(result.debug_log))
            for item in result.debug_log:
                print("          %s" % item)

class CSVOutput(OutputBase):
    def __enter__(self):
        self.out = csv.writer(sys.stdout, quoting=csv.QUOTE_ALL)
        self.out.writerow(["crashid", "old", "new", "same?", "notes"])
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        self.out = None

    def data(self, crash_id, old_sig, result, verbose):
        self.out.writerow(
            [
                crash_id,
                old_sig,
                result.signature,
                str(old_sig == result.signature),
                result.notes,
            ]
        )

def fetch(endpoint, crash_id, api_token=None):
    kwargs = {"params": {"crash_id": crash_id}}
    if api_token:
        kwargs["headers"] = {"Auth-Token": api_token}

    return requests.get(API_URL + endpoint, **kwargs)

def main(argv=None):
    """Takes crash data via args and generates a Socorro signature"""
    parser = argparse.ArgumentParser(description=DESCRIPTION, epilog=EPILOG)
    parser.add_argument(
        "-v", "--verbose", help="increase output verbosity", action="store_true"
    )
    parser.add_argument("--format", help="specify output format: csv, text (default)")
    parser.add_argument(
        "--different-only",
        dest="different",
        action="store_true",
        help="limit output to just the signatures that changed",
    )
    parser.add_argument(
        "crashids",
        metavar="crashid",
        nargs="*",
        help="crash id to generate signatures for",
    )

    if argv is None:
        args = parser.parse_args()
    else:
        args = parser.parse_args(argv)

    if args.format == "csv":
        outputter = CSVOutput
    else:
        outputter = TextOutput

    api_token = os.environ.get("SOCORRO_API_TOKEN", "")

    generator = SignatureGenerator()
    if args.crashids:
        crashids_iterable = args.crashids
    elif not sys.stdin.isatty():
        # If a script is piping to this script, then isatty() returns False. If
        # there is no script piping to this script, then isatty() returns True
        # and if we do list(sys.stdin), it'll block waiting for input.
        crashids_iterable = list(sys.stdin)
    else:
        crashids_iterable = []

    if not crashids_iterable:
        parser.print_help()
        return 0

    with outputter() as out:
        for crash_id in crashids_iterable:
            crash_id = parse_crashid(crash_id.strip())

            resp = fetch("/RawCrash/", crash_id, api_token)
            if resp.status_code == 404:
                out.warning("%s: does not exist." % crash_id)
                continue
            if resp.status_code == 429:
                out.warning("API rate limit reached. %s" % resp.content)
                # FIXME(willkg): Maybe there's something better we could do here. Like maybe wait a
                # few minutes.
                return 1
            if resp.status_code == 500:
                out.warning("HTTP 500: %s" % resp.content)
                continue

            raw_crash = resp.json()

            # If there's an error in the raw crash, then something is wrong--probably with the API
            # token. So print that out and exit.
            if "error" in raw_crash:
                out.warning("Error fetching raw crash: %s" % raw_crash["error"])
                return 1

            resp = fetch("/ProcessedCrash/", crash_id, api_token)
            if resp.status_code == 404:
                out.warning("%s: does not have processed crash." % crash_id)
                continue
            if resp.status_code == 429:
                out.warning("API rate limit reached. %s" % resp.content)
                # FIXME(willkg): Maybe there's something better we could do here. Like maybe wait a
                # few minutes.
                return 1
            if resp.status_code == 500:
                out.warning("HTTP 500: %s" % resp.content)
                continue

            processed_crash = resp.json()

            # If there's an error in the processed crash, then something is wrong--probably with the
            # API token. So print that out and exit.
            if "error" in processed_crash:
                out.warning(
                    "Error fetching processed crash: %s" % processed_crash["error"]
                )
                return 1

            old_signature = processed_crash["signature"]
            crash_data = convert_to_crash_data(raw_crash, processed_crash)

            result = generator.generate(crash_data)

            if not args.different or old_signature != result.signature:
                out.data(crash_id, old_signature, result, args.verbose)
<commit_msg>Fix error handling in signature cmd<commit_after># This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

import argparse
import csv
import os
import sys

import requests

from .generator import SignatureGenerator
from .utils import convert_to_crash_data, parse_crashid

DESCRIPTION = """
Given one or more crash ids via command line or stdin (one per line), pulls down information from
Socorro, generates signatures, and prints signature information.
"""

EPILOG = """
Note: In order for the SignatureJitCategory rule to work, you need a valid API token from
Socorro that has "View Personally Identifiable Information" permission.
"""

# FIXME(willkg): This hits production. We might want it configurable.
API_URL = "https://crash-stats.mozilla.org/api"

class OutputBase:
    """Base class for outputter classes

    Outputter classes are context managers. If they require start/top or begin/end semantics, they
    should implement ``__enter__`` and ``__exit__``.

    Otherwise they can just implement ``data`` and should be fine.

    """

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        pass

    def warning(self, line):
        """Prints out a warning line to stderr

        :arg str line: the line to print to stderr

        """
        print("WARNING: %s" % line, file=sys.stderr)

    def data(self, crash_id, old_sig, result, verbose):
        """Outputs a data point

        :arg str crash_id: the crash id for the signature generated
        :arg str old_sig: the old signature retrieved in the processed crash
        :arg Result result: the signature result
        :arg boolean verbose: whether or not to be verbose

        """
        pass

class TextOutput(OutputBase):
    def data(self, crash_id, old_sig, result, verbose):
        print("Crash id: %s" % crash_id)
        print("Original: %s" % old_sig)
        print("New:      %s" % result.signature)
        print("Same?:    %s" % (old_sig == result.signature))

        if result.notes:
            print("Notes:    (%d)" % len(result.notes))
            for note in result.notes:
                print("          %s" % note)
        if verbose and result.debug_log:
            print("Debug:    (%d)" % len(result.debug_log))
            for item in result.debug_log:
                print("          %s" % item)

class CSVOutput(OutputBase):
    def __enter__(self):
        self.out = csv.writer(sys.stdout, quoting=csv.QUOTE_ALL)
        self.out.writerow(["crashid", "old", "new", "same?", "notes"])
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        self.out = None

    def data(self, crash_id, old_sig, result, verbose):
        self.out.writerow(
            [
                crash_id,
                old_sig,
                result.signature,
                str(old_sig == result.signature),
                result.notes,
            ]
        )

def fetch(endpoint, crash_id, api_token=None):
    kwargs = {"params": {"crash_id": crash_id}}
    if api_token:
        kwargs["headers"] = {"Auth-Token": api_token}

    return requests.get(API_URL + endpoint, **kwargs)

def main(argv=None):
    """Takes crash data via args and generates a Socorro signature"""
    parser = argparse.ArgumentParser(description=DESCRIPTION, epilog=EPILOG)
    parser.add_argument(
        "-v", "--verbose", help="increase output verbosity", action="store_true"
    )
    parser.add_argument("--format", help="specify output format: csv, text (default)")
    parser.add_argument(
        "--different-only",
        dest="different",
        action="store_true",
        help="limit output to just the signatures that changed",
    )
    parser.add_argument(
        "crashids",
        metavar="crashid",
        nargs="*",
        help="crash id to generate signatures for",
    )

    if argv is None:
        args = parser.parse_args()
    else:
        args = parser.parse_args(argv)

    if args.format == "csv":
        outputter = CSVOutput
    else:
        outputter = TextOutput

    api_token = os.environ.get("SOCORRO_API_TOKEN", "")

    generator = SignatureGenerator()
    if args.crashids:
        crashids_iterable = args.crashids
    elif not sys.stdin.isatty():
        # If a script is piping to this script, then isatty() returns False. If
        # there is no script piping to this script, then isatty() returns True
        # and if we do list(sys.stdin), it'll block waiting for input.
        crashids_iterable = list(sys.stdin)
    else:
        crashids_iterable = []

    if not crashids_iterable:
        parser.print_help()
        return 0

    with outputter() as out:
        for crash_id in crashids_iterable:
            crash_id = crash_id.strip()
            parsed_crash_id = parse_crashid(crash_id)
            if not parsed_crash_id:
                out.warning("Error: %r is not a valid crash id" % crash_id)
                continue

            crash_id = parsed_crash_id

            resp = fetch("/RawCrash/", crash_id, api_token)
            if resp.status_code == 404:
                out.warning("%s: does not exist." % crash_id)
                continue
            if resp.status_code == 429:
                out.warning("API rate limit reached. %s" % resp.content)
                # FIXME(willkg): Maybe there's something better we could do here. Like maybe wait a
                # few minutes.
                return 1
            if resp.status_code == 500:
                out.warning("HTTP 500: %s" % resp.content)
                continue

            raw_crash = resp.json()

            # If there's an error in the raw crash, then something is wrong--probably with the API
            # token. So print that out and exit.
            if "errors" in raw_crash:
                out.warning("Error fetching raw crash: %s" % raw_crash["errors"])
                return 1

            resp = fetch("/ProcessedCrash/", crash_id, api_token)
            if resp.status_code == 404:
                out.warning("%s: does not have processed crash." % crash_id)
                continue
            if resp.status_code == 429:
                out.warning("API rate limit reached. %s" % resp.content)
                # FIXME(willkg): Maybe there's something better we could do here. Like maybe wait a
                # few minutes.
                return 1
            if resp.status_code == 500:
                out.warning("HTTP 500: %s" % resp.content)
                continue

            processed_crash = resp.json()

            # If there's an error in the processed crash, then something is wrong--probably with the
            # API token. So print that out and exit.
            if "errors" in processed_crash:
                out.warning(
                    "Error fetching processed crash: %s" % processed_crash["errors"]
                )
                return 1

            old_signature = processed_crash["signature"]
            crash_data = convert_to_crash_data(raw_crash, processed_crash)

            result = generator.generate(crash_data)

            if not args.different or old_signature != result.signature:
                out.data(crash_id, old_signature, result, args.verbose)
<|endoftext|>


<commit_before>##Some points to mention...
##
##The model knows nothing about the view or the controller.
##The view knows nothing about the controller or the model.
##The controller understands both the model and the view.
##
##The model uses observables, essentially when important data is changed,
##any interested listener gets notified through a callback mechanism.
##
##The following opens up two windows, one that reports how much money you
##have, and one that has two buttons, one to add money and one to remove
##money.
##
##The important thing is that the controller is set up to monitor changes
##in the model.  In this case the controller notices that you clicked a
##button and modifies the money in the model which then sends out a
##message that it has changed.  The controller notices this and updates
##the widgets.
##
##The cool thing is that anything modifying the model will notify the
##controller.  In this case it is the controller modifying the model, but it
##could be anything else, even another controller off in the distance
##looking at something else.
##
##The main idea is that you give a controller the model and view that it
##needs, but the model's can be shared between controllers so that when
##the model is updated, all associated views are updated. -Brian Kelley
##
## following is a Tkinter approximation of the original example.

import tkinter as tk
from tkinter.filedialog import asksaveasfilename
import os
from tksimpledialog import Dialog
from amcquestion import AMCQuestion
from amc_question_dialogs import CreateQuestionDialog, EditQuestionDialog

class Observable:
    '''A mutable element of the model that can notify the controller when
    modified
    '''
    def __init__(self, initialValue=None):
        self.data = initialValue
        self.callbacks = {}

    def addCallback(self, func):
        self.callbacks[func] = 1

    def delCallback(self, func):
        del self.callback[func]

    def _docallbacks(self):
        for func in self.callbacks:
             func(self.data)

    def set(self, data):
        self.data = data
        self._docallbacks()

    def get(self):
        return self.data

    def unset(self):
        self.data = None

class Test:
    def __init__(self):
        self.string = ""
        self.indent = 0

    def newLine(self, newlines, string):
        self.string += "\n"*newlines + "  "*self.indent + string

    def increaseIndent(self):
        self.indent += 1

    def decreaseIndent(self):
        self.indent = max(0, self.indent - 1)

def sanitizeLaTeX(s):
    """Converts LaTeX control characters from text input to the appropriate
    LaTeX commands (usually involves escaping with backslash).

    """

    # Characters to escape with a backslash
    chars_to_escape = ['#', '$', '%', '&', '_', '^', '{', '}']

    # Split the string at literal backslashes to avoid problem of replacing the
    # backslashes inserted later as part of escape sequences.
    segments = s.split('\\')
    for n in range(len(segments)):
        for char in chars_to_escape:
            segments[n] = segments[n].replace(char, '\\' + char)

        # Replace characters that require more than simple backslash escapes.
        segments[n] = segments[n].replace('~', '\\textasciitilde{}')

    # Replace the literal backslashes used to split the string with proper
    # escape sequence.
    sanitized = '\\textbackslash{}'.join(segments)

    return sanitized

class Model:
    def __init__(self):
        self.myQuestions = Observable([])
        self.saveFile = Observable(os.getcwd() + "/amc_questions_output.tex")
        self.papertypes = ["letterpaper", "a4paper"]
        self.papertype = self.papertypes[0]

    def addQuestion(self, question):
        self.myQuestions.set(self.myQuestions.get() +
            [question])

    def removeQuestion(self, index):
        questions = self.myQuestions.get()
        del questions[index]
        self.myQuestions.set(questions)

    def changeQuestion(self, index, question):
        questions = self.myQuestions.get()
        questions[index] = question
        self.myQuestions.set(questions)

    def setFile(self, filename):
        self.saveFile.set(filename)

    def newLine(newlines, indent, string):
        return "\n"*newlines + "  "*indent + string

    def writeTest(self):
        test = Test()
        test.newLine(0, "%Created with AMC Question Creator")
        papertype = self.papertypes[0]
        test.newLine(1, "\\documentclass[" + papertype + "]{article}")

        test.newLine(2, "\\usepackage[utf8x]{inputenc}")
        test.newLine(1, "\\usepackage[T1]{fontenc}")

        # Select options for the AMC package
        options = ""
        box = True
        completemulti = True
        separateanswersheet = True
        if box:
            options += ",box"
        if completemulti:
            options += ",completemulti"
        if separateanswersheet:
            options += ",separateanswersheet"
        options = options[1:] # Remove leading comma

        test.newLine(2,
            "\\usepackage[" + options + "]{automultiplechoice}")

        test.newLine(2, "\\begin{document}")

        # Imagine this must seed a pseudorandom number generator. Am not
        # randomizing questions right now, so will not use.
        random_seed = ""
        # random_seed = "1237893"
        if len(random_seed) > 0:
            test.newLine(2, indent, "\\AMCrandomseed{" + random_seed + "}")

        # Redefine how questions are defined on answer sheet
        form_question_format = "\\vspace{\\AMCformVSpace}\\par " + \
            "{\\sc Question #1:} "
        if len(form_question_format) > 0:
            test.newLine(2,
                "\n\n\\def\\AMCformQuestion#1{" + form_question_format + "}")

        copies = 10
        test.newLine(2, "\\onecopy{" + str(copies) + "}{")

        test.newLine(2, "%%% beginning of the test sheet header:")

        title = "QCM"
        test.newLine(2, "\\noindent{\\bf " + sanitizeLaTeX(title) +
            " \\hfill TEST}")

        date = "Mar. 26, 2014"
        test.newLine(2, "\\vspace*{.5cm}")
        test.newLine(1, "\\begin{minipage}{.4\\linewidth}")
        test.increaseIndent()
        test.newLine(1, "\\centering\\large\\bf Test\\\\ Examination on " +
            sanitizeLaTeX(date))
        test.decreaseIndent()
        test.newLine(1, "\\end{minipage}")

        test.newLine(2, "\\begin{center}\\em")
        # Consider using ISO time variable here.
        time = "10 minutes"
        test.newLine(1, "\\Time: " + time)
        test.increaseIndent()
        instructions = "No documents allowed. The use of electronic" + \
                       " calculators is forbidden."
        test.newLine(2, instructions)
        test.decreaseIndent()
        test.newLine(2, "\\end{center}")
        test.newLine(1, "\\vspace{1ex}")

        test.newLine(2, "%%% end of the header")

        # Write the questions themselves
        questions = self.myQuestions.get()
        for question in questions:
            test.newLine(2, "\\begin{question}{" + question.get_label() + "}")
            test.increaseIndent()
            test.newLine(1, sanitizeLaTeX(question.get_question()))
            test.newLine(1, "\\begin{choices}")
            answers = question.get_answers()
            marks = ["correct" if n == question.get_correct() else "wrong" for
                n in range(len(answers))]
            test.increaseIndent()
            for n in range(len(answers)):
              test.newLine(1, "\\" + marks[n] + "choice{" +
                  sanitizeLaTeX(answers[n]) + "}")
            test.decreaseIndent()
            test.newLine(1, "\\end{choices}")
            test.decreaseIndent()
            test.newLine(1, "\\end{question}")

        # Prepare answer sheet.
        test.newLine(2, "\\AMCcleardoublepage")
        test.newLine(2, "\\AMCformBegin")

        test.newLine(2, "%%% beginning of the answer sheet header")

        return test.string

class View(tk.Toplevel):
    def __init__(self, master, papertypes):
        tk.Toplevel.__init__(self, master)
        self.protocol('WM_DELETE_WINDOW', self.master.destroy)

        cur_row = 0

        tk.Label(self, text='AMC questions').grid(row=cur_row, columnspan=3)

        cur_row += 1

        self.paperSize = tk.StringVar(self)
        self.paperSize.set(papertypes[0])
        self.paperOptionMenu = tk.OptionMenu(self, self.paperSize,
            *papertypes)
        self.paperOptionMenu.grid(row=cur_row, column=1, sticky='w')

        cur_row += 1

        self.newButton = tk.Button(self, text="New question")
        self.newButton.grid(row=cur_row, sticky='w')

        self.editButton = tk.Button(self, text="Edit question")
        self.editButton.grid(row=cur_row, column=1, sticky='w')

        self.deleteButton = tk.Button(self, text="Delete question")
        self.deleteButton.grid(row=cur_row, column=2, sticky='w')

        cur_row += 1

        self.questionListbox = tk.Listbox(self)
        self.questionListbox.grid(row=cur_row, columnspan=3)

        cur_row += 1

        self.saveLabel = tk.Label(self)
        self.saveLabel.grid(row=cur_row, columnspan=3, sticky='w')

        cur_row += 1

        self.selectFileButton = tk.Button(self, text="Choose file")
        self.selectFileButton.grid(row=cur_row, column=1, sticky='w')

        self.saveFileButton = tk.Button(self, text="Save")
        self.saveFileButton.grid(row=cur_row, column=2, sticky='w')

    def RefreshQuestions(self, questions):
        self.questionListbox.delete(0, 'end')
        for question in questions:
            self.questionListbox.insert('end', question.get_label())

class Controller:
    def __init__(self, root):
        self.model = Model()
        self.model.myQuestions.addCallback(self.QuestionsChanged)
        self.model.saveFile.addCallback(self.FileChanged)
        self.view = View(root, self.model.papertypes)
        self.ConfigureView()
        self.QuestionsChanged(self.model.myQuestions.get())
        self.FileChanged(self.model.saveFile.get())

    def ConfigureView(self):
        #self.view.paperOptionMenu.config(command=self.UpdatePaperType)
        self.view.newButton.config(command=self.AddQuestion)
        self.view.deleteButton.config(command=self.RemoveQuestion)
        self.view.editButton.config(command=self.EditQuestion)
        self.view.selectFileButton.config(command=self.SelectFile)
        self.view.saveFileButton.config(command=self.SaveFile)

    def UpdatePaperType(self, papertype):
        self.model.papertype = papertype

    def AddQuestion(self):
        d = CreateQuestionDialog(self.view, title="Create Question")
        if d.new_question:
            self.model.addQuestion(d.new_question)

    def RemoveQuestion(self):
        selectionTuple = self.view.questionListbox.curselection()
        # Only remove a question when one is selected
        if len(selectionTuple) > 0:
            self.model.removeQuestion(int(selectionTuple[0]))

    def EditQuestion(self):
        selectionTuple = self.view.questionListbox.curselection()
        # Only open edit dialog when a question is selected
        if len(selectionTuple) > 0:
            d = EditQuestionDialog(self.model.myQuestions.get()[int(
                selectionTuple[0])], self.view, title="Edit Question")
            # Only replace the selected question if an edit was committed
            if d.updated_question:
                self.model.changeQuestion(int(selectionTuple[0]),
                    d.updated_question)

    def SelectFile(self):
        filename = asksaveasfilename(initialfile=
            "amc_questions_output.tex")
        if filename != "":
            self.model.setFile(filename)

    def SaveFile(self):
        questionsString = self.model.writeTest()
        with open(self.model.saveFile.get(), 'w') as f:
            f.write(questionsString)
        f.closed

    def QuestionsChanged(self, questions):
        self.view.RefreshQuestions(questions)

    def FileChanged(self, filename):
        self.view.saveLabel.config(text=filename)

if __name__ == '__main__':
    root = tk.Tk()
    root.withdraw()
    app = Controller(root)
    root.mainloop()
<commit_msg>Added answer sheet material to the test writer.<commit_after>##Some points to mention...
##
##The model knows nothing about the view or the controller.
##The view knows nothing about the controller or the model.
##The controller understands both the model and the view.
##
##The model uses observables, essentially when important data is changed,
##any interested listener gets notified through a callback mechanism.
##
##The following opens up two windows, one that reports how much money you
##have, and one that has two buttons, one to add money and one to remove
##money.
##
##The important thing is that the controller is set up to monitor changes
##in the model.  In this case the controller notices that you clicked a
##button and modifies the money in the model which then sends out a
##message that it has changed.  The controller notices this and updates
##the widgets.
##
##The cool thing is that anything modifying the model will notify the
##controller.  In this case it is the controller modifying the model, but it
##could be anything else, even another controller off in the distance
##looking at something else.
##
##The main idea is that you give a controller the model and view that it
##needs, but the model's can be shared between controllers so that when
##the model is updated, all associated views are updated. -Brian Kelley
##
## following is a Tkinter approximation of the original example.

import tkinter as tk
from tkinter.filedialog import asksaveasfilename
import os
from tksimpledialog import Dialog
from amcquestion import AMCQuestion
from amc_question_dialogs import CreateQuestionDialog, EditQuestionDialog

class Observable:
    '''A mutable element of the model that can notify the controller when
    modified
    '''
    def __init__(self, initialValue=None):
        self.data = initialValue
        self.callbacks = {}

    def addCallback(self, func):
        self.callbacks[func] = 1

    def delCallback(self, func):
        del self.callback[func]

    def _docallbacks(self):
        for func in self.callbacks:
             func(self.data)

    def set(self, data):
        self.data = data
        self._docallbacks()

    def get(self):
        return self.data

    def unset(self):
        self.data = None

class Test:
    def __init__(self):
        self.string = ""
        self.indent = 0

    def newLine(self, newlines, string):
        self.string += "\n"*newlines + "  "*self.indent + string

    def increaseIndent(self):
        self.indent += 1

    def decreaseIndent(self):
        self.indent = max(0, self.indent - 1)

def sanitizeLaTeX(s):
    """Converts LaTeX control characters from text input to the appropriate
    LaTeX commands (usually involves escaping with backslash).

    """

    # Characters to escape with a backslash
    chars_to_escape = ['#', '$', '%', '&', '_', '^', '{', '}']

    # Split the string at literal backslashes to avoid problem of replacing the
    # backslashes inserted later as part of escape sequences.
    segments = s.split('\\')
    for n in range(len(segments)):
        for char in chars_to_escape:
            segments[n] = segments[n].replace(char, '\\' + char)

        # Replace characters that require more than simple backslash escapes.
        segments[n] = segments[n].replace('~', '\\textasciitilde{}')

    # Replace the literal backslashes used to split the string with proper
    # escape sequence.
    sanitized = '\\textbackslash{}'.join(segments)

    return sanitized

class Model:
    def __init__(self):
        self.myQuestions = Observable([])
        self.saveFile = Observable(os.getcwd() + "/amc_questions_output.tex")
        self.papertypes = ["letterpaper", "a4paper"]
        self.papertype = self.papertypes[0]

    def addQuestion(self, question):
        self.myQuestions.set(self.myQuestions.get() +
            [question])

    def removeQuestion(self, index):
        questions = self.myQuestions.get()
        del questions[index]
        self.myQuestions.set(questions)

    def changeQuestion(self, index, question):
        questions = self.myQuestions.get()
        questions[index] = question
        self.myQuestions.set(questions)

    def setFile(self, filename):
        self.saveFile.set(filename)

    def newLine(newlines, indent, string):
        return "\n"*newlines + "  "*indent + string

    def writeTest(self):
        test = Test()
        test.newLine(0, "%Created with AMC Question Creator")
        papertype = self.papertypes[0]
        test.newLine(1, "\\documentclass[" + papertype + "]{article}")

        test.newLine(2, "\\usepackage[utf8x]{inputenc}")
        test.newLine(1, "\\usepackage[T1]{fontenc}")

        # Select options for the AMC package
        options = ""
        box = True
        completemulti = True
        separateanswersheet = True
        if box:
            options += ",box"
        if completemulti:
            options += ",completemulti"
        if separateanswersheet:
            options += ",separateanswersheet"
        options = options[1:] # Remove leading comma

        test.newLine(2,
            "\\usepackage[" + options + "]{automultiplechoice}")

        test.newLine(2, "\\begin{document}")

        # Imagine this must seed a pseudorandom number generator. Am not
        # randomizing questions right now, so will not use.
        random_seed = ""
        # random_seed = "1237893"
        if len(random_seed) > 0:
            test.newLine(2, "\\AMCrandomseed{" + random_seed + "}")

        # Redefine how questions are defined on answer sheet
        form_question_format = "\\vspace{\\AMCformVSpace}\\par " + \
            "{\\sc Question #1:} "
        if len(form_question_format) > 0:
            test.newLine(2,
                "\n\n\\def\\AMCformQuestion#1{" + form_question_format + "}")

        copies = 10
        test.newLine(2, "\\onecopy{" + str(copies) + "}{")

        test.newLine(2, "%%% beginning of the test sheet header:")

        title = "QCM"
        test.newLine(2, "\\noindent{\\bf " + sanitizeLaTeX(title) +
            " \\hfill TEST}")

        date = "Mar. 26, 2014"
        test.newLine(2, "\\vspace*{.5cm}")
        test.newLine(1, "\\begin{minipage}{.4\\linewidth}")
        test.increaseIndent()
        test.newLine(1, "\\centering\\large\\bf Test\\\\ Examination on " +
            sanitizeLaTeX(date))
        test.decreaseIndent()
        test.newLine(1, "\\end{minipage}")

        test.newLine(2, "\\begin{center}\\em")
        # Consider using ISO time variable here.
        time = "10 minutes"
        test.newLine(1, "\\Time: " + time)
        test.increaseIndent()
        instructions = "No documents allowed. The use of electronic" + \
                       " calculators is forbidden."
        test.newLine(2, instructions)
        test.decreaseIndent()
        test.newLine(2, "\\end{center}")
        test.newLine(1, "\\vspace{1ex}")

        test.newLine(2, "%%% end of the header")

        # Write the questions themselves
        questions = self.myQuestions.get()
        for question in questions:
            test.newLine(2, "\\begin{question}{" + question.get_label() + "}")
            test.increaseIndent()
            test.newLine(1, sanitizeLaTeX(question.get_question()))
            test.newLine(1, "\\begin{choices}")
            answers = question.get_answers()
            marks = ["correct" if n == question.get_correct() else "wrong" for
                n in range(len(answers))]
            test.increaseIndent()
            for n in range(len(answers)):
              test.newLine(1, "\\" + marks[n] + "choice{" +
                  sanitizeLaTeX(answers[n]) + "}")
            test.decreaseIndent()
            test.newLine(1, "\\end{choices}")
            test.decreaseIndent()
            test.newLine(1, "\\end{question}")

        # Prepare answer sheet.
        test.newLine(2, "\\AMCcleardoublepage")
        test.newLine(2, "\\AMCformBegin")

        test.newLine(2, "%%% beginning of the answer sheet header")

        test.newLine(2, r"{\large\bf Answer sheet:}")
        test.newLine(1, r"\hfill \namefield{\fbox{")
        test.increaseIndent()
        test.newLine(1, r"\begin{minipage}{.5\linewidth}")
        test.increaseIndent()
        test.newLine(1, "Firstname and lastname:")
        test.newLine(2, r"\vspace*{.5cm}\dotfill")
        test.newLine(1, r"\vspace*{1mm}")
        test.decreaseIndent()
        test.newLine(1, r"\end{minipage}")
        test.decreaseIndent()
        test.newLine(1, "}}")

        test.newLine(2, r"\begin{center}")
        test.increaseIndent()
        test.newLine(1, r"\bf\em Answers must be given exclusively on this " +
                     r"sheet:")
        test.newLine(1, "answers given on the other sheets will be ignored.")
        test.decreaseIndent()
        test.newLine(1, r"\end{center}")

        test.newLine(2, "%%% end of the answer sheet header")

        test.newLine(2, r"\AMCform")

        test.newLine(2, r"\clearpage")

        test.newLine(2, "}")

        test.newLine(2, r"\end{document}")

        return test.string

class View(tk.Toplevel):
    def __init__(self, master, papertypes):
        tk.Toplevel.__init__(self, master)
        self.protocol('WM_DELETE_WINDOW', self.master.destroy)

        cur_row = 0

        tk.Label(self, text='AMC questions').grid(row=cur_row, columnspan=3)

        cur_row += 1

        self.paperSize = tk.StringVar(self)
        self.paperSize.set(papertypes[0])
        self.paperOptionMenu = tk.OptionMenu(self, self.paperSize,
            *papertypes)
        self.paperOptionMenu.grid(row=cur_row, column=1, sticky='w')

        cur_row += 1

        self.newButton = tk.Button(self, text="New question")
        self.newButton.grid(row=cur_row, sticky='w')

        self.editButton = tk.Button(self, text="Edit question")
        self.editButton.grid(row=cur_row, column=1, sticky='w')

        self.deleteButton = tk.Button(self, text="Delete question")
        self.deleteButton.grid(row=cur_row, column=2, sticky='w')

        cur_row += 1

        self.questionListbox = tk.Listbox(self)
        self.questionListbox.grid(row=cur_row, columnspan=3)

        cur_row += 1

        self.saveLabel = tk.Label(self)
        self.saveLabel.grid(row=cur_row, columnspan=3, sticky='w')

        cur_row += 1

        self.selectFileButton = tk.Button(self, text="Choose file")
        self.selectFileButton.grid(row=cur_row, column=1, sticky='w')

        self.saveFileButton = tk.Button(self, text="Save")
        self.saveFileButton.grid(row=cur_row, column=2, sticky='w')

    def RefreshQuestions(self, questions):
        self.questionListbox.delete(0, 'end')
        for question in questions:
            self.questionListbox.insert('end', question.get_label())

class Controller:
    def __init__(self, root):
        self.model = Model()
        self.model.myQuestions.addCallback(self.QuestionsChanged)
        self.model.saveFile.addCallback(self.FileChanged)
        self.view = View(root, self.model.papertypes)
        self.ConfigureView()
        self.QuestionsChanged(self.model.myQuestions.get())
        self.FileChanged(self.model.saveFile.get())

    def ConfigureView(self):
        #self.view.paperOptionMenu.config(command=self.UpdatePaperType)
        self.view.newButton.config(command=self.AddQuestion)
        self.view.deleteButton.config(command=self.RemoveQuestion)
        self.view.editButton.config(command=self.EditQuestion)
        self.view.selectFileButton.config(command=self.SelectFile)
        self.view.saveFileButton.config(command=self.SaveFile)

    def UpdatePaperType(self, papertype):
        self.model.papertype = papertype

    def AddQuestion(self):
        d = CreateQuestionDialog(self.view, title="Create Question")
        if d.new_question:
            self.model.addQuestion(d.new_question)

    def RemoveQuestion(self):
        selectionTuple = self.view.questionListbox.curselection()
        # Only remove a question when one is selected
        if len(selectionTuple) > 0:
            self.model.removeQuestion(int(selectionTuple[0]))

    def EditQuestion(self):
        selectionTuple = self.view.questionListbox.curselection()
        # Only open edit dialog when a question is selected
        if len(selectionTuple) > 0:
            d = EditQuestionDialog(self.model.myQuestions.get()[int(
                selectionTuple[0])], self.view, title="Edit Question")
            # Only replace the selected question if an edit was committed
            if d.updated_question:
                self.model.changeQuestion(int(selectionTuple[0]),
                    d.updated_question)

    def SelectFile(self):
        filename = asksaveasfilename(initialfile=
            "amc_questions_output.tex")
        if filename != "":
            self.model.setFile(filename)

    def SaveFile(self):
        questionsString = self.model.writeTest()
        with open(self.model.saveFile.get(), 'w') as f:
            f.write(questionsString)
        f.closed

    def QuestionsChanged(self, questions):
        self.view.RefreshQuestions(questions)

    def FileChanged(self, filename):
        self.view.saveLabel.config(text=filename)

if __name__ == '__main__':
    root = tk.Tk()
    root.withdraw()
    app = Controller(root)
    root.mainloop()
<|endoftext|>


<commit_before>import time
import logging

from wia import Wia
from wia.rest_client import post, get, put, delete
from wia.error import WiaError, WiaValidationError, WiaUnauthorisedError, WiaForbiddenError, WiaNotFoundError

class WiaResource(object):
    @classmethod
    def is_success(self, response):
        if response.status_code == 200 or response.status_code == 201:
            return True
        else:
            return False

    @classmethod
    def error_response(self, response):
        if response.status_code == 400:
            return WiaValidationError(response)
        elif response.status_code == 401:
            return WiaUnauthorisedError(response)
        elif response.status_code == 403:
            return WiaForbiddenError(response)
        elif response.status_code == 404:
            return WiaNotFoundError(response)
        else:
            return WiaError(response)

class WiaResourceDelete(WiaResource):
    def __init__(self, **kwargs):
        self.id = (kwargs['id'] if 'id' in kwargs else None)
        self.deleted = (kwargs['deleted'] if 'deleted' in kwargs else None)

class Device(WiaResource):
    def __init__(self, **kwargs):
        self.id = (kwargs['id'] if 'id' in kwargs else None)
        self.name = (kwargs['name'] if 'name' in kwargs else None)
        self.sensors = (kwargs['sensors'] if 'sensors' in kwargs else None)
        self.events = (kwargs['events'] if 'events' in kwargs else None)
        self.location = (kwargs['location'] if 'location' in kwargs else None)
        self.public = (kwargs['public'] if 'public' in kwargs else None)
        self.isOnline = (kwargs['isOnline'] if 'isOnline' in kwargs else None)
        self.createdAt = (kwargs['createdAt'] if 'createdAt' in kwargs else None)
        self.updatedAt = (kwargs['updatedAt'] if 'updatedAt' in kwargs else None)

    @classmethod
    def create(self, **kwargs):
        path = 'devices'
        response = post(path, kwargs)
        if is_success(response):
            return Device(**response.json())
        else:
            return error_response(response)

    @classmethod
    def retrieve(self, id):
        path = 'devices/' + id
        response = get(path)
        return Device(**response.json())

    def save(self):
        path = 'devices/' + self.id
        return put(path, name=self.name)

    def delete(self):
        path = 'devices/' + self.id
        repsonse = delete(path)

        if WiaResource.is_success(response):
            return WiaResourceDelete(**response.json())
        else:
            return WiaResource.error_response(response)

    @classmethod
    def list(self, **kwargs):
        list_devices = get('devices', **kwargs)
        for device in list_devices['devices']:
            data = device
            logging.debug("Device: %s", data)
        logging.debug("Count: %s", list_devices['count'])
        return list_devices

class Event(WiaResource):
    def __init__(self, **kwargs):
        self.id = (kwargs['id'] if 'id' in kwargs else None)
        self.name = (kwargs['name'] if 'name' in kwargs else None)
        self.data = (kwargs['data'] if 'data' in kwargs else None)
        self.file = (kwargs['file'] if 'file' in kwargs else None)
        self.timestamp = (kwargs['timestamp'] if 'timestamp' in kwargs else None)

    @classmethod
    def publish(self, **kwargs):
        path = 'events'
        if not ('file' in kwargs) and Wia().Stream.connected and Wia().client_id is not None:
            topic = 'devices/' + Wia().client_id + '/' + path + '/' + kwargs['name']
            Wia().Stream.publish(topic=topic, **kwargs)
            return Event()
        else:
            response = post(path, kwargs)
            if WiaResource.is_success(response):
                return Event(**response.json())
            else:
                return WiaResource.error_response(response)

    @classmethod
    def subscribe(self, **kwargs):
        device=kwargs['device']
        topic='devices/' + device + '/events/'
        if 'name' in kwargs:
            topic += kwargs['name']
        else:
            topic += '+'
        Wia().Stream.subscribe(topic=topic, func=kwargs['func'])

    @classmethod
    def unsubscribe(self, **kwargs):
        device=kwargs['device']
        topic='devices/' + device + '/events/'
        if 'name' in kwargs:
            topic += kwargs['name']
        else:
            topic += '+'
        Wia().Stream.unsubscribe(topic=topic)

    @classmethod
    def list(self, **kwargs):
        response = get('events', **kwargs)
        if WiaResource.is_success(response):
            responseJson = response.json()
            events = []
            for event in responseJson['events']:
                events.append(Event(**event))
            return {'events':events,'count': responseJson['count']}
        else:
            return WiaResource.error_response(response)

class Sensor(WiaResource):
    def __init__(self, **kwargs):
        self.name = (kwargs['name'] if 'name' in kwargs else None)
        self.data = (kwargs['data'] if 'data' in kwargs else None)
        self.timestamp = (kwargs['timestamp'] if 'timestamp' in kwargs else None)

    @classmethod
    def publish(self, **kwargs):
        path = 'sensors'
        if Wia().Stream.connected and Wia().client_id is not None:
            topic = 'devices/' + Wia().client_id + '/' + path + '/' + kwargs['name']
            Wia().Stream.publish(topic=topic, **kwargs)
            return {}
        else:
            response = post(path, kwargs)
            if WiaResource.is_success(response):
                return Sensor(**response.json())
            else:
                return WiaResource.error_response(response)

    @classmethod
    def subscribe(self, **kwargs):
        device=kwargs['device']
        topic='devices/' + device + '/sensors/'
        if 'name' in kwargs:
            topic += kwargs['name']
        else:
            topic += '+'
        Wia().Stream.subscribe(topic=topic, func=kwargs['func'])

    @classmethod
    def unsubscribe(self, **kwargs):
        device=kwargs['device']
        topic='devices/' + device + '/sensors/'
        if 'name' in kwargs:
            topic += kwargs['name']
        else:
            topic += '+'
        Wia().Stream.unsubscribe(topic=topic)

    @classmethod
    def list(self, **kwargs):
        response = get('sensors', **kwargs)
        if WiaResource.is_success(response):
            responseJson = response.json()
            sensors = []
            for sensor in responseJson['sensors']:
                sensors.append(Sensor(**sensor))
            return {'sensors':sensors,'count': responseJson['count']}
        else:
            return WiaResource.error_response(response)

class Location(WiaResource):
    def __init__(self, **kwargs):
        self.id = (kwargs['id'] if 'id' in kwargs else None)
        self.latitude = (kwargs['latitude'] if 'latitude' in kwargs else None)
        self.longitude = (kwargs['longitude'] if 'longitude' in kwargs else None)
        self.altitude = (kwargs['altitude'] if 'altitude' in kwargs else None)
        self.timestamp = (kwargs['timestamp'] if 'timestamp' in kwargs else None)
        self.recievedTimestamp = (kwargs['recievedTimestamp'] if 'recievedTimestamp' in kwargs else None)

    @classmethod
    def publish(self, **kwargs):
        path = 'locations'
        if Wia().Stream.connected and Wia().client_id is not None:
            topic = 'devices/' + Wia().client_id + '/' + path
            Wia().Stream.publish(topic=topic, **kwargs)
            return {}
        else:
            response = post(path, kwargs)
            if WiaResource.is_success(response):
                return Location(**response.json())
            else:
                return WiaResource.error_response(response)

    @classmethod
    def subscribe(self, **kwargs):
        device = kwargs['device']
        topic = 'devices/' + device + '/locations'
        Wia().Stream.subscribe(topic=topic, func=kwargs['func'])

    @classmethod
    def unsubscribe(self, **kwargs):
        device = kwargs['device']
        topic = 'devices/' + device + '/locations'
        Wia().Stream.unsubscribe(topic=topic)

    @classmethod
    def list(self, **kwargs):
        response = get('locations', **kwargs)
        if WiaResource.is_success(response):
            responseJson = response.json()
            locations = []
            for location in responseJson['locations']:
                locations.append(Location(**location))
            return {'locations':locations,'count': responseJson['count']}
        else:
            return WiaResource.error_response(response)

class Log(WiaResource):
    def __init__(self, **kwargs):
        self.level = (kwargs['level'] if 'level' in kwargs else None)
        self.message = (kwargs['message'] if 'message' in kwargs else None)
        self.data = (kwargs['data'] if 'data' in kwargs else None)
        self.timestamp = (kwargs['timestamp'] if 'timestamp' in kwargs else None)

    @classmethod
    def publish(self, **kwargs):
        path = 'logs'
        if Wia().Stream.connected and Wia().client_id is not None:
            topic = 'devices/' + Wia().client_id + '/' + path + '/' + kwargs['level']
            Wia().Stream.publish(topic=topic, **kwargs)
            return {}
        else:
            response = post(path, kwargs)
            if WiaResource.is_success(response):
                return Log(**response.json())
            else:
                return WiaResource.error_response(response)

    @classmethod
    def subscribe(self, **kwargs):
        device = kwargs['device']
        topic = 'devices/' + device + '/logs'
        Wia().Stream.subscribe(topic=topic, func=kwargs['func'])

    @classmethod
    def unsubscribe(self, **kwargs):
        device = kwargs['device']
        topic = 'devices/' + device + '/logs'
        Wia().Stream.unsubscribe(topic=topic)

    @classmethod
    def list(self, **kwargs):
        response = get('logs', **kwargs)
        if WiaResource.is_success(response):
            responseJson = response.json()
            logs = []
            for log in responseJson['logs']:
                logs.append(Log(**log))
            return {'logs':logs,'count': responseJson['count']}
        else:
            return WiaResource.error_response(response)

class Function(WiaResource):
    def __init__(self, **kwargs):
        self.id = (kwargs['id'] if 'id' in kwargs else None)
        self.name = (kwargs['name'] if 'name' in kwargs else None)
        self.isEnabled = (kwargs['isEnabled'] if 'isEnabled' in kwargs else None)
        self.device = (kwargs['device'] if 'device' in kwargs else None)
        self.enabledAt = (kwargs['enabledAt'] if 'enabledAt' in kwargs else None)
        self.createdAt = (kwargs['createdAt'] if 'createdAt' in kwargs else None)
        self.updatedAt = (kwargs['updatedAt'] if 'updatedAt' in kwargs else None)

    @classmethod
    def create(self, **kwargs):
        path = 'functions'
        response = post(path, kwargs)

        if WiaResource.is_success(response):
            return Function(**response.json())
        else:
            return WiaResource.error_response(response)

    @classmethod
    def delete(self, id):
        path = 'functions/' + id
        repsonse = delete(path)

        if WiaResource.is_success(response):
            return WiaResourceDelete(**response.json())
        else:
            return WiaResource.error_response(response)

    @classmethod
    def call(self, **kwargs):
        if Wia().Stream.connected:
            topic = 'devices/' + kwargs['device'] + '/functions/' + kwargs['func'] + '/call'
            if 'data' not in kwargs:
                Wia().Stream.publish(topic=topic)
            elif type(kwargs['data']) is dict:
                Wia().Stream.publish(topic=topic, **kwargs['data'])
            else:
                data = kwargs['data']
                kwargs.pop('data')
                kwargs['data'] = {'arg': data}
                Wia().Stream.publish(topic=topic, **kwargs['data'])
        else:
            raise Exception("Unable to call function, not connected to stream")

    @classmethod
    def list(self, **kwargs):
        response = get('functions', **kwargs)
        if WiaResource.is_success(response):
            responseJson = response.json()
            functions = []
            for func in responseJson['functions']:
                functions.append(Function(**func))
            return {'functions':functions,'count': responseJson['count']}
        else:
            return WiaResource.error_response(response)

class Customer(WiaResource):
    def __init__(self, **kwargs):
        self.id = (kwargs['id'] if 'id' in kwargs else None)
        self.username = (kwargs['username'] if 'username' in kwargs else None)
        self.email = (kwargs['email'] if 'email' in kwargs else None)
        self.fullName = (kwargs['fullName'] if 'fullName' in kwargs else None)

    @classmethod
    def signup(self, **kwargs):
        response = post('customers/signup', kwargs)
        if WiaResource.is_success(response):
            return Customer(**response.json())
        else:
            return WiaResource.error_response(response)

class AccessToken(WiaResource):
    def __init__(self, **kwargs):
        self.accessToken = (kwargs['accessToken'] if 'accessToken' in kwargs else None)
        self.refreshToken = (kwargs['refreshToken'] if 'refreshToken' in kwargs else None)
        self.tokenType = (kwargs['tokenType'] if 'tokenType' in kwargs else None)
        self.expiresIn = (kwargs['expiresIn'] if 'expiresIn' in kwargs else None)
        self.scope = (kwargs['scope'] if 'scope' in kwargs else None)

    @classmethod
    def create(self, **kwargs):
        response = post('auth/token', kwargs)
        if WiaResource.is_success(response):
            return AccessToken(**response.json())
        else:
            return WiaResource.error_response(response)

class WhoAmI(WiaResource):
    def __init__(self, **kwargs):
        self.contextData = (kwargs['contextData'] if 'contextData' in kwargs else None)
        self.scope = (kwargs['scope'] if 'scope' in kwargs else None)

    @classmethod
    def retrieve(self):
        response = get('whoami')
        if WiaResource.is_success(response):
            return WhoAmI(**response.json())
        else:
            return WiaResource.error_response(response)
<commit_msg>Added additional functions to Customer<commit_after>import time
import logging

from wia import Wia
from wia.rest_client import post, get, put, delete
from wia.error import WiaError, WiaValidationError, WiaUnauthorisedError, WiaForbiddenError, WiaNotFoundError

class WiaResource(object):
    @classmethod
    def is_success(self, response):
        if response.status_code == 200 or response.status_code == 201:
            return True
        else:
            return False

    @classmethod
    def error_response(self, response):
        if response.status_code == 400:
            return WiaValidationError(response)
        elif response.status_code == 401:
            return WiaUnauthorisedError(response)
        elif response.status_code == 403:
            return WiaForbiddenError(response)
        elif response.status_code == 404:
            return WiaNotFoundError(response)
        else:
            return WiaError(response)

class WiaResourceDelete(WiaResource):
    def __init__(self, **kwargs):
        self.id = (kwargs['id'] if 'id' in kwargs else None)
        self.deleted = (kwargs['deleted'] if 'deleted' in kwargs else None)

class Device(WiaResource):
    def __init__(self, **kwargs):
        self.id = (kwargs['id'] if 'id' in kwargs else None)
        self.name = (kwargs['name'] if 'name' in kwargs else None)
        self.sensors = (kwargs['sensors'] if 'sensors' in kwargs else None)
        self.events = (kwargs['events'] if 'events' in kwargs else None)
        self.location = (kwargs['location'] if 'location' in kwargs else None)
        self.public = (kwargs['public'] if 'public' in kwargs else None)
        self.isOnline = (kwargs['isOnline'] if 'isOnline' in kwargs else None)
        self.createdAt = (kwargs['createdAt'] if 'createdAt' in kwargs else None)
        self.updatedAt = (kwargs['updatedAt'] if 'updatedAt' in kwargs else None)

    @classmethod
    def create(self, **kwargs):
        path = 'devices'
        response = post(path, kwargs)
        if is_success(response):
            return Device(**response.json())
        else:
            return error_response(response)

    @classmethod
    def retrieve(self, id):
        path = 'devices/' + id
        response = get(path)
        return Device(**response.json())

    def save(self):
        path = 'devices/' + self.id
        return put(path, name=self.name)

    def delete(self):
        path = 'devices/' + self.id
        repsonse = delete(path)

        if WiaResource.is_success(response):
            return WiaResourceDelete(**response.json())
        else:
            return WiaResource.error_response(response)

    @classmethod
    def list(self, **kwargs):
        response = get('devices', **kwargs)
        if WiaResource.is_success(response):
            responseJson = response.json()
            devices = []
            for device in responseJson['devices']:
                devices.append(Device(**device))
            return {'devices':devices,'count': responseJson['count']}
        else:
            return WiaResource.error_response(response)

class Event(WiaResource):
    def __init__(self, **kwargs):
        self.id = (kwargs['id'] if 'id' in kwargs else None)
        self.name = (kwargs['name'] if 'name' in kwargs else None)
        self.data = (kwargs['data'] if 'data' in kwargs else None)
        self.file = (kwargs['file'] if 'file' in kwargs else None)
        self.timestamp = (kwargs['timestamp'] if 'timestamp' in kwargs else None)

    @classmethod
    def publish(self, **kwargs):
        path = 'events'
        if not ('file' in kwargs) and Wia().Stream.connected and Wia().client_id is not None:
            topic = 'devices/' + Wia().client_id + '/' + path + '/' + kwargs['name']
            Wia().Stream.publish(topic=topic, **kwargs)
            return Event()
        else:
            response = post(path, kwargs)
            if WiaResource.is_success(response):
                return Event(**response.json())
            else:
                return WiaResource.error_response(response)

    @classmethod
    def subscribe(self, **kwargs):
        device=kwargs['device']
        topic='devices/' + device + '/events/'
        if 'name' in kwargs:
            topic += kwargs['name']
        else:
            topic += '+'
        Wia().Stream.subscribe(topic=topic, func=kwargs['func'])

    @classmethod
    def unsubscribe(self, **kwargs):
        device=kwargs['device']
        topic='devices/' + device + '/events/'
        if 'name' in kwargs:
            topic += kwargs['name']
        else:
            topic += '+'
        Wia().Stream.unsubscribe(topic=topic)

    @classmethod
    def list(self, **kwargs):
        response = get('events', **kwargs)
        if WiaResource.is_success(response):
            responseJson = response.json()
            events = []
            for event in responseJson['events']:
                events.append(Event(**event))
            return {'events':events,'count': responseJson['count']}
        else:
            return WiaResource.error_response(response)

class Sensor(WiaResource):
    def __init__(self, **kwargs):
        self.name = (kwargs['name'] if 'name' in kwargs else None)
        self.data = (kwargs['data'] if 'data' in kwargs else None)
        self.timestamp = (kwargs['timestamp'] if 'timestamp' in kwargs else None)

    @classmethod
    def publish(self, **kwargs):
        path = 'sensors'
        if Wia().Stream.connected and Wia().client_id is not None:
            topic = 'devices/' + Wia().client_id + '/' + path + '/' + kwargs['name']
            Wia().Stream.publish(topic=topic, **kwargs)
            return {}
        else:
            response = post(path, kwargs)
            if WiaResource.is_success(response):
                return Sensor(**response.json())
            else:
                return WiaResource.error_response(response)

    @classmethod
    def subscribe(self, **kwargs):
        device=kwargs['device']
        topic='devices/' + device + '/sensors/'
        if 'name' in kwargs:
            topic += kwargs['name']
        else:
            topic += '+'
        Wia().Stream.subscribe(topic=topic, func=kwargs['func'])

    @classmethod
    def unsubscribe(self, **kwargs):
        device=kwargs['device']
        topic='devices/' + device + '/sensors/'
        if 'name' in kwargs:
            topic += kwargs['name']
        else:
            topic += '+'
        Wia().Stream.unsubscribe(topic=topic)

    @classmethod
    def list(self, **kwargs):
        response = get('sensors', **kwargs)
        if WiaResource.is_success(response):
            responseJson = response.json()
            sensors = []
            for sensor in responseJson['sensors']:
                sensors.append(Sensor(**sensor))
            return {'sensors':sensors,'count': responseJson['count']}
        else:
            return WiaResource.error_response(response)

class Location(WiaResource):
    def __init__(self, **kwargs):
        self.id = (kwargs['id'] if 'id' in kwargs else None)
        self.latitude = (kwargs['latitude'] if 'latitude' in kwargs else None)
        self.longitude = (kwargs['longitude'] if 'longitude' in kwargs else None)
        self.altitude = (kwargs['altitude'] if 'altitude' in kwargs else None)
        self.timestamp = (kwargs['timestamp'] if 'timestamp' in kwargs else None)
        self.recievedTimestamp = (kwargs['recievedTimestamp'] if 'recievedTimestamp' in kwargs else None)

    @classmethod
    def publish(self, **kwargs):
        path = 'locations'
        if Wia().Stream.connected and Wia().client_id is not None:
            topic = 'devices/' + Wia().client_id + '/' + path
            Wia().Stream.publish(topic=topic, **kwargs)
            return {}
        else:
            response = post(path, kwargs)
            if WiaResource.is_success(response):
                return Location(**response.json())
            else:
                return WiaResource.error_response(response)

    @classmethod
    def subscribe(self, **kwargs):
        device = kwargs['device']
        topic = 'devices/' + device + '/locations'
        Wia().Stream.subscribe(topic=topic, func=kwargs['func'])

    @classmethod
    def unsubscribe(self, **kwargs):
        device = kwargs['device']
        topic = 'devices/' + device + '/locations'
        Wia().Stream.unsubscribe(topic=topic)

    @classmethod
    def list(self, **kwargs):
        response = get('locations', **kwargs)
        if WiaResource.is_success(response):
            responseJson = response.json()
            locations = []
            for location in responseJson['locations']:
                locations.append(Location(**location))
            return {'locations':locations,'count': responseJson['count']}
        else:
            return WiaResource.error_response(response)

class Log(WiaResource):
    def __init__(self, **kwargs):
        self.level = (kwargs['level'] if 'level' in kwargs else None)
        self.message = (kwargs['message'] if 'message' in kwargs else None)
        self.data = (kwargs['data'] if 'data' in kwargs else None)
        self.timestamp = (kwargs['timestamp'] if 'timestamp' in kwargs else None)

    @classmethod
    def publish(self, **kwargs):
        path = 'logs'
        if Wia().Stream.connected and Wia().client_id is not None:
            topic = 'devices/' + Wia().client_id + '/' + path + '/' + kwargs['level']
            Wia().Stream.publish(topic=topic, **kwargs)
            return {}
        else:
            response = post(path, kwargs)
            if WiaResource.is_success(response):
                return Log(**response.json())
            else:
                return WiaResource.error_response(response)

    @classmethod
    def subscribe(self, **kwargs):
        device = kwargs['device']
        topic = 'devices/' + device + '/logs'
        Wia().Stream.subscribe(topic=topic, func=kwargs['func'])

    @classmethod
    def unsubscribe(self, **kwargs):
        device = kwargs['device']
        topic = 'devices/' + device + '/logs'
        Wia().Stream.unsubscribe(topic=topic)

    @classmethod
    def list(self, **kwargs):
        response = get('logs', **kwargs)
        if WiaResource.is_success(response):
            responseJson = response.json()
            logs = []
            for log in responseJson['logs']:
                logs.append(Log(**log))
            return {'logs':logs,'count': responseJson['count']}
        else:
            return WiaResource.error_response(response)

class Function(WiaResource):
    def __init__(self, **kwargs):
        self.id = (kwargs['id'] if 'id' in kwargs else None)
        self.name = (kwargs['name'] if 'name' in kwargs else None)
        self.isEnabled = (kwargs['isEnabled'] if 'isEnabled' in kwargs else None)
        self.device = (kwargs['device'] if 'device' in kwargs else None)
        self.enabledAt = (kwargs['enabledAt'] if 'enabledAt' in kwargs else None)
        self.createdAt = (kwargs['createdAt'] if 'createdAt' in kwargs else None)
        self.updatedAt = (kwargs['updatedAt'] if 'updatedAt' in kwargs else None)

    @classmethod
    def create(self, **kwargs):
        path = 'functions'
        response = post(path, kwargs)

        if WiaResource.is_success(response):
            return Function(**response.json())
        else:
            return WiaResource.error_response(response)

    @classmethod
    def delete(self, id):
        path = 'functions/' + id
        repsonse = delete(path)

        if WiaResource.is_success(response):
            return WiaResourceDelete(**response.json())
        else:
            return WiaResource.error_response(response)

    @classmethod
    def call(self, **kwargs):
        if Wia().Stream.connected:
            topic = 'devices/' + kwargs['device'] + '/functions/' + kwargs['func'] + '/call'
            if 'data' not in kwargs:
                Wia().Stream.publish(topic=topic)
            elif type(kwargs['data']) is dict:
                Wia().Stream.publish(topic=topic, **kwargs['data'])
            else:
                data = kwargs['data']
                kwargs.pop('data')
                kwargs['data'] = {'arg': data}
                Wia().Stream.publish(topic=topic, **kwargs['data'])
        else:
            raise Exception("Unable to call function, not connected to stream")

    @classmethod
    def list(self, **kwargs):
        response = get('functions', **kwargs)
        if WiaResource.is_success(response):
            responseJson = response.json()
            functions = []
            for func in responseJson['functions']:
                functions.append(Function(**func))
            return {'functions':functions,'count': responseJson['count']}
        else:
            return WiaResource.error_response(response)

class Customer(WiaResource):
    def __init__(self, **kwargs):
        self.id = (kwargs['id'] if 'id' in kwargs else None)
        self.username = (kwargs['username'] if 'username' in kwargs else None)
        self.email = (kwargs['email'] if 'email' in kwargs else None)
        self.fullName = (kwargs['fullName'] if 'fullName' in kwargs else None)

    @classmethod
    def signup(self, **kwargs):
        response = post('customers/signup', kwargs)
        if WiaResource.is_success(response):
            return Customer(**response.json())
        else:
            return WiaResource.error_response(response)

    @classmethod
    def create(self, **kwargs):
        path = 'customers'
        response = post(path, kwargs)
        if is_success(response):
            return Customer(**response.json())
        else:
            return error_response(response)

    @classmethod
    def retrieve(self, id):
        path = 'customers/' + id
        response = get(path)
        if is_success(response):
            return Customer(**response.json())
        else:
            return error_response(response)

    def delete(self, id):
        path = 'customers/' + id
        repsonse = delete(path)

        if WiaResource.is_success(response):
            return WiaResourceDelete(**response.json())
        else:
            return WiaResource.error_response(response)

    @classmethod
    def list(self, **kwargs):
        response = get('customers', **kwargs)
        if WiaResource.is_success(response):
            responseJson = response.json()
            customers = []
            for customer in responseJson['customers']:
                customers.append(Customer(**customer))
            return {'customers':customers,'count': responseJson['count']}
        else:
            return WiaResource.error_response(response)

class AccessToken(WiaResource):
    def __init__(self, **kwargs):
        self.accessToken = (kwargs['accessToken'] if 'accessToken' in kwargs else None)
        self.refreshToken = (kwargs['refreshToken'] if 'refreshToken' in kwargs else None)
        self.tokenType = (kwargs['tokenType'] if 'tokenType' in kwargs else None)
        self.expiresIn = (kwargs['expiresIn'] if 'expiresIn' in kwargs else None)
        self.scope = (kwargs['scope'] if 'scope' in kwargs else None)

    @classmethod
    def create(self, **kwargs):
        response = post('auth/token', kwargs)
        if WiaResource.is_success(response):
            return AccessToken(**response.json())
        else:
            return WiaResource.error_response(response)

class WhoAmI(WiaResource):
    def __init__(self, **kwargs):
        self.contextData = (kwargs['contextData'] if 'contextData' in kwargs else None)
        self.scope = (kwargs['scope'] if 'scope' in kwargs else None)

    @classmethod
    def retrieve(self):
        response = get('whoami')
        if WiaResource.is_success(response):
            return WhoAmI(**response.json())
        else:
            return WiaResource.error_response(response)
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
__author__      = "Luca Cerina"
__copyright__   = "Copyright 2017, Luca Cerina"
__credits__     = ["Jeremy Karst"]
__email__       = "luca.cerina@polimi.it"

import numpy as np

# AMPD function
def ampd(sigInput, LSMlimit = 1):
	"""Find the peaks in the signal with the AMPD algorithm.

		Original implementation by Felix Scholkmann et al. in
		"An Efficient Algorithm for Automatic Peak Detection in
		Noisy Periodic and Quasi-Periodic Signals", Algorithms 2012,
		 5, 588-603

		Parameters
		----------
		sigInput: ndarray
			The 1D signal given as input to the algorithm
		lsmLimit: float
			Wavelet transform limit as a ratio of full signal length.
			Valid values: 0-1, the LSM array will no longer be calculated after this point
			  which results in the inability to find peaks at a scale larger than this factor.
			  For example a value of .5 will be unable to find peaks that are of period
			  1/2 * signal length, a default value of 1 will search all LSM sizes.

		Returns
		-------
		pks: ndarray
			The ordered array of peaks found in sigInput
	"""

	# Create preprocessing linear fit
	sigTime = np.arange(0, len(sigInput))

	# Detrend
	dtrSignal = (sigInput - np.polyval(np.polyfit(sigTime, sigInput, 1), sigTime)).astype(float)

	N = len(dtrSignal)
	L = int(np.ceil(N*LSMlimit / 2.0)) - 1

	# Generate random matrix
	LSM = np.ones([L,N], dtype='uint8')

	# Local minima extraction
	for k in range(1, L):
		LSM[k - 1, np.where((dtrSignal[k:N - k - 1] > dtrSignal[0: N - 2 * k - 1]) & (dtrSignal[k:N - k - 1] > dtrSignal[2 * k: N - 1]))[0]+k] = 0

	pks = np.where(np.sum(LSM[0:np.argmin(np.sum(LSM, 1)), :], 0)==0)[0]
	return pks

# Fast AMPD
def ampdFast(sigInput, order, LSMlimit = 1, verbose = False):
	"""A slightly faster version of AMPD which divides the signal in 'order' windows

		Parameters
		----------
		sigInput: ndarray
			The 1D signal given as input to the algorithm
		order: int
			The number of windows in which sigInput is divided
		lsmLimit: float
			Wavelet transform limit as a ratio of full signal length.
			Valid values: 0-1, the LSM array will no longer be calculated after this point
			  which results in the inability to find peaks at a scale larger than this factor.
			  For example a value of .5 will be unable to find peaks that are of period
			  1/2 * signal length, a default value of 1 will search all LSM sizes.
		verbose: bool
			Enable verbosity while parsing sectors
		Returns
		-------
		pks: ndarray
			The ordered array of peaks found in sigInput
	"""

	# Check if order is valid (perfectly separable)
	if(len(sigInput)%order != 0):
		print("AMPD: Invalid order, decreasing order")
		while(len(sigInput)%order != 0):
			order -= 1
		print("AMPD: Using order " + str(order))

	N = int(len(sigInput) / order / 2)

	# Loop function calls
	for i in range(0, len(sigInput)-N, N):
		if(verbose):
			print("\t sector: " + str(i) + "|" + str((i+2*N-1)))
		pksTemp = ampd(sigInput[i:(i+2*N-1)], LSMlimit)
		if(i == 0):
			pks = pksTemp
		else:
			pks = np.concatenate((pks, pksTemp+i))

	# Keep only unique values
	pks = np.unique(pks)

	return pks
<commit_msg>minor verbose fix<commit_after># -*- coding: utf-8 -*-
__author__      = "Luca Cerina"
__copyright__   = "Copyright 2017, Luca Cerina"
__credits__     = ["Jeremy Karst"]
__email__       = "luca.cerina@polimi.it"

import numpy as np

# AMPD function
def ampd(sigInput, LSMlimit = 1):
	"""Find the peaks in the signal with the AMPD algorithm.

		Original implementation by Felix Scholkmann et al. in
		"An Efficient Algorithm for Automatic Peak Detection in
		Noisy Periodic and Quasi-Periodic Signals", Algorithms 2012,
		 5, 588-603

		Parameters
		----------
		sigInput: ndarray
			The 1D signal given as input to the algorithm
		lsmLimit: float
			Wavelet transform limit as a ratio of full signal length.
			Valid values: 0-1, the LSM array will no longer be calculated after this point
			  which results in the inability to find peaks at a scale larger than this factor.
			  For example a value of .5 will be unable to find peaks that are of period
			  1/2 * signal length, a default value of 1 will search all LSM sizes.

		Returns
		-------
		pks: ndarray
			The ordered array of peaks found in sigInput
	"""

	# Create preprocessing linear fit
	sigTime = np.arange(0, len(sigInput))

	# Detrend
	dtrSignal = (sigInput - np.polyval(np.polyfit(sigTime, sigInput, 1), sigTime)).astype(float)

	N = len(dtrSignal)
	L = int(np.ceil(N*LSMlimit / 2.0)) - 1

	# Generate random matrix
	LSM = np.ones([L,N], dtype='uint8')

	# Local minima extraction
	for k in range(1, L):
		LSM[k - 1, np.where((dtrSignal[k:N - k - 1] > dtrSignal[0: N - 2 * k - 1]) & (dtrSignal[k:N - k - 1] > dtrSignal[2 * k: N - 1]))[0]+k] = 0

	pks = np.where(np.sum(LSM[0:np.argmin(np.sum(LSM, 1)), :], 0)==0)[0]
	return pks

# Fast AMPD
def ampdFast(sigInput, order, LSMlimit = 1, verbose = False):
	"""A slightly faster version of AMPD which divides the signal in 'order' windows

		Parameters
		----------
		sigInput: ndarray
			The 1D signal given as input to the algorithm
		order: int
			The number of windows in which sigInput is divided
		lsmLimit: float
			Wavelet transform limit as a ratio of full signal length.
			Valid values: 0-1, the LSM array will no longer be calculated after this point
			  which results in the inability to find peaks at a scale larger than this factor.
			  For example a value of .5 will be unable to find peaks that are of period
			  1/2 * signal length, a default value of 1 will search all LSM sizes.
		verbose: bool
			Enable verbosity while parsing sectors
		Returns
		-------
		pks: ndarray
			The ordered array of peaks found in sigInput
	"""

	# Check if order is valid (perfectly separable)
	if(len(sigInput)%order != 0):
		if verbose:
			print("AMPD: Invalid order, decreasing order")
		while(len(sigInput)%order != 0):
			order -= 1
		if verbose:
			print("AMPD: Using order " + str(order))

	N = int(len(sigInput) / order / 2)

	# Loop function calls
	for i in range(0, len(sigInput)-N, N):
		if(verbose):
			print("\t sector: " + str(i) + "|" + str((i+2*N-1)))
		pksTemp = ampd(sigInput[i:(i+2*N-1)], LSMlimit)
		if(i == 0):
			pks = pksTemp
		else:
			pks = np.concatenate((pks, pksTemp+i))

	# Keep only unique values
	pks = np.unique(pks)

	return pks
<|endoftext|>