<commit_before>#!/usr/bin/python
import math as m
import Tkinter as tk
import ttk
from Meter import *
import requests
from metar import Metar
import datetime

#evnt=0  #dbg global

class Appl(tk.Frame):
    def __init__(self, master=None):
        tk.Frame.__init__(self,master)
        self.grid(sticky=tk.N+tk.S+tk.E+tk.W)
        self.value=tk.StringVar()
        self.value.set(0)
        self.locations=["KBIL","KBOS","KBZN","KMSP","KGPZ"]
        self.location=tk.StringVar()
        self.createwidgets()
        self.location.set(self.locations[0])
        self.setloc()

    def createwidgets(self):
        # Make top window resizeable.
        top=self.winfo_toplevel()
        top.rowconfigure(0,weight=1)
        top.columnconfigure(0,weight=1)
        self.rowconfigure(1,weight=1)
        self.columnconfigure(0,weight=1)

        self.grip=ttk.Sizegrip(self)
        self.grip.grid(row=2,column=1,sticky=tk.SE)
        # Quit Button
        self.quitButton=tk.Button(self,text="Quit",
                                  command=self.quit,relief="raised")
        self.quitButton.grid(row=0,column=0,sticky=tk.W)
        # Reset Button
        self.resetButton=tk.Button(self,text="Reset",
                                  command=self.reset,relief="raised")
        self.resetButton.grid(row=0,column=0,sticky=tk.E)
        # To enter station id
        self.e=ttk.Combobox(self,values=self.locations,#state='readonly',
                            textvariable=self.location)
        self.e.grid(row=0,column=1,sticky=tk.E)
        # My custom widget
        self.m=Meter(self,width=300,height=300,from_=-40,to=120)
        self.m.grid(row=1,column=0,columnspan=2,sticky=tk.W+tk.E+tk.N+tk.S)
        # time data
        self.uptime=tk.StringVar()
        self._tleft=ttk.Label(self,anchor=tk.CENTER,textvariable=self.uptime)
        self._tleft.grid(row=1,column=0,sticky=tk.W+tk.S)
        self.downtime=tk.StringVar()
        self._tright=tk.Label(self,textvariable=self.downtime)
        self._tright.grid(row=1,column=1,sticky=tk.E+tk.S)
        # site label
        self._sitename=tk.StringVar()
        self.site=ttk.Label(self, textvariable=self._sitename,
                                 font=("Comic Sans", -24,''))
        self.site.grid(row=1,column=0,columnspan=2,sticky=tk.S)
        # bindings
        self.e.bind("<<ComboboxSelected>>",self.setloc)
        self.e.bind("<Return>",self.keyhandler)

    def reset(self):
        self.m.reset()

    def keyhandler(self,event):
        """ Only allow 4 letter strings through to be fetched"""
        sin=self.location.get()
        if len(sin) == 4:
            self.setloc()
        else:
            self.location.set("")

    def setloc(self,*args):
        self.location.set(self.location.get().upper())
        if hasattr(self, "callback"): self.after_cancel(self.callback)
        self.getval()

    def cmd(self,x):
        pass

    def getval(self):
        params={'station_ids': self.location.get(), 'chk_metars': 'true', 'std_trans':'standard'}
        try:
            r=requests.get("http://www.aviationweather.gov/adds/metars", params=params)
            w=r.text
            ws=w.index(self.location.get())
            w=w[ws:]
            we=w.index("<")
            w=w[:we]
            self.st=Metar.Metar(w)
            if self.st.temp.value():
                self._sitename.set(params['station_ids'])
                self.m.set(self.st.temp.value()*9./5.+32.0)
        except:
            # Just use old values if connection problems
            pass
        # update Labels in corners
        self.uptime.set(str(self.st.time))
        self.downtime.set(str(datetime.datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S")))
        # Set up next check
        self.callback=self.after(10*60*1000,func=self.getval)
#        self.callback=self.after(1*60*1000,func=self.getval)

app=Appl()
app.master.title('Meter Widget')
app.mainloop()
<commit_msg>Use XML instead of Metar data feed.<commit_after>#!/usr/bin/python
import math as m
import Tkinter as tk
import ttk
import xml.etree.ElementTree as ET
from Meter import *
import requests
import datetime

#evnt=0  #dbg global

class Appl(tk.Frame):
    def __init__(self, master=None):
        tk.Frame.__init__(self,master)
        self.grid(sticky=tk.N+tk.S+tk.E+tk.W)
        self.value=tk.StringVar()
        self.value.set(0)
        self.locations=["KBIL","KBOS","KBZN","KMSP","KGPZ"]
        self.location=tk.StringVar()
        self.createwidgets()
        self.location.set(self.locations[0])
        self.setloc()

    def createwidgets(self):
        # Make top window resizeable.
        top=self.winfo_toplevel()
        top.rowconfigure(0,weight=1)
        top.columnconfigure(0,weight=1)
        self.rowconfigure(1,weight=1)
        self.columnconfigure(0,weight=1)

        self.grip=ttk.Sizegrip(self)
        self.grip.grid(row=2,column=1,sticky=tk.SE)
        # Quit Button
        self.quitButton=tk.Button(self,text="Quit",
                                  command=self.quit,relief="raised")
        self.quitButton.grid(row=0,column=0,sticky=tk.W)
        # Reset Button
        self.resetButton=tk.Button(self,text="Reset",
                                  command=self.reset,relief="raised")
        self.resetButton.grid(row=0,column=0,sticky=tk.E)
        # To enter station id
        self.e=ttk.Combobox(self,values=self.locations,#state='readonly',
                            textvariable=self.location)
        self.e.grid(row=0,column=1,sticky=tk.E)
        # My custom widget
        self.m=Meter(self,width=300,height=300,from_=-40,to=120)
        self.m.grid(row=1,column=0,columnspan=2,sticky=tk.W+tk.E+tk.N+tk.S)
        # time data
        self.uptime=tk.StringVar()
        self._tleft=ttk.Label(self,anchor=tk.CENTER,textvariable=self.uptime)
        self._tleft.grid(row=1,column=0,sticky=tk.W+tk.S)
        self.downtime=tk.StringVar()
        self._tright=tk.Label(self,textvariable=self.downtime)
        self._tright.grid(row=1,column=1,sticky=tk.E+tk.S)
        # site label
        self._sitename=tk.StringVar()
        self.site=ttk.Label(self, textvariable=self._sitename,
                                 font=("Comic Sans", -24,''))
        self.site.grid(row=1,column=0,columnspan=2,sticky=tk.S)
        # bindings
        self.e.bind("<<ComboboxSelected>>",self.setloc)
        self.e.bind("<Return>",self.keyhandler)

    def reset(self):
        self.m.reset()

    def keyhandler(self,event):
        """ Only allow 4 letter strings through to be fetched"""
        sin=self.location.get()
        if len(sin) == 4:
            self.setloc()
        else:
            self.location.set("")

    def setloc(self,*args):
        self.location.set(self.location.get().upper())
        if hasattr(self, "callback"): self.after_cancel(self.callback)
        self.getval()

    def cmd(self,x):
        pass

    def getval(self):
        params={'sid': self.location.get(), 'num': '1'}
        dsource="http://www.wrh.noaa.gov/mesowest/getobextXml.php"
        try:
            r=requests.get(dsource, params=params)
            self.xmlroot=ET.fromstring(r.text)
            ob=self.xmlroot[0]
            vars={i.get('var'):i.get('value') for i in ob}
            t=ob.get('time')
            self.m.set(vars['T'])
            self._sitename.set(params['sid'])
        except:
            # Just use old values if connection problems
            pass
        # update Labels in corners
        self.uptime.set(t)
        self.downtime.set(str(datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")))
        # Set up next check
        self.callback=self.after(10*60*1000,func=self.getval)
#        self.callback=self.after(1*60*1000,func=self.getval)

app=Appl()
app.master.title('Meter Widget')
app.mainloop()
<|endoftext|>


<commit_before>"""
An implementation of a grader that uses codejail to sandbox submission execution.
"""
import sys
import imp
import json
import random
import gettext
from path import path
import six

import codejail

from grader_support.gradelib import EndTest
from grader_support.graderutil import LANGUAGE
import grader_support

from .grader import Grader

TIMEOUT = 1

def path_to_six():
    """
    Return the full path to six.py
    """
    if any(six.__file__.endswith(suffix) for suffix in ('.pyc', '.pyo')):
        # __file__ points to the compiled bytecode in python 2
        return path(six.__file__[:-1])
    else:
        # __file__ points to the .py file in python 3
        return path(six.__file__)

SUPPORT_FILES = [
    path(grader_support.__file__).dirname(),
    path_to_six(),
]

def truncate(out):
    """
    Truncate test output that's too long.  This is per-test.
    """
    TOO_LONG = 5000    # 5K bytes seems like enough for a single test.
    if len(out) > TOO_LONG:
        out = out[:TOO_LONG] + "...OUTPUT TRUNCATED"

    return out

def prepend_coding(code):
    """
    Add a coding line--makes submissions with inline unicode not
    explode (as long as they're utf8, I guess)
    """
    return '# coding: utf8\n' + code

class JailedGrader(Grader):
    """
    A grader implementation that uses codejail.
    Instantiate it with grader_root="path/to/graders"
    and optionally codejail_python="python name" (the name that you used to configure codejail)
    """
    def __init__(self, *args, **kwargs):
        self.codejail_python = kwargs.pop("codejail_python", "python")
        super(JailedGrader, self).__init__(*args, **kwargs)
        self.locale_dir = self.grader_root / "conf" / "locale"
        self.fork_per_item = False  # it's probably safe not to fork

    def _enable_i18n(self, language):
        trans = gettext.translation('graders', localedir=self.locale_dir, fallback=True, languages=[language])
        trans.install(unicode=True, names=None)

    def _run(self, grader_path, thecode, seed):
        files = SUPPORT_FILES + [grader_path]
        if self.locale_dir.exists():
            files.append(self.locale_dir)
        extra_files = [('submission.py', thecode.encode('utf-8'))]
        argv = ["-m", "grader_support.run", path(grader_path).basename(), 'submission.py', seed]
        jail = codejail.get_codejail(self.codejail_python)
        r = jail.jail_code(files=files, extra_files=extra_files, argv=argv)
        return r

    def grade(self, grader_path, grader_config, submission):
        if type(submission) != unicode:
            self.log.warning("Submission is NOT unicode")

        results = {
            'errors': [],
            'tests': [],
            'correct': False,
            'score': 0,
        }

        self._enable_i18n(grader_config.get("lang", LANGUAGE))

        answer_path = path(grader_path).dirname() / 'answer.py'
        with open(answer_path) as f:
            answer = f.read().decode('utf-8')

        # Import the grader, straight from the original file.  (It probably isn't in
        # sys.path, and we may be in a long running gunicorn process, so we don't
        # want to add stuff to sys.path either.)
        grader_module = imp.load_source("grader_module", grader_path)
        grader = grader_module.grader

        # Preprocess for grader-specified errors
        errors = grader.input_errors(submission)
        if errors != []:
            results['errors'].extend(errors)
            # Don't run tests if there were errors
            return results

        # Add a unicode encoding declaration.
        processed_answer = prepend_coding(grader.preprocess(answer))
        processed_submission = prepend_coding(grader.preprocess(submission))

        # Same seed for both runs
        seed = str(random.randint(0, 20000))

        # Run the official answer, to get the expected output.
        expected_ok = False
        expected_exc = None
        try:
            # If we want a factor of two speedup for now: trust the staff solution to
            # avoid hitting the sandbox. (change run to run_trusted)
            expected_outputs = None  # in case run_trusted raises an exception.
            expected_outputs = self._run(grader_path, processed_answer, seed).stdout
            if expected_outputs:
                expected = json.loads(expected_outputs)
                expected_ok = True
        except Exception:
            expected_exc = sys.exc_info()
        else:
            # We just ran the official answer, nothing should have gone wrong, so check
            # everything, and note it as bad if anything is wrong.
            if expected_ok:
                if expected['exceptions'] \
                        or expected['grader']['status'] != 'ok' \
                        or expected['submission']['status'] != 'ok':
                    expected_ok = False

        if not expected_ok:
            # We couldn't run the official answer properly, bail out, but don't show
            # details to the student, since none of it is their code.
            results['errors'].append(_('There was a problem running the staff solution (Staff debug: L364)'))
            self.log.error("Couldn't run staff solution. grader = %s, output: %r",
                           grader_path, expected_outputs, exc_info=expected_exc)
            return results

        # The expected code ran fine, go ahead and run the student submission.
        actual_ok = False
        actual_exc = None
        try:
            # Do NOT trust the student solution (in production).
            actual_outputs = None   # in case run raises an exception.
            actual_outputs = self._run(grader_path, processed_submission, seed).stdout
            if actual_outputs:
                actual = json.loads(actual_outputs)
                actual_ok = True
            else:
                results['errors'].append(_("There was a problem running your solution (Staff debug: L379)."))
        except Exception:
            actual_exc = sys.exc_info()
        else:
            if actual_ok and actual['grader']['status'] == 'ok':
                if actual['submission']['status'] != 'ok':
                    # The grader ran OK, but the student code didn't, so show the student
                    # details of what went wrong.  There is probably an exception to show.
                    shown_error = actual['submission']['exception'] or _('There was an error thrown while running your solution.')
                    results['errors'].append(shown_error)
            else:
                # The grader didn't run well, we are going to bail.
                actual_ok = False

        # If something went wrong, then don't continue
        if not actual_ok:
            results['errors'].append(_("We couldn't run your solution (Staff debug: L397)."))
            self.log.error("Couldn't run student solution. grader = %s, output: %r",
                           grader_path, actual_outputs, exc_info=actual_exc)
            return results

        # Compare actual and expected through the grader tests, but only if we haven't
        # already found a problem.
        corrects = []
        if not results['errors']:
            expected_results = expected['results']
            actual_results = actual['results']
            if len(expected_results) != len(actual_results):
                results['errors'].append(_('Something went wrong: different numbers of '
                                         'tests ran for your code and for our reference code.'))
                return results

            for test, exp, act in zip(grader.tests(), expected_results, actual_results):
                exp_short_desc, exp_long_desc, exp_output = exp
                act_short_desc, act_long_desc, act_output = act
                if exp_short_desc != act_short_desc:
                    results['errors'].append(_("Something went wrong: tests don't match up."))
                    # TODO: don't give up so easily?
                    return results
                # Truncate here--we don't want to send long output back, and also don't want to
                # confuse students by comparing the full output but sending back truncated output.
                act_output = truncate(act_output)
                try:
                    correct = test.compare_results(exp_output, act_output)
                except EndTest as e:
                    # Allows a grader's compare_results function to raise an EndTest exception
                    # (defined in gradelib.py). This enables the checker to print out an error
                    # message to the student, which will be appended to the end of stdout.
                    if e is not None:
                        act_output += '\n'
                        error_msg = _("ERROR")
                        act_output += u"*** {error_msg}: {error_detail} ***".format(
                            error_msg=error_msg,
                            error_detail=e
                        )
                    correct = False
                corrects.append(correct)
                if not grader_config.get("hide_output", False):
                    results['tests'].append((exp_short_desc, exp_long_desc,
                                            correct, exp_output, act_output))

        # If there were no tests run, then there was probably an error, so it's incorrect
        n = len(corrects)
        results['correct'] = all(corrects) and n > 0
        results['score'] = float(sum(corrects))/n if n > 0 else 0

        if n == 0 and len(results['errors']) == 0:
            results['errors'] = [
                _("There was a problem while running your code (Staff debug: L450). "
                  "Please contact the course staff for assistance.")
            ]

        return results

def main(args):     # pragma: no cover
    """
    Prints a json list:
    [ ("Test description", "value") ]

    TODO: what about multi-file submission?
    """
    import logging
    from pprint import pprint
    from codejail.jail_code import configure
    import getpass

    logging.basicConfig(level=logging.DEBUG)
    if len(args) != 2:
        return

    configure("python", sys.executable, user=getpass.getuser())
    (grader_path, submission_path) = args

    with open(submission_path) as f:
        submission = f.read().decode('utf-8')

    grader_config = {"lang": "eo"}
    grader_path = path(grader_path).abspath()
    g = JailedGrader(grader_root=grader_path.dirname().parent.parent)
    pprint(g.grade(grader_path, grader_config, submission))

if __name__ == '__main__':      # pragma: no cover
    main(sys.argv[1:])
<commit_msg>Added a short-circuit option for xqueue submissions<commit_after>"""
An implementation of a grader that uses codejail to sandbox submission execution.
"""
import sys
import imp
import json
import random
import gettext
from path import path
import six

import codejail

from grader_support.gradelib import EndTest
from grader_support.graderutil import LANGUAGE
import grader_support

from .grader import Grader

TIMEOUT = 1

def path_to_six():
    """
    Return the full path to six.py
    """
    if any(six.__file__.endswith(suffix) for suffix in ('.pyc', '.pyo')):
        # __file__ points to the compiled bytecode in python 2
        return path(six.__file__[:-1])
    else:
        # __file__ points to the .py file in python 3
        return path(six.__file__)

SUPPORT_FILES = [
    path(grader_support.__file__).dirname(),
    path_to_six(),
]

def truncate(out):
    """
    Truncate test output that's too long.  This is per-test.
    """
    TOO_LONG = 5000    # 5K bytes seems like enough for a single test.
    if len(out) > TOO_LONG:
        out = out[:TOO_LONG] + "...OUTPUT TRUNCATED"

    return out

def prepend_coding(code):
    """
    Add a coding line--makes submissions with inline unicode not
    explode (as long as they're utf8, I guess)
    """
    return '# coding: utf8\n' + code

class JailedGrader(Grader):
    """
    A grader implementation that uses codejail.
    Instantiate it with grader_root="path/to/graders"
    and optionally codejail_python="python name" (the name that you used to configure codejail)
    """
    def __init__(self, *args, **kwargs):
        self.codejail_python = kwargs.pop("codejail_python", "python")
        super(JailedGrader, self).__init__(*args, **kwargs)
        self.locale_dir = self.grader_root / "conf" / "locale"
        self.fork_per_item = False  # it's probably safe not to fork

    def _enable_i18n(self, language):
        trans = gettext.translation('graders', localedir=self.locale_dir, fallback=True, languages=[language])
        trans.install(unicode=True, names=None)

    def _run(self, grader_path, thecode, seed):
        files = SUPPORT_FILES + [grader_path]
        if self.locale_dir.exists():
            files.append(self.locale_dir)
        extra_files = [('submission.py', thecode.encode('utf-8'))]
        argv = ["-m", "grader_support.run", path(grader_path).basename(), 'submission.py', seed]
        jail = codejail.get_codejail(self.codejail_python)
        r = jail.jail_code(files=files, extra_files=extra_files, argv=argv)
        return r

    def grade(self, grader_path, grader_config, submission):
        if type(submission) != unicode:
            self.log.warning("Submission is NOT unicode")

        results = {
            'errors': [],
            'tests': [],
            'correct': False,
            'score': 0,
        }

        if grader_config.get('skip_grader', False):
            results['correct'] = True
            results['score'] = 1
            self.log.debug('Skipping the grader.')
            return results

        self._enable_i18n(grader_config.get("lang", LANGUAGE))

        answer_path = path(grader_path).dirname() / 'answer.py'
        with open(answer_path) as f:
            answer = f.read().decode('utf-8')

        # Import the grader, straight from the original file.  (It probably isn't in
        # sys.path, and we may be in a long running gunicorn process, so we don't
        # want to add stuff to sys.path either.)
        grader_module = imp.load_source("grader_module", grader_path)
        grader = grader_module.grader

        # Preprocess for grader-specified errors
        errors = grader.input_errors(submission)
        if errors != []:
            results['errors'].extend(errors)
            # Don't run tests if there were errors
            return results

        # Add a unicode encoding declaration.
        processed_answer = prepend_coding(grader.preprocess(answer))
        processed_submission = prepend_coding(grader.preprocess(submission))

        # Same seed for both runs
        seed = str(random.randint(0, 20000))

        # Run the official answer, to get the expected output.
        expected_ok = False
        expected_exc = None
        try:
            # If we want a factor of two speedup for now: trust the staff solution to
            # avoid hitting the sandbox. (change run to run_trusted)
            expected_outputs = None  # in case run_trusted raises an exception.
            expected_outputs = self._run(grader_path, processed_answer, seed).stdout
            if expected_outputs:
                expected = json.loads(expected_outputs)
                expected_ok = True
        except Exception:
            expected_exc = sys.exc_info()
        else:
            # We just ran the official answer, nothing should have gone wrong, so check
            # everything, and note it as bad if anything is wrong.
            if expected_ok:
                if expected['exceptions'] \
                        or expected['grader']['status'] != 'ok' \
                        or expected['submission']['status'] != 'ok':
                    expected_ok = False

        if not expected_ok:
            # We couldn't run the official answer properly, bail out, but don't show
            # details to the student, since none of it is their code.
            results['errors'].append(_('There was a problem running the staff solution (Staff debug: L364)'))
            self.log.error("Couldn't run staff solution. grader = %s, output: %r",
                           grader_path, expected_outputs, exc_info=expected_exc)
            return results

        # The expected code ran fine, go ahead and run the student submission.
        actual_ok = False
        actual_exc = None
        try:
            # Do NOT trust the student solution (in production).
            actual_outputs = None   # in case run raises an exception.
            actual_outputs = self._run(grader_path, processed_submission, seed).stdout
            if actual_outputs:
                actual = json.loads(actual_outputs)
                actual_ok = True
            else:
                results['errors'].append(_("There was a problem running your solution (Staff debug: L379)."))
        except Exception:
            actual_exc = sys.exc_info()
        else:
            if actual_ok and actual['grader']['status'] == 'ok':
                if actual['submission']['status'] != 'ok':
                    # The grader ran OK, but the student code didn't, so show the student
                    # details of what went wrong.  There is probably an exception to show.
                    shown_error = actual['submission']['exception'] or _('There was an error thrown while running your solution.')
                    results['errors'].append(shown_error)
            else:
                # The grader didn't run well, we are going to bail.
                actual_ok = False

        # If something went wrong, then don't continue
        if not actual_ok:
            results['errors'].append(_("We couldn't run your solution (Staff debug: L397)."))
            self.log.error("Couldn't run student solution. grader = %s, output: %r",
                           grader_path, actual_outputs, exc_info=actual_exc)
            return results

        # Compare actual and expected through the grader tests, but only if we haven't
        # already found a problem.
        corrects = []
        if not results['errors']:
            expected_results = expected['results']
            actual_results = actual['results']
            if len(expected_results) != len(actual_results):
                results['errors'].append(_('Something went wrong: different numbers of '
                                         'tests ran for your code and for our reference code.'))
                return results

            for test, exp, act in zip(grader.tests(), expected_results, actual_results):
                exp_short_desc, exp_long_desc, exp_output = exp
                act_short_desc, act_long_desc, act_output = act
                if exp_short_desc != act_short_desc:
                    results['errors'].append(_("Something went wrong: tests don't match up."))
                    # TODO: don't give up so easily?
                    return results
                # Truncate here--we don't want to send long output back, and also don't want to
                # confuse students by comparing the full output but sending back truncated output.
                act_output = truncate(act_output)
                try:
                    correct = test.compare_results(exp_output, act_output)
                except EndTest as e:
                    # Allows a grader's compare_results function to raise an EndTest exception
                    # (defined in gradelib.py). This enables the checker to print out an error
                    # message to the student, which will be appended to the end of stdout.
                    if e is not None:
                        act_output += '\n'
                        error_msg = _("ERROR")
                        act_output += u"*** {error_msg}: {error_detail} ***".format(
                            error_msg=error_msg,
                            error_detail=e
                        )
                    correct = False
                corrects.append(correct)
                if not grader_config.get("hide_output", False):
                    results['tests'].append((exp_short_desc, exp_long_desc,
                                            correct, exp_output, act_output))

        # If there were no tests run, then there was probably an error, so it's incorrect
        n = len(corrects)
        results['correct'] = all(corrects) and n > 0
        results['score'] = float(sum(corrects))/n if n > 0 else 0

        if n == 0 and len(results['errors']) == 0:
            results['errors'] = [
                _("There was a problem while running your code (Staff debug: L450). "
                  "Please contact the course staff for assistance.")
            ]

        return results

def main(args):     # pragma: no cover
    """
    Prints a json list:
    [ ("Test description", "value") ]

    TODO: what about multi-file submission?
    """
    import logging
    from pprint import pprint
    from codejail.jail_code import configure
    import getpass

    logging.basicConfig(level=logging.DEBUG)
    if len(args) != 2:
        return

    configure("python", sys.executable, user=getpass.getuser())
    (grader_path, submission_path) = args

    with open(submission_path) as f:
        submission = f.read().decode('utf-8')

    grader_config = {"lang": "eo"}
    grader_path = path(grader_path).abspath()
    g = JailedGrader(grader_root=grader_path.dirname().parent.parent)
    pprint(g.grade(grader_path, grader_config, submission))

if __name__ == '__main__':      # pragma: no cover
    main(sys.argv[1:])
<|endoftext|>


<commit_before>__version_info__ = (1, 2, 1)
__version__ = '.'.join(map(str, __version_info__))
<commit_msg>Cut 1.2.2<commit_after>__version_info__ = (1, 2, 2)
__version__ = '.'.join(map(str, __version_info__))
<|endoftext|>


<commit_before>"""mist.io views"""
import os
import logging
import json
import tempfile

from pyramid.response import Response
from libcloud.compute.base import Node, NodeSize, NodeImage, NodeLocation
from libcloud.compute.providers import get_driver
#from libcloud.compute.base import NodeAuthSSHKey
#from libcloud.compute.deployment import MultiStepDeployment, ScriptDeployment
from libcloud.compute.deployment import SSHKeyDeployment
from libcloud.compute.types import Provider
from mist.io.config import BACKENDS, BASE_EC2_AMIS
from pyramid.view import view_config
from fabric.api import run, env
from mist.io.machinecaps import get_machine_actions

log = logging.getLogger('mist.io')

def connect(request):
    """Establish backend connection using the credentials specified"""
    try:
        backend_list = request.environ['beaker.session']['backends']
    except:
        backend_list = BACKENDS

    backend_index = int(request.matchdict['backend'])
    backend = backend_list[backend_index]

    driver = get_driver(int(backend['provider']))

    # TODO: better checks for this
    if 'auth_url' in backend.keys():
        # openstack
        conn = driver(backend['id'],
                      backend['secret'],
                      ex_force_auth_url=backend.get('auth_url', None),
                      ex_force_auth_version=backend.get('auth_version', '1.0'))
    elif backend['title'] in 'Linode':
        # linode
        conn = driver(backend['secret'])
    else:
        # ec2, rackspace
        conn = driver(backend['id'], backend['secret'])
    return conn

@view_config(route_name='home',
             request_method='GET',
             renderer='templates/home.pt')
def home(request):
    """Fill in an object with backend data, taken from config.py"""
    try:
        backend_list = request.environ['beaker.session']['backends']
        session = True
    except:
        backend_list = BACKENDS
        session = False

    backends = []
    for backend in backend_list:
        backends.append({'id'           : backend['id'],
                         'title'        : backend['title'],
                         'provider'     : backend['provider'],
                         'poll_interval': backend['poll_interval'],
                         'status'       : 'off',
                         })

    return {'project': 'mist.io',
            'backends': backends,
            'session': session}

@view_config(route_name='machines', request_method='GET', renderer='json')
def list_machines(request):
    """List machines for a backend"""

    try:
        conn = connect(request)
    except:
        return Response('Backend not found', 404)

    try:
        machines = conn.list_nodes()
    except:
        return Response('Backend unavailable', 503)

    ret = []
    for m in machines:
        # for rackspace get the tags stored in extra.metadata.tags attr, for amazon get extra.tags.tags attr
        tags = m.extra.get('tags', None) or m.extra.get('metadata', None)
        tags = tags and tags.get('tags', None) or []
        machine = {'id'           : m.id,
                  'uuid'          : m.get_uuid(),
                  'name'          : m.name,
                  # both rackspace and amazon have the image in the imageId extra attr,
                  'imageId'       : m.image or m.extra.get('imageId', None),
                  # for rackspace get flavorId extra attr, for amazon the instancetype extra attr
                  'size'          : m.size or m.extra.get('flavorId', None) or m.extra.get('instancetype', None),
                  'state'         : m.state,
                  'private_ips'   : m.private_ips,
                  'public_ips'    : m.public_ips,
                  'tags'          : tags,
                  'extra'         : m.extra,
                  }
        machine.update(get_machine_actions(m, conn))
        ret.append(machine)
    return ret

@view_config(route_name='machines', request_method='POST', renderer='json')
def create_machine(request):
    """Create a new virtual machine on the specified backend"""
    try:
        conn = connect(request)
    except:
        return Response('Backend not found', 404)

    try:
        machine_name = request.json_body['name']
        location_id = request.json_body['location']
        image_id = request.json_body['image']
        size_id = request.json_body['size']
    except Exception as e:
        return Response('Invalid payload', 400)

    size = NodeSize(size_id, name='', ram='', disk='', bandwidth='', price='',
                    driver=conn)
    image = NodeImage(image_id, name='', driver=conn)
    location = NodeLocation(location_id, name='', country='', driver=conn)

    if conn.type == Provider.RACKSPACE and len(request.registry.settings['keypairs']):
        # try to deploy node with ssh key installed
        ssh_key = SSHKeyDeployment(request.registry.settings['keypairs'][0][0])
        try:
            conn.deploy_node(name=machine_name,
                             image=image,
                             size=size,
                             location=location,
                             deploy=ssh_key)
            return []
        except:
            log.warn('Failed to deploy node with ssh key. Trying to create simple node')

    try:
        conn.create_node(name=machine_name,
                         image=image,
                         size=size,
                         location=location)
        return []
    except Exception as e:
        return Response('Something went wrong with the creation', 500)

@view_config(route_name='machine',
             request_method='POST',
             request_param='action=start',
             renderer='json')
def start_machine(request):
    """Starts a machine on backends that supported start/stop"""
    #TODO: in which providers could this work?
    try:
        conn = connect(request)
    except:
        return Response('Backend not found', 404)

    machine_id = request.matchdict['machine']
    machine = Node(machine_id,
                   name=machine_id,
                   state=0,
                   public_ips=[],
                   private_ips=[],
                   driver=conn)
    #machine.start()

    return []

@view_config(route_name='machine',
             request_method='POST',
             request_param='action=stop',
             renderer='json')
def stop_machine(request):
    """Stops a machine on backends that supported start/stop"""
    #TODO: in which providers could this work?
    try:
        conn = connect(request)
    except:
        return Response('Backend not found', 404)

    machine_id = request.matchdict['machine']
    machine = Node(machine_id,
                   name=machine_id,
                   state=0,
                   public_ips=[],
                   private_ips=[],
                   driver=conn)
    #if conn.has('ex_stop'):
    conn.ex_stop(machine)

    return []

@view_config(route_name='machine',
             request_method='POST',
             request_param='action=reboot',
             renderer='json')
def reboot_machine(request):
    """Reboots a machine on a certain backend"""
    try:
        conn = connect(request)
    except:
        return Response('Backend not found', 404)

    machine_id = request.matchdict['machine']
    machine = Node(machine_id,
                   name=machine_id,
                   state=0,
                   public_ips=[],
                   private_ips=[],
                   driver=conn)
    machine.reboot()
    return []

@view_config(route_name='machine',
             request_method='POST',
             request_param='action=destroy',
             renderer='json')
def destroy_machine(request):
    """Destroys a machine on a certain backend"""
    try:
        conn = connect(request)
    except:
        return Response('Backend not found', 404)

    machine_id = request.matchdict['machine']
    machine = Node(machine_id,
                   name=machine_id,
                   state=0,
                   public_ips=[],
                   private_ips=[],
                   driver=conn)
    machine.destroy()

    return []

@view_config(route_name='metadata', request_method='POST')
def set_metadata(request):
    """Sets metadata for a machine, given the backend and machine id"""
    #TODO: the following are not working
    """Examples
    Openstack:
        conn.ex_set_metadata(machine,
                            {'name': 'ServerX',
                             'description': 'all the money'})
    EC2:
        conn2.ex_create_tags(machine, {'something': 'something_something'})
    """
    ret = []
    done = False
    backends = [b for b in BACKENDS if b['id'] == request.matchdict['backend']]
    if backends:
        backend = backends[0]
        conn = connect(backend)
        machines = conn.list_nodes()
        for machine in machines:
            if machine.id == request.matchdict['machine']:
                try:
                    metadata = request.json_body
                    #get metadata from request
                except:
                    return Response('Not proper format for metadata', 404)
                try:
                    #e.g. Openstack
                    metadata = conn.ex_set_metadata(machine, metadata)
                    done = True
                except:
                    try:
                        #e.g. EC2
                        metadata = conn.ex_create_tags(machine, metadata)
                        done = True
                    except:
                        return Response('Not implemented for this backend', 404)
                break
    if not done:
        return Response('Invalid backend', 404)

    return Response(json.dumps(ret))

@view_config(route_name='images', request_method='GET', renderer='json')
def list_images(request):
    """List images from each backend"""
    try:
        conn = connect(request)
    except:
        return Response('Backend not found', 404)

    try:
        try:
            backend_list = request.environ['beaker.session']['backends']
        except:
            backend_list = BACKENDS
        backend_index = int(request.matchdict['backend'])
        backend = backend_list[backend_index]
        if backend['provider'] == Provider.EC2:
            images = conn.list_images(None, BASE_EC2_AMIS.keys())
        else:
            images = conn.list_images()
    except:
        return Response('Backend unavailable', 503)

    ret = []
    for image in images:
        ret.append({'id'    : image.id,
                    'extra' : image.extra,
                    'name'  : image.name,
                    })
    return ret

@view_config(route_name='sizes', request_method='GET', renderer='json')
def list_sizes(request):
    """List sizes (aka flavors) from each backend"""
    try:
        conn = connect(request)
    except:
        return Response('Backend not found', 404)

    try:
        sizes = conn.list_sizes()
    except:
        return Response('Backend unavailable', 503)

    ret = []
    for size in sizes:
        ret.append({'id'        : size.id,
                    'bandwidth' : size.bandwidth,
                    'disk'      : size.disk,
                    'driver'    : size.driver.name,
                    'name'      : size.name,
                    'price'     : size.price,
                    'ram'       : size.ram,
                    })

    return ret

@view_config(route_name='locations', request_method='GET', renderer='json')
def list_locations(request):
    """List locations from each backend"""
    try:
        conn = connect(request)
    except:
        return Response('Backend not found', 404)

    try:
        locations = conn.list_locations()
    except:
        return Response('Backend unavailable', 503)

    ret = []
    for location in locations:
        ret.append({'id'        : location.id,
                    'name'      : location.name,
                    'country'   : location.country,
                    })

    return ret

@view_config(route_name='image_details', request_method='GET', renderer='json')
def get_image_details(request):
    """get_image_details"""
    try:
        conn = connect(request)
    except:
        return Response('Backend not found', 404)

    try:
        image = conn.get_image(request.params['id'])
    except:
        return Response('Backend unavailable', 503)

    if image is None:
        ret = {}
    else:
        ret = {'id'     : image.id,
               'extra' : image.extra,
               'name'  : image.name,
               }
    return ret

@view_config(route_name='backends', request_method='GET', renderer='json')
def get_backends(request):
    """get_backends"""
    try:
        backend_list = request.environ['beaker.session']['backends']
    except:
        backend_list = BACKENDS

    backends = []
    index = 0
    for backend in backend_list:
        backends.append({'index'        : index,
                         'id'           : backend['id'],
                         'title'        : backend['title'],
                         'provider'     : backend['provider'],
                         'poll_interval': backend['poll_interval'],
                         'status'       : 'online',
                         })
        index = index + 1

    return backends

def config_fabric_ssh(ip, private_key):
    """Configures the ssh connection used by fabric.

    The problem is that fabric does not support passing the private key as a
    string, but only as a file. To solve this we use a temporary file. After
    the connection is closed you should erase this file. That's why this
    function returns the path of the temporary file.
    """
    if not ip or not private_key:
        log.info('IP or private key missing. SSH configuration failed.')
        return False

    env.host_string = ip
    env.user = 'root'
    #env.connection_attempts - defaults to 1
    #env.timeout - e.g. 20 in secs defaults to 10

    (tmp_key, tmp_path) = tempfile.mkstemp()
    key_fd = os.fdopen(tmp_key, 'w+b')
    key_fd.write(private_key)
    key_fd.close()
    env.key_filename = [tmp_path]

    return tmp_path

@view_config(route_name='machine_key', request_method='GET', renderer='json')
def machine_key(request):
    """Check if the machine has a key pair deployed"""
    tmp_path = config_fabric_ssh(request.params.get('ip', None),
                                 request.registry.settings['keypairs'][0][1])

    if run('uptime').failed:
        ret = {'has_key': False}
    else:
        ret = {'has_key': True}

    os.remove(tmp_path)

    return ret

@view_config(route_name='machine_shell', request_method='POST', renderer='json')
def shell_command(request):
    """Send a shell command to a machine over ssh"""

    tmp_path = config_fabric_ssh(request.params.get('ip', None),
                                 request.registry.settings['keypairs'][0][1])

    try:
        cmd_output = run(request.params.get('command', None))
    except:
        cmd_output = ''; # FIXME grab the UNIX error

    os.remove(tmp_path)

    return cmd_output

@view_config(route_name='machine_uptime', request_method='GET', renderer='json')
def machine_uptime(request):
    """Check if the machine has a key pair deployed"""
    tmp_path = config_fabric_ssh(request.params.get('ip', None),
                                 request.registry.settings['keypairs'][0][1])

    uptime =  run('cat /proc/uptime')
    if uptime:
        uptime = float(uptime.split()[0]) * 1000

    return {'uptime': uptime }
<commit_msg>Clear imports<commit_after>"""mist.io views"""
import os
import logging
import json
import tempfile

from pyramid.response import Response
from pyramid.view import view_config

from libcloud.compute.base import Node
from libcloud.compute.base import NodeSize
from libcloud.compute.base import NodeImage
from libcloud.compute.base import NodeLocation
from libcloud.compute.providers import get_driver
from libcloud.compute.deployment import SSHKeyDeployment
from libcloud.compute.types import Provider

from fabric.api import run
from fabric.api import env

from mist.io.config import BACKENDS
from mist.io.config import BASE_EC2_AMIS
from mist.io.machinecaps import get_machine_actions

log = logging.getLogger('mist.io')

def connect(request):
    """Establish backend connection using the credentials specified"""
    try:
        backend_list = request.environ['beaker.session']['backends']
    except:
        backend_list = BACKENDS

    backend_index = int(request.matchdict['backend'])
    backend = backend_list[backend_index]

    driver = get_driver(int(backend['provider']))

    # TODO: better checks for this
    if 'auth_url' in backend.keys():
        # openstack
        conn = driver(backend['id'],
                      backend['secret'],
                      ex_force_auth_url=backend.get('auth_url', None),
                      ex_force_auth_version=backend.get('auth_version', '1.0'))
    elif backend['title'] in 'Linode':
        # linode
        conn = driver(backend['secret'])
    else:
        # ec2, rackspace
        conn = driver(backend['id'], backend['secret'])
    return conn

@view_config(route_name='home',
             request_method='GET',
             renderer='templates/home.pt')
def home(request):
    """Fill in an object with backend data, taken from config.py"""
    try:
        backend_list = request.environ['beaker.session']['backends']
        session = True
    except:
        backend_list = BACKENDS
        session = False

    backends = []
    for backend in backend_list:
        backends.append({'id'           : backend['id'],
                         'title'        : backend['title'],
                         'provider'     : backend['provider'],
                         'poll_interval': backend['poll_interval'],
                         'status'       : 'off',
                         })

    return {'project': 'mist.io',
            'backends': backends,
            'session': session}

@view_config(route_name='machines', request_method='GET', renderer='json')
def list_machines(request):
    """List machines for a backend"""

    try:
        conn = connect(request)
    except:
        return Response('Backend not found', 404)

    try:
        machines = conn.list_nodes()
    except:
        return Response('Backend unavailable', 503)

    ret = []
    for m in machines:
        # for rackspace get the tags stored in extra.metadata.tags attr, for amazon get extra.tags.tags attr
        tags = m.extra.get('tags', None) or m.extra.get('metadata', None)
        tags = tags and tags.get('tags', None) or []
        machine = {'id'           : m.id,
                  'uuid'          : m.get_uuid(),
                  'name'          : m.name,
                  # both rackspace and amazon have the image in the imageId extra attr,
                  'imageId'       : m.image or m.extra.get('imageId', None),
                  # for rackspace get flavorId extra attr, for amazon the instancetype extra attr
                  'size'          : m.size or m.extra.get('flavorId', None) or m.extra.get('instancetype', None),
                  'state'         : m.state,
                  'private_ips'   : m.private_ips,
                  'public_ips'    : m.public_ips,
                  'tags'          : tags,
                  'extra'         : m.extra,
                  }
        machine.update(get_machine_actions(m, conn))
        ret.append(machine)
    return ret

@view_config(route_name='machines', request_method='POST', renderer='json')
def create_machine(request):
    """Create a new virtual machine on the specified backend"""
    try:
        conn = connect(request)
    except:
        return Response('Backend not found', 404)

    try:
        machine_name = request.json_body['name']
        location_id = request.json_body['location']
        image_id = request.json_body['image']
        size_id = request.json_body['size']
    except Exception as e:
        return Response('Invalid payload', 400)

    size = NodeSize(size_id, name='', ram='', disk='', bandwidth='', price='',
                    driver=conn)
    image = NodeImage(image_id, name='', driver=conn)
    location = NodeLocation(location_id, name='', country='', driver=conn)

    if conn.type == Provider.RACKSPACE and len(request.registry.settings['keypairs']):
        # try to deploy node with ssh key installed
        ssh_key = SSHKeyDeployment(request.registry.settings['keypairs'][0][0])
        try:
            conn.deploy_node(name=machine_name,
                             image=image,
                             size=size,
                             location=location,
                             deploy=ssh_key)
            return []
        except:
            log.warn('Failed to deploy node with ssh key. Trying to create simple node')

    try:
        conn.create_node(name=machine_name,
                         image=image,
                         size=size,
                         location=location)
        return []
    except Exception as e:
        return Response('Something went wrong with the creation', 500)

@view_config(route_name='machine',
             request_method='POST',
             request_param='action=start',
             renderer='json')
def start_machine(request):
    """Starts a machine on backends that supported start/stop"""
    #TODO: in which providers could this work?
    try:
        conn = connect(request)
    except:
        return Response('Backend not found', 404)

    machine_id = request.matchdict['machine']
    machine = Node(machine_id,
                   name=machine_id,
                   state=0,
                   public_ips=[],
                   private_ips=[],
                   driver=conn)
    #machine.start()

    return []

@view_config(route_name='machine',
             request_method='POST',
             request_param='action=stop',
             renderer='json')
def stop_machine(request):
    """Stops a machine on backends that supported start/stop"""
    #TODO: in which providers could this work?
    try:
        conn = connect(request)
    except:
        return Response('Backend not found', 404)

    machine_id = request.matchdict['machine']
    machine = Node(machine_id,
                   name=machine_id,
                   state=0,
                   public_ips=[],
                   private_ips=[],
                   driver=conn)
    #if conn.has('ex_stop'):
    conn.ex_stop(machine)

    return []

@view_config(route_name='machine',
             request_method='POST',
             request_param='action=reboot',
             renderer='json')
def reboot_machine(request):
    """Reboots a machine on a certain backend"""
    try:
        conn = connect(request)
    except:
        return Response('Backend not found', 404)

    machine_id = request.matchdict['machine']
    machine = Node(machine_id,
                   name=machine_id,
                   state=0,
                   public_ips=[],
                   private_ips=[],
                   driver=conn)
    machine.reboot()
    return []

@view_config(route_name='machine',
             request_method='POST',
             request_param='action=destroy',
             renderer='json')
def destroy_machine(request):
    """Destroys a machine on a certain backend"""
    try:
        conn = connect(request)
    except:
        return Response('Backend not found', 404)

    machine_id = request.matchdict['machine']
    machine = Node(machine_id,
                   name=machine_id,
                   state=0,
                   public_ips=[],
                   private_ips=[],
                   driver=conn)
    machine.destroy()

    return []

@view_config(route_name='metadata', request_method='POST')
def set_metadata(request):
    """Sets metadata for a machine, given the backend and machine id"""
    #TODO: the following are not working
    """Examples
    Openstack:
        conn.ex_set_metadata(machine,
                            {'name': 'ServerX',
                             'description': 'all the money'})
    EC2:
        conn2.ex_create_tags(machine, {'something': 'something_something'})
    """
    ret = []
    done = False
    backends = [b for b in BACKENDS if b['id'] == request.matchdict['backend']]
    if backends:
        backend = backends[0]
        conn = connect(backend)
        machines = conn.list_nodes()
        for machine in machines:
            if machine.id == request.matchdict['machine']:
                try:
                    metadata = request.json_body
                    #get metadata from request
                except:
                    return Response('Not proper format for metadata', 404)
                try:
                    #e.g. Openstack
                    metadata = conn.ex_set_metadata(machine, metadata)
                    done = True
                except:
                    try:
                        #e.g. EC2
                        metadata = conn.ex_create_tags(machine, metadata)
                        done = True
                    except:
                        return Response('Not implemented for this backend', 404)
                break
    if not done:
        return Response('Invalid backend', 404)

    return Response(json.dumps(ret))

@view_config(route_name='images', request_method='GET', renderer='json')
def list_images(request):
    """List images from each backend"""
    try:
        conn = connect(request)
    except:
        return Response('Backend not found', 404)

    try:
        try:
            backend_list = request.environ['beaker.session']['backends']
        except:
            backend_list = BACKENDS
        backend_index = int(request.matchdict['backend'])
        backend = backend_list[backend_index]
        if backend['provider'] == Provider.EC2:
            images = conn.list_images(None, BASE_EC2_AMIS.keys())
        else:
            images = conn.list_images()
    except:
        return Response('Backend unavailable', 503)

    ret = []
    for image in images:
        ret.append({'id'    : image.id,
                    'extra' : image.extra,
                    'name'  : image.name,
                    })
    return ret

@view_config(route_name='sizes', request_method='GET', renderer='json')
def list_sizes(request):
    """List sizes (aka flavors) from each backend"""
    try:
        conn = connect(request)
    except:
        return Response('Backend not found', 404)

    try:
        sizes = conn.list_sizes()
    except:
        return Response('Backend unavailable', 503)

    ret = []
    for size in sizes:
        ret.append({'id'        : size.id,
                    'bandwidth' : size.bandwidth,
                    'disk'      : size.disk,
                    'driver'    : size.driver.name,
                    'name'      : size.name,
                    'price'     : size.price,
                    'ram'       : size.ram,
                    })

    return ret

@view_config(route_name='locations', request_method='GET', renderer='json')
def list_locations(request):
    """List locations from each backend"""
    try:
        conn = connect(request)
    except:
        return Response('Backend not found', 404)

    try:
        locations = conn.list_locations()
    except:
        return Response('Backend unavailable', 503)

    ret = []
    for location in locations:
        ret.append({'id'        : location.id,
                    'name'      : location.name,
                    'country'   : location.country,
                    })

    return ret

@view_config(route_name='image_details', request_method='GET', renderer='json')
def get_image_details(request):
    """get_image_details"""
    try:
        conn = connect(request)
    except:
        return Response('Backend not found', 404)

    try:
        image = conn.get_image(request.params['id'])
    except:
        return Response('Backend unavailable', 503)

    if image is None:
        ret = {}
    else:
        ret = {'id'     : image.id,
               'extra' : image.extra,
               'name'  : image.name,
               }
    return ret

@view_config(route_name='backends', request_method='GET', renderer='json')
def get_backends(request):
    """get_backends"""
    try:
        backend_list = request.environ['beaker.session']['backends']
    except:
        backend_list = BACKENDS

    backends = []
    index = 0
    for backend in backend_list:
        backends.append({'index'        : index,
                         'id'           : backend['id'],
                         'title'        : backend['title'],
                         'provider'     : backend['provider'],
                         'poll_interval': backend['poll_interval'],
                         'status'       : 'online',
                         })
        index = index + 1

    return backends

def config_fabric_ssh(ip, private_key):
    """Configures the ssh connection used by fabric.

    The problem is that fabric does not support passing the private key as a
    string, but only as a file. To solve this we use a temporary file. After
    the connection is closed you should erase this file. That's why this
    function returns the path of the temporary file.
    """
    if not ip or not private_key:
        log.info('IP or private key missing. SSH configuration failed.')
        return False

    env.host_string = ip
    env.user = 'root'
    #env.connection_attempts - defaults to 1
    #env.timeout - e.g. 20 in secs defaults to 10

    (tmp_key, tmp_path) = tempfile.mkstemp()
    key_fd = os.fdopen(tmp_key, 'w+b')
    key_fd.write(private_key)
    key_fd.close()
    env.key_filename = [tmp_path]

    return tmp_path

@view_config(route_name='machine_key', request_method='GET', renderer='json')
def machine_key(request):
    """Check if the machine has a key pair deployed"""
    tmp_path = config_fabric_ssh(request.params.get('ip', None),
                                 request.registry.settings['keypairs'][0][1])

    if run('uptime').failed:
        ret = {'has_key': False}
    else:
        ret = {'has_key': True}

    os.remove(tmp_path)

    return ret

@view_config(route_name='machine_shell', request_method='POST', renderer='json')
def shell_command(request):
    """Send a shell command to a machine over ssh"""

    tmp_path = config_fabric_ssh(request.params.get('ip', None),
                                 request.registry.settings['keypairs'][0][1])

    try:
        cmd_output = run(request.params.get('command', None))
    except:
        cmd_output = ''; # FIXME grab the UNIX error

    os.remove(tmp_path)

    return cmd_output

@view_config(route_name='machine_uptime', request_method='GET', renderer='json')
def machine_uptime(request):
    """Check if the machine has a key pair deployed"""
    tmp_path = config_fabric_ssh(request.params.get('ip', None),
                                 request.registry.settings['keypairs'][0][1])

    uptime =  run('cat /proc/uptime')
    if uptime:
        uptime = float(uptime.split()[0]) * 1000

    return {'uptime': uptime }
<|endoftext|>


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

try:
    import json
except ImportError:
    import simplejson as json

import subprocess
from random import sample
import os
from time import sleep
from filecmp import cmp

##this script checks the role of the node
#and installs cron and amen (for coordinator)
try:
    config_file = file('/etc/yanoama.conf').read()
    config = json.loads(config_file)
except:
    print "There was an error in your configuration file (/etc/yanoama.conf)"
    raise

#globals
_coordinators = config.get('coordinators', {})
_ple_deployment = config.get('ple_deployment', {})
DEPLOYMENT_PATH=_ple_deployment['path']
_amen = config.get('amen', {})
STORAGE_PATH = _ple_deployment['storage_path']
AMEN_HOME=_amen['home']
HOSTNAME = subprocess.Popen(['hostname'], \
                            stdout=subprocess.PIPE,\
                            close_fds=True).\
                            communicate()[0].rstrip()
_amen = config.get('amen', {})
_backend = _amen.get('backend', {})
_mongo = _backend.get('mongo', {})
MONGO_PORT=_mongo.get('port',39167)
MONGO_REPLICATION_PORT=_mongo.get('replication_port',40167)
_pilot = config.get('pilot', {})
PILOT_PORT=_pilot.get('port',44444)
##gets a comma-separated samples as a
#string for cron jobs
def get_cron_time_samples(start,stop,samples):
    """Get a comma-separated samples as a
    string for cron jobs.

    Keyword arguments:
    start -- start integer to sample
    stop -- stop integer to sample
    samples -- number of samples

    Returns:
    string -- comma-separated list of samples
    from the start-stop range

    """
    return str(sample(xrange(start,stop),samples)).\
        replace(' ','').\
        replace(']','').replace('[','')

#this installs a cron job to the local
#cron. the frequency might be once, twice
#a day.
#args:
#job: string with cron job
#frequency: how many daily calls,
#    default is ONCE_A_DAY
def install_cron_job(job,frequency='ONCE_A_DAY'):
    """Add a job to the local
    crontable. the frequency might be once, twice
    a day..

    Keyword arguments:
    job -- command or job from cron
    frequency -- to run this job (default ONCE_A_DAY)
    """

    minute=get_cron_time_samples(0, 60, 1)
    hour=""
    if(frequency=='TWICE_A_DAY'):
        #twice a day
        #once a day
        hour= get_cron_time_samples(0, 24, 2)
    else:
        #once a day
        hour=get_cron_time_samples(0, 8, 1)
    mystring = str(minute)+"       "+str(hour)+\
    "     *       *       *       "+job
    cron_temp_file='/tmp/cron.temp'
    #first backup current jobs
    current_jobs=subprocess.\
        Popen(['crontab', '-l'], \
          stdout=subprocess.PIPE, close_fds=True).communicate()[0]
    f = open(cron_temp_file, 'w')
    if len(current_jobs)>0:
        f.write(current_jobs)
    f.write(mystring+'\n')
    f.close()
    subprocess.Popen(['crontab',cron_temp_file], \
                    stdout=subprocess.PIPE, \
                    close_fds=True)

    #clean up
    subprocess.Popen(['rm', cron_temp_file], \
                         stdout=subprocess.PIPE, close_fds=True)

def install_runnable_script(script,\
                            deployment_path=DEPLOYMENT_PATH,\
                            dst_dir='/bin/'):
    """Install a script to a directory of the local
    path and make it runnable, where script is a relative
    path of deployment_path.

    Keyword arguments:
    script -- path to the script relative to
    deployment path
    deployment_path -- where yanoama is installed
    (default DEPLOYMENT_PATH global)
    dst_dir -- destination path to install this
    script (default '/bin')

    """
    subprocess.Popen(['sudo','cp',\
                      deployment_path+\
                      '/'+script,\
                      dst_dir],\
                     stdout=subprocess.PIPE, \
                     close_fds=True)
    #making it runnable
    subprocess.Popen(['sudo','chmod', 'guo+x',\
                      (dst_dir+'/'+script.split('/')[-1])],\
                     stdout=subprocess.PIPE,\
                     close_fds=True)

#deployment is based on the node role
#that's either coordinator or peer
if __name__ == '__main__':
    """Deploy yanoama based on the node role,
    either peer or coordinator.

    """
    #update services file
    services_file='/etc/services'
    services_origin_file='/etc/services.origin'
    temp_services_file='/tmp/services'
    #check if /etc/hosts.origin exists
    if not os.path.exists(services_origin_file):
        subprocess.Popen(['sudo','cp', services_file,services_origin_file], \
                         stdout=subprocess.PIPE, close_fds=True)
    while not os.path.exists(services_origin_file):
        print 'waiting for copying origin...'
        sleep(1/1000.0)
    subprocess.Popen(['sudo','cp', services_origin_file,services_file], \
                     stdout=subprocess.PIPE, close_fds=True)
    subprocess.Popen(['sudo','cp', services_origin_file,services_file], \
                     stdout=subprocess.PIPE, close_fds=True)
    while not cmp(services_origin_file, services_file):
        print 'waiting for copying services file...'
        sleep(1/1000.0)
    subprocess.Popen(['cp', services_origin_file,temp_services_file], \
                     stdout=subprocess.PIPE, close_fds=True)
    while not os.path.exists(temp_services_file):
        print 'waiting for copying services temp file...'
        sleep(1/1000.0)
    f = open(temp_services_file, 'a')
    f.write('#local services'+'\n')
    f.write('pilot\t\t'+str(PILOT_PORT)+'/tcp\n')
    f.write('mongo\t\t'+str(MONGO_PORT)+'/tcp\n')
    f.write('mongo_replication\t\t'+str(MONGO_REPLICATION_PORT)+'/tcp\n')
    f.close()
    print "waiting for copying temp file..."
    sleep(1)
    subprocess.Popen(['sudo','cp',temp_services_file,services_file], \
                     stdout=subprocess.PIPE, close_fds=True)
    while not cmp(services_file, temp_services_file):
        print "waiting for copying temp file to services..."
        sleep(1/1000.0)
    sleep(1)
    #clean up
    subprocess.Popen(['rm',temp_services_file], \
                     stdin=cp_command.stdout, stdout=subprocess.PIPE, close_fds=True)

    if HOSTNAME in _coordinators.keys():
        """this is a coordinator
        #here the two-step installation procedure
        #1) install cron jobs: get_rtt.py and
        #                    peering.py
        #2) install mongodb database as part of amen
        #                    monitoring system.

        """
        #
        #
        #(1)installing cron jobs
        ##    get_rtt
        install_runnable_script('/yanoama/monitoring/get_rtt.py')
        #install as a cron job
        install_cron_job("get_rtt.py > /tmp/get_rtt_output.log 2>&1")
        ##    peering.py
        install_runnable_script('/yanoama/system/peering.py')
        #install as a cron job for running twice a day
        install_cron_job("peering.py > /tmp/peering_output.log 2>&1",\
                         'TWICE_A_DAY')
        #(2) install amen in coordinator: mongodb
        #based on
        #http://docs.mongodb.org/manual/tutorial/install-mongodb-on-red-hat-centos-or-fedora-linux/
        #last visit 5 March 2013
        #listen on 39167 port
        subprocess.Popen(['sudo','cp',DEPLOYMENT_PATH+'/contrib/mongodb/mongod.conf','/etc/mongod.conf'], stdout=subprocess.PIPE, close_fds=True)
        subprocess.Popen(['sudo','/sbin/chkconfig','mongod','on'], stdout=subprocess.PIPE, close_fds=True)
        subprocess.Popen(['sudo','/sbin/service','mongod','start'], stdout=subprocess.PIPE, close_fds=True)

    else:
        """peer/storage node role
        #two-step installation procedure
        #1) install cron jobs: membership.py
        #
        #2) install and run amen agent (daemon)
        """
        #
        #
        #(1) install membership script
        install_runnable_script('/yanoama/system/membership.py')
        install_cron_job("membership.py > /tmp/membership_output.log 2>&1")

        #(2) instal/run amen monitoring agent
        #default dir for storing objects/videos
        if os.path.exists(STORAGE_PATH):
            subprocess.Popen(['rm','-rf',STORAGE_PATH], stdout=subprocess.PIPE, close_fds=True)
        subprocess.Popen(['mkdir',STORAGE_PATH], stdout=subprocess.PIPE, close_fds=True)
        #install amen agent/deamon in this peer
        if not os.path.exists(AMEN_HOME):
            subprocess.Popen(['sudo','mkdir',AMEN_HOME],stdout=subprocess.PIPE, close_fds=True)
            subprocess.Popen(['sudo','mkdir',AMEN_HOME+'/bin'],stdout=subprocess.PIPE, close_fds=True)
            subprocess.Popen(['sudo','touch',AMEN_HOME+'/amend.log'],stdout=subprocess.PIPE, close_fds=True)
        subprocess.Popen(['sudo','cp',DEPLOYMENT_PATH+'/contrib/amen/amend',AMEN_HOME+'/bin/'],stdout=subprocess.PIPE, close_fds=True)
        #run daemon
        subprocess.Popen(['sudo','chmod','+x',AMEN_HOME+'/bin/amend'],stdout=subprocess.PIPE, close_fds=True)
        subprocess.Popen(['sudo',AMEN_HOME+'/bin/amend','start'],stdout=subprocess.PIPE, close_fds=True)

        print "outside"<commit_msg>broken<commit_after>#!/usr/bin/env python

try:
    import json
except ImportError:
    import simplejson as json

import subprocess
from random import sample
import os
from time import sleep
from filecmp import cmp

##this script checks the role of the node
#and installs cron and amen (for coordinator)
try:
    config_file = file('/etc/yanoama.conf').read()
    config = json.loads(config_file)
except:
    print "There was an error in your configuration file (/etc/yanoama.conf)"
    raise

#globals
_coordinators = config.get('coordinators', {})
_ple_deployment = config.get('ple_deployment', {})
DEPLOYMENT_PATH=_ple_deployment['path']
_amen = config.get('amen', {})
STORAGE_PATH = _ple_deployment['storage_path']
AMEN_HOME=_amen['home']
HOSTNAME = subprocess.Popen(['hostname'], \
                            stdout=subprocess.PIPE,\
                            close_fds=True).\
                            communicate()[0].rstrip()
_amen = config.get('amen', {})
_backend = _amen.get('backend', {})
_mongo = _backend.get('mongo', {})
MONGO_PORT=_mongo.get('port',39167)
MONGO_REPLICATION_PORT=_mongo.get('replication_port',40167)
_pilot = config.get('pilot', {})
PILOT_PORT=_pilot.get('port',44444)
##gets a comma-separated samples as a
#string for cron jobs
def get_cron_time_samples(start,stop,samples):
    """Get a comma-separated samples as a
    string for cron jobs.

    Keyword arguments:
    start -- start integer to sample
    stop -- stop integer to sample
    samples -- number of samples

    Returns:
    string -- comma-separated list of samples
    from the start-stop range

    """
    return str(sample(xrange(start,stop),samples)).\
        replace(' ','').\
        replace(']','').replace('[','')

#this installs a cron job to the local
#cron. the frequency might be once, twice
#a day.
#args:
#job: string with cron job
#frequency: how many daily calls,
#    default is ONCE_A_DAY
def install_cron_job(job,frequency='ONCE_A_DAY'):
    """Add a job to the local
    crontable. the frequency might be once, twice
    a day..

    Keyword arguments:
    job -- command or job from cron
    frequency -- to run this job (default ONCE_A_DAY)
    """

    minute=get_cron_time_samples(0, 60, 1)
    hour=""
    if(frequency=='TWICE_A_DAY'):
        #twice a day
        #once a day
        hour= get_cron_time_samples(0, 24, 2)
    else:
        #once a day
        hour=get_cron_time_samples(0, 8, 1)
    mystring = str(minute)+"       "+str(hour)+\
    "     *       *       *       "+job
    cron_temp_file='/tmp/cron.temp'
    #first backup current jobs
    current_jobs=subprocess.\
        Popen(['crontab', '-l'], \
          stdout=subprocess.PIPE, close_fds=True).communicate()[0]
    f = open(cron_temp_file, 'w')
    if len(current_jobs)>0:
        f.write(current_jobs)
    f.write(mystring+'\n')
    f.close()
    subprocess.Popen(['crontab',cron_temp_file], \
                    stdout=subprocess.PIPE, \
                    close_fds=True)

    #clean up
    subprocess.Popen(['rm', cron_temp_file], \
                         stdout=subprocess.PIPE, close_fds=True)

def install_runnable_script(script,\
                            deployment_path=DEPLOYMENT_PATH,\
                            dst_dir='/bin/'):
    """Install a script to a directory of the local
    path and make it runnable, where script is a relative
    path of deployment_path.

    Keyword arguments:
    script -- path to the script relative to
    deployment path
    deployment_path -- where yanoama is installed
    (default DEPLOYMENT_PATH global)
    dst_dir -- destination path to install this
    script (default '/bin')

    """
    subprocess.Popen(['sudo','cp',\
                      deployment_path+\
                      '/'+script,\
                      dst_dir],\
                     stdout=subprocess.PIPE, \
                     close_fds=True)
    #making it runnable
    subprocess.Popen(['sudo','chmod', 'guo+x',\
                      (dst_dir+'/'+script.split('/')[-1])],\
                     stdout=subprocess.PIPE,\
                     close_fds=True)

#deployment is based on the node role
#that's either coordinator or peer
if __name__ == '__main__':
    """Deploy yanoama based on the node role,
    either peer or coordinator.

    """
    #update services file
    services_file='/etc/services'
    services_origin_file='/etc/services.origin'
    temp_services_file='/tmp/services'
    #check if /etc/hosts.origin exists
    if not os.path.exists(services_origin_file):
        subprocess.Popen(['sudo','cp', services_file,services_origin_file], \
                         stdout=subprocess.PIPE, close_fds=True)
    while not os.path.exists(services_origin_file):
        print 'waiting for copying origin...'
        sleep(1/1000.0)
    subprocess.Popen(['sudo','cp', services_origin_file,services_file], \
                     stdout=subprocess.PIPE, close_fds=True)
    subprocess.Popen(['sudo','cp', services_origin_file,services_file], \
                     stdout=subprocess.PIPE, close_fds=True)
    while not cmp(services_origin_file, services_file):
        print 'waiting for copying services file...'
        sleep(1/1000.0)
    subprocess.Popen(['cp', services_origin_file,temp_services_file], \
                     stdout=subprocess.PIPE, close_fds=True)
    while not os.path.exists(temp_services_file):
        print 'waiting for copying services temp file...'
        sleep(1/1000.0)
    f = open(temp_services_file, 'a')
    f.write('#local services'+'\n')
    f.write('pilot\t\t'+str(PILOT_PORT)+'/tcp\n')
    f.write('mongo\t\t'+str(MONGO_PORT)+'/tcp\n')
    f.write('mongo_replication\t\t'+str(MONGO_REPLICATION_PORT)+'/tcp\n')
    f.close()
    print "waiting for copying temp file..."
    sleep(1)
    subprocess.Popen(['sudo','cp',temp_services_file,services_file], \
                     stdout=subprocess.PIPE, close_fds=True)
    while not cmp(services_file, temp_services_file):
        print "waiting for copying temp file to services..."
        sleep(1/1000.0)
    sleep(1)
    #clean up
    subprocess.Popen(['rm',temp_services_file], \
                     stdout=subprocess.PIPE, close_fds=True)

    if HOSTNAME in _coordinators.keys():
        """this is a coordinator
        #here the two-step installation procedure
        #1) install cron jobs: get_rtt.py and
        #                    peering.py
        #2) install mongodb database as part of amen
        #                    monitoring system.

        """
        #
        #
        #(1)installing cron jobs
        ##    get_rtt
        install_runnable_script('/yanoama/monitoring/get_rtt.py')
        #install as a cron job
        install_cron_job("get_rtt.py > /tmp/get_rtt_output.log 2>&1")
        ##    peering.py
        install_runnable_script('/yanoama/system/peering.py')
        #install as a cron job for running twice a day
        install_cron_job("peering.py > /tmp/peering_output.log 2>&1",\
                         'TWICE_A_DAY')
        #(2) install amen in coordinator: mongodb
        #based on
        #http://docs.mongodb.org/manual/tutorial/install-mongodb-on-red-hat-centos-or-fedora-linux/
        #last visit 5 March 2013
        #listen on 39167 port
        subprocess.Popen(['sudo','cp',DEPLOYMENT_PATH+'/contrib/mongodb/mongod.conf','/etc/mongod.conf'], stdout=subprocess.PIPE, close_fds=True)
        subprocess.Popen(['sudo','/sbin/chkconfig','mongod','on'], stdout=subprocess.PIPE, close_fds=True)
        subprocess.Popen(['sudo','/sbin/service','mongod','start'], stdout=subprocess.PIPE, close_fds=True)

    else:
        """peer/storage node role
        #two-step installation procedure
        #1) install cron jobs: membership.py
        #
        #2) install and run amen agent (daemon)
        """
        #
        #
        #(1) install membership script
        install_runnable_script('/yanoama/system/membership.py')
        install_cron_job("membership.py > /tmp/membership_output.log 2>&1")

        #(2) instal/run amen monitoring agent
        #default dir for storing objects/videos
        if os.path.exists(STORAGE_PATH):
            subprocess.Popen(['rm','-rf',STORAGE_PATH], stdout=subprocess.PIPE, close_fds=True)
        subprocess.Popen(['mkdir',STORAGE_PATH], stdout=subprocess.PIPE, close_fds=True)
        #install amen agent/deamon in this peer
        if not os.path.exists(AMEN_HOME):
            subprocess.Popen(['sudo','mkdir',AMEN_HOME],stdout=subprocess.PIPE, close_fds=True)
            subprocess.Popen(['sudo','mkdir',AMEN_HOME+'/bin'],stdout=subprocess.PIPE, close_fds=True)
            subprocess.Popen(['sudo','touch',AMEN_HOME+'/amend.log'],stdout=subprocess.PIPE, close_fds=True)
        subprocess.Popen(['sudo','cp',DEPLOYMENT_PATH+'/contrib/amen/amend',AMEN_HOME+'/bin/'],stdout=subprocess.PIPE, close_fds=True)
        #run daemon
        subprocess.Popen(['sudo','chmod','+x',AMEN_HOME+'/bin/amend'],stdout=subprocess.PIPE, close_fds=True)
        subprocess.Popen(['sudo',AMEN_HOME+'/bin/amend','start'],stdout=subprocess.PIPE, close_fds=True)

        print "outside"<|endoftext|>


<commit_before>#! /usr/bin/env python
# coding=utf8

from BotModule import BotModule
import twitter, time, HTMLParser, config

class TwitterUser:
	def __init__(self, nick):
		self.nick = nick
		self.lastUpdate = time.time()
		self.lastId = 0

	def updateTimestamp(self, timestamp):
		if timestamp > self.lastUpdate:
			self.lastUpdate = timestamp

	def updateId(self, lastId):
		if id > self.lastId:
			self.lastId = lastId

class TwitterModule(BotModule):

	def __init__(self):
		cfg = config.Config(file("bot.config"))
		modulecfg = cfg.twitter

		self.offset = 60 * 5
		self.users = [TwitterUser('cyberchampionka')]
		self.lastTick = time.time()
		self.api = twitter.Api(consumer_key=modulecfg.consumer_key,
								consumer_secret=modulecfg.consumer_secret_token,
								access_token_key=modulecfg.access_token_key,
								access_token_secret=modulecfg.access_token_secret)
		self.htmlparser = HTMLParser.HTMLParser()

		return

	def tick(self):
		tmp = 0
		timestamp = time.time()

		# max requests = 150!
		if timestamp - self.lastTick > self.offset:
			if self.DEBUG:
				print "Processing tick"
			users_tmp = self.users
			for user in users_tmp:
				if self.DEBUG:
					print "Processing " + user.nick + "s tweets"
				try:
					statuses = self.api.GetUserTimeline(id = user.nick, since_id = user.lastId, include_rts = True)
				except twitter.TwitterError, err:
					if str(err).startswith('Rate limit exceeded.'):
						if self.DEBUG:
							print '%s: Disabling for 60 Minutes' % str(err)
						self.lastTick = timestamp + 60*60
						return
					elif str(err).startswith('Not authorized'):
						if self.DEBUG:
							print 'Removing %s: %s' % (user.nick, str(err))
						self.users.remove(user)
					elif str(err).startswith('Not found'):
						if self.DEBUG:
							print 'Removing %s: %s' % (user.nick, str(err))
						self.users.remove(user)
					else:
						if self.DEBUG:
							print 'Unknown Error! Removing %s: %s' % (user.nick, str(err))
						self.users.remove(user)
					continue

				except Exception as e:
					if self.DEBUG:
						print 'unhandled exception: %s' % str(e)
					continue

				tmp_created = 0
				tmp_id = 0
				for status in reversed(statuses):
					if status.created_at_in_seconds > user.lastUpdate:
						if self.DEBUG:
							print "Sending to channel: [" + user.nick + "] " + status.text.replace('\n','').replace('\r','')
						self.sendPublicMessage('[@' + self.htmlparser.unescape(user.nick).encode('utf-8') + '] ' + self.htmlparser.unescape(status.text.replace('\n','').replace('\r','')).encode('utf-8'))
						if tmp_created < status.created_at_in_seconds:
							tmp_created = status.created_at_in_seconds
						if tmp_id < status.id:
							tmp_id = status.id

				user.updateTimestamp(tmp_created)
				user.updateId(tmp_id)

			self.lastTick = timestamp

	def command(self, nick, cmd, args, type):
		if cmd == '!t' or cmd == '!twitter':
			if len(args) == 0:
				answer = "Twitter module expects at least 1 parameter!"

				if type == 'public':
					self.sendPublicMessage(answer)
				elif type == 'private':
					self.sendPrivateMessage(nick, answer)
				return

			if len(args) > 1 and args[0] == 'add':
				if self.DEBUG:
					print 'Adding ' + ', '.join(args[1:])
				self.users.extend([TwitterUser(u) for u in args[1:]])

			elif len(args) > 1 and args[0] == 'del':
				if self.DEBUG:
					print 'Removing %s' % ', '.join(args[1:])
				for u in args[1:]:
					for user in self.users:
						if user.nick == u:
							self.users.remove(user)

			elif args[0] == 'list':
				if self.DEBUG:
					print 'Printing userlist ' + ', '.join(user.nick for user in self.users)

				answer = '[Twitter] ' + ', '.join(user.nick for user in self.users)

				if type == 'public':
					self.sendPublicMessage(answer)
				elif type == 'private':
					self.sendPrivateMessage(nick, answer)

			elif type == 'public' and 0 < len(args) < 3:
				number = 0
				if len(args) > 1:
					try:
						number = int(args[1],0)
					except:
						return
				try:
					statuses = self.api.GetUserTimeline(args[0])
				except:
					return
				if statuses is not None and 0 <= number < len(statuses):
					if self.DEBUG:
						print "Sending to channel: [" + args[0] + "] " + statuses[0].text.replace('\n','').replace('\r','')
					self.sendPublicMessage('[@' + args[0] + '] ' + self.htmlparser.unescape(statuses[number].text.replace('\n','').replace('\r','')).encode('utf-8'))

	def help(self, nick):
		self.sendPrivateMessage(nick, "!t[witter] <nick>[ <i>] Zeigt den <i>-t letzten Tweet von <nick>")
		self.sendPrivateMessage(nick, "!t[witter] add <nick>[ <nick2>][ <nick3>]... Fügt <nick> hinzu")
		self.sendPrivateMessage(nick, "!t[witter] del <nick>[ <nick2>][ <nick3>]... Entfernt <nick>")
		self.sendPrivateMessage(nick, "!t[witter] list Zeigt die derzeit gefollowten User an")

		return
<commit_msg>Fixed typo<commit_after>#! /usr/bin/env python
# coding=utf8

from BotModule import BotModule
import twitter, time, HTMLParser, config

class TwitterUser:
	def __init__(self, nick):
		self.nick = nick
		self.lastUpdate = time.time()
		self.lastId = 0

	def updateTimestamp(self, timestamp):
		if timestamp > self.lastUpdate:
			self.lastUpdate = timestamp

	def updateId(self, lastId):
		if id > self.lastId:
			self.lastId = lastId

class TwitterModule(BotModule):

	def __init__(self):
		cfg = config.Config(file("bot.config"))
		modulecfg = cfg.twitter

		self.offset = 60 * 5
		self.users = [TwitterUser('cyberchampionka')]
		self.lastTick = time.time()
		self.api = twitter.Api(consumer_key=modulecfg.consumer_key,
								consumer_secret=modulecfg.consumer_secret,
								access_token_key=modulecfg.access_token_key,
								access_token_secret=modulecfg.access_token_secret)
		self.htmlparser = HTMLParser.HTMLParser()

		return

	def tick(self):
		tmp = 0
		timestamp = time.time()

		# max requests = 150!
		if timestamp - self.lastTick > self.offset:
			if self.DEBUG:
				print "Processing tick"
			users_tmp = self.users
			for user in users_tmp:
				if self.DEBUG:
					print "Processing " + user.nick + "s tweets"
				try:
					statuses = self.api.GetUserTimeline(id = user.nick, since_id = user.lastId, include_rts = True)
				except twitter.TwitterError, err:
					if str(err).startswith('Rate limit exceeded.'):
						if self.DEBUG:
							print '%s: Disabling for 60 Minutes' % str(err)
						self.lastTick = timestamp + 60*60
						return
					elif str(err).startswith('Not authorized'):
						if self.DEBUG:
							print 'Removing %s: %s' % (user.nick, str(err))
						self.users.remove(user)
					elif str(err).startswith('Not found'):
						if self.DEBUG:
							print 'Removing %s: %s' % (user.nick, str(err))
						self.users.remove(user)
					else:
						if self.DEBUG:
							print 'Unknown Error! Removing %s: %s' % (user.nick, str(err))
						self.users.remove(user)
					continue

				except Exception as e:
					if self.DEBUG:
						print 'unhandled exception: %s' % str(e)
					continue

				tmp_created = 0
				tmp_id = 0
				for status in reversed(statuses):
					if status.created_at_in_seconds > user.lastUpdate:
						if self.DEBUG:
							print "Sending to channel: [" + user.nick + "] " + status.text.replace('\n','').replace('\r','')
						self.sendPublicMessage('[@' + self.htmlparser.unescape(user.nick).encode('utf-8') + '] ' + self.htmlparser.unescape(status.text.replace('\n','').replace('\r','')).encode('utf-8'))
						if tmp_created < status.created_at_in_seconds:
							tmp_created = status.created_at_in_seconds
						if tmp_id < status.id:
							tmp_id = status.id

				user.updateTimestamp(tmp_created)
				user.updateId(tmp_id)

			self.lastTick = timestamp

	def command(self, nick, cmd, args, type):
		if cmd == '!t' or cmd == '!twitter':
			if len(args) == 0:
				answer = "Twitter module expects at least 1 parameter!"

				if type == 'public':
					self.sendPublicMessage(answer)
				elif type == 'private':
					self.sendPrivateMessage(nick, answer)
				return

			if len(args) > 1 and args[0] == 'add':
				if self.DEBUG:
					print 'Adding ' + ', '.join(args[1:])
				self.users.extend([TwitterUser(u) for u in args[1:]])

			elif len(args) > 1 and args[0] == 'del':
				if self.DEBUG:
					print 'Removing %s' % ', '.join(args[1:])
				for u in args[1:]:
					for user in self.users:
						if user.nick == u:
							self.users.remove(user)

			elif args[0] == 'list':
				if self.DEBUG:
					print 'Printing userlist ' + ', '.join(user.nick for user in self.users)

				answer = '[Twitter] ' + ', '.join(user.nick for user in self.users)

				if type == 'public':
					self.sendPublicMessage(answer)
				elif type == 'private':
					self.sendPrivateMessage(nick, answer)

			elif type == 'public' and 0 < len(args) < 3:
				number = 0
				if len(args) > 1:
					try:
						number = int(args[1],0)
					except:
						return
				try:
					statuses = self.api.GetUserTimeline(args[0])
				except:
					return
				if statuses is not None and 0 <= number < len(statuses):
					if self.DEBUG:
						print "Sending to channel: [" + args[0] + "] " + statuses[0].text.replace('\n','').replace('\r','')
					self.sendPublicMessage('[@' + args[0] + '] ' + self.htmlparser.unescape(statuses[number].text.replace('\n','').replace('\r','')).encode('utf-8'))

	def help(self, nick):
		self.sendPrivateMessage(nick, "!t[witter] <nick>[ <i>] Zeigt den <i>-t letzten Tweet von <nick>")
		self.sendPrivateMessage(nick, "!t[witter] add <nick>[ <nick2>][ <nick3>]... Fügt <nick> hinzu")
		self.sendPrivateMessage(nick, "!t[witter] del <nick>[ <nick2>][ <nick3>]... Entfernt <nick>")
		self.sendPrivateMessage(nick, "!t[witter] list Zeigt die derzeit gefollowten User an")

		return
<|endoftext|>


<commit_before>#! /usr/bin/env python
# coding=utf8

from BotModule import BotModule
import twitter, time, HTMLParser

class TwitterUser:
	def __init__(self, nick):
		self.nick = nick
		self.lastUpdate = time.time()
		self.lastId = 0

	def updateTimestamp(self, timestamp):
		if timestamp > self.lastUpdate:
			self.lastUpdate = timestamp

	def updateId(self, lastId):
		if id > self.lastId:
			self.lastId = lastId

class TwitterModule(BotModule):

	def __init__(self):
		self.offset = 30
		self.users = [TwitterUser('cyberchampionka')]
		self.lastTick = time.time()
		self.api = twitter.Api()
		self.htmlparser = HTMLParser.HTMLParser()

		return

	def tick(self):
		tmp = 0
		timestamp = time.time()

		# max requests = 150!
		if timestamp - self.lastTick > self.offset:
			if self.DEBUG:
				print "Processing tick"
			users_tmp = self.users
			for user in users_tmp:
				if self.DEBUG:
					print "Processing " + user.nick + "s tweets"
				try:
					statuses = self.api.GetUserTimeline(id = user.nick, since_id = user.lastId, include_rts = True)
				except twitter.TwitterError, err:
					if str(err).startswith('Rate limit exceeded.'):
						if self.DEBUG:
							print '%s: Disabling for 60 Minutes' % str(err)
						self.lastTick = timestamp + 60*60
						return
					elif str(err).startswith('Not authorized'):
						if self.DEBUG:
							print 'Removing %s: %s' % (user.nick, str(err))
						self.users.remove(user)
					elif str(err).startswith('Not found'):
						if self.DEBUG:
							print 'Removing %s: %s' % (user.nick, str(err))
						self.users.remove(user)
					else:
						if self.DEBUG:
							print 'Unknown Error! Removing %s: %s' % (user.nick, str(err))
						self.users.remove(user)
					continue

				except Exception as e:
					if self.DEBUG:
						print 'unhandled exception: %s' % str(e)
					continue

				tmp_created = 0
				tmp_id = 0
				for status in statuses:
					if status.created_at_in_seconds > user.lastUpdate:
						if self.DEBUG:
							print "Sending to channel: [" + user.nick + "] " + status.text.replace('\n','').replace('\r','')
						self.sendPublicMessage('[' + self.htmlparser.unescape(user.nick).encode('utf-8') + '] ' + self.htmlparser.unescape(status.text.replace('\n','').replace('\r','')).encode('utf-8'))
						if tmp_created < status.created_at_in_seconds:
							tmp_created = status.created_at_in_seconds
						if tmp_id < status.id:
							tmp_id = status.id

				user.updateTimestamp(tmp_created)
				user.updateId(tmp_id)

			self.lastTick = timestamp

	def command(self, nick, cmd, args, type):
		if cmd == '!t' or cmd == '!twitter':
			if len(args) > 1 and args[0] == 'add':
				if self.DEBUG:
					print 'Adding ' + ', '.join(args[1:])
				self.users.extend([TwitterUser(u) for u in args[1:]])

			elif len(args) > 1 and args[0] == 'del':
				if self.DEBUG:
					print 'Removing %s' % ', '.join(args[1:])
				for u in args[1:]:
					tmp_user = self.users
					for user in tmp_user:
						if user.nick == u:
							self.users.remove(user)

			elif type == 'public' and 0 < len(args) < 3:
				number = 0
				if len(args) > 1:
					try:
						number = int(args[1],0)
					except:
						return
				try:
					statuses = self.api.GetUserTimeline(args[0])
				except:
					return
				if statuses is not None and 0 <= number < len(statuses):
					if self.DEBUG:
						print "Sending to channel: [" + args[0] + "] " + statuses[0].text.replace('\n','').replace('\r','')
					self.sendPublicMessage('[' + args[0] + '] ' + self.htmlparser.unescape(statuses[number].text.replace('\n','').replace('\r','')).encode('utf-8'))

	def help(self, nick):
		self.sendPrivateMessage(nick, "!t[witter] <nick>[ <i>] Zeigt den <i>-t letzten Tweet von <nick>")
		self.sendPrivateMessage(nick, "!t[witter] add <nick>[ <nick2>][ <nick3>]... Fügt <nick> hinzu")
		self.sendPrivateMessage(nick, "!t[witter] del <nick>[ <nick2>][ <nick3>]... Entfernt <nick>")

		return
<commit_msg>Changed the output, added @ to nicks and prints statuses in the correct order<commit_after>#! /usr/bin/env python
# coding=utf8

from BotModule import BotModule
import twitter, time, HTMLParser

class TwitterUser:
	def __init__(self, nick):
		self.nick = nick
		self.lastUpdate = time.time()
		self.lastId = 0

	def updateTimestamp(self, timestamp):
		if timestamp > self.lastUpdate:
			self.lastUpdate = timestamp

	def updateId(self, lastId):
		if id > self.lastId:
			self.lastId = lastId

class TwitterModule(BotModule):

	def __init__(self):
		self.offset = 30
		self.users = [TwitterUser('cyberchampionka')]
		self.lastTick = time.time()
		self.api = twitter.Api()
		self.htmlparser = HTMLParser.HTMLParser()

		return

	def tick(self):
		tmp = 0
		timestamp = time.time()

		# max requests = 150!
		if timestamp - self.lastTick > self.offset:
			if self.DEBUG:
				print "Processing tick"
			users_tmp = self.users
			for user in users_tmp:
				if self.DEBUG:
					print "Processing " + user.nick + "s tweets"
				try:
					statuses = self.api.GetUserTimeline(id = user.nick, since_id = user.lastId, include_rts = True)
				except twitter.TwitterError, err:
					if str(err).startswith('Rate limit exceeded.'):
						if self.DEBUG:
							print '%s: Disabling for 60 Minutes' % str(err)
						self.lastTick = timestamp + 60*60
						return
					elif str(err).startswith('Not authorized'):
						if self.DEBUG:
							print 'Removing %s: %s' % (user.nick, str(err))
						self.users.remove(user)
					elif str(err).startswith('Not found'):
						if self.DEBUG:
							print 'Removing %s: %s' % (user.nick, str(err))
						self.users.remove(user)
					else:
						if self.DEBUG:
							print 'Unknown Error! Removing %s: %s' % (user.nick, str(err))
						self.users.remove(user)
					continue

				except Exception as e:
					if self.DEBUG:
						print 'unhandled exception: %s' % str(e)
					continue

				tmp_created = 0
				tmp_id = 0
				for status in reversed(statuses):
					if status.created_at_in_seconds > user.lastUpdate:
						if self.DEBUG:
							print "Sending to channel: [" + user.nick + "] " + status.text.replace('\n','').replace('\r','')
						self.sendPublicMessage('[@' + self.htmlparser.unescape(user.nick).encode('utf-8') + '] ' + self.htmlparser.unescape(status.text.replace('\n','').replace('\r','')).encode('utf-8'))
						if tmp_created < status.created_at_in_seconds:
							tmp_created = status.created_at_in_seconds
						if tmp_id < status.id:
							tmp_id = status.id

				user.updateTimestamp(tmp_created)
				user.updateId(tmp_id)

			self.lastTick = timestamp

	def command(self, nick, cmd, args, type):
		if cmd == '!t' or cmd == '!twitter':
			if len(args) > 1 and args[0] == 'add':
				if self.DEBUG:
					print 'Adding ' + ', '.join(args[1:])
				self.users.extend([TwitterUser(u) for u in args[1:]])

			elif len(args) > 1 and args[0] == 'del':
				if self.DEBUG:
					print 'Removing %s' % ', '.join(args[1:])
				for u in args[1:]:
					tmp_user = self.users
					for user in tmp_user:
						if user.nick == u:
							self.users.remove(user)

			elif type == 'public' and 0 < len(args) < 3:
				number = 0
				if len(args) > 1:
					try:
						number = int(args[1],0)
					except:
						return
				try:
					statuses = self.api.GetUserTimeline(args[0])
				except:
					return
				if statuses is not None and 0 <= number < len(statuses):
					if self.DEBUG:
						print "Sending to channel: [" + args[0] + "] " + statuses[0].text.replace('\n','').replace('\r','')
					self.sendPublicMessage('[' + args[0] + '] ' + self.htmlparser.unescape(statuses[number].text.replace('\n','').replace('\r','')).encode('utf-8'))

	def help(self, nick):
		self.sendPrivateMessage(nick, "!t[witter] <nick>[ <i>] Zeigt den <i>-t letzten Tweet von <nick>")
		self.sendPrivateMessage(nick, "!t[witter] add <nick>[ <nick2>][ <nick3>]... Fügt <nick> hinzu")
		self.sendPrivateMessage(nick, "!t[witter] del <nick>[ <nick2>][ <nick3>]... Entfernt <nick>")

		return
<|endoftext|>