<commit_before>#!/usr/bin/python
# This code is an example for a tutorial on Ubuntu Unity/Gnome AppIndicators:
# http://candidtim.github.io/appindicator/2014/09/13/ubuntu-appindicator-step-by-step.html
# icons from https://materialdesignicons.com/

import os
import time
import signal
import threading
from datetime import datetime
from datetime import timedelta

from gi.repository import Gtk as gtk
from gi.repository import AppIndicator3 as appindicator

APPINDICATOR_ID = 'batteryindicator'
indicator = None
icon = None
category = appindicator.IndicatorCategory.SYSTEM_SERVICES
th_background = None
th_background_stop = None

def run_monitoring(stop_event):
    t1 = datetime.now()
    while not stop_event.is_set():
        t2 = datetime.now()
        if (t2 - t1) > timedelta(seconds=3):
            t1 = t2
            # do monitoring
        time.sleep(0.1)

def run_background_monitoring():
    global th_background
    global th_background_stop
    th_background_stop = threading.Event()
    th_background = threading.Thread(
        target=run_monitoring, args=[th_background_stop])
    th_background.start()

def set_icon(new_icon):
    global icon
    icon = new_icon
    indicator.set_icon(icon)

def setup_indicator(icon):
    global indicator
    indicator = appindicator.Indicator.new(
        APPINDICATOR_ID, icon, category)
    indicator.set_status(appindicator.IndicatorStatus.ACTIVE)
    indicator.set_menu(build_menu())
    indicator.set_label('84% 0:21', '')
    set_icon(icon)

def build_menu():
    menu = gtk.Menu()
    item_quit = gtk.MenuItem('Quit')
    item_quit.connect('activate', quit)
    menu.append(item_quit)
    menu.show_all()
    return menu

def quit(source):
    if th_background_stop:
        th_background_stop.set()
    gtk.main_quit()

def run_forever():
    gtk.main()

def run():
    signal.signal(signal.SIGINT, signal.SIG_DFL)
    setup_indicator(
        os.path.abspath('ic_battery_charging_white_48dp.png'))
    run_background_monitoring()
    run_forever()

if __name__ == "__main__":
    run()
<commit_msg>autoupdate battery life<commit_after>#!/usr/bin/python
# This code is an example for a tutorial on Ubuntu Unity/Gnome AppIndicators:
# http://candidtim.github.io/appindicator/2014/09/13/ubuntu-appindicator-step-by-step.html
# icons from https://materialdesignicons.com/

import os
import time
import signal
import threading
from datetime import datetime
from datetime import timedelta

from gi.repository import Gtk as gtk
from gi.repository import AppIndicator3 as appindicator

APPINDICATOR_ID = 'batteryindicator'
indicator = None
icon = None
category = appindicator.IndicatorCategory.SYSTEM_SERVICES
th_background = None
th_background_stop = None
is_charging = None
battery_life = None

def probing():
    global battery_life
    global is_charging

    if battery_life is None:
        battery_life = 0

    if battery_life is not None:
        if battery_life <= 0:
            is_charging = True
        if battery_life >= 100:
            is_charging = False

        direction = 1 if is_charging else -1
        battery_life += 5 * direction

    set_label()

def run_monitoring(stop_event):
    t1 = datetime.now()
    while not stop_event.is_set():
        t2 = datetime.now()
        if (t2 - t1) > timedelta(seconds=0.5):
            t1 = t2
            probing()
        time.sleep(0.1)

def run_background_monitoring():
    global th_background
    global th_background_stop
    th_background_stop = threading.Event()
    th_background = threading.Thread(
        target=run_monitoring, args=[th_background_stop])
    th_background.start()

def set_icon(new_icon):
    global icon
    icon = new_icon
    indicator.set_icon(icon)

def set_label():
    indicator.set_label('{0}% 0:21'.format(battery_life), '')

def setup_indicator(icon):
    global indicator
    indicator = appindicator.Indicator.new(
        APPINDICATOR_ID, icon, category)
    indicator.set_status(appindicator.IndicatorStatus.ACTIVE)
    indicator.set_menu(build_menu())
    set_icon(icon)
    set_label()

def build_menu():
    menu = gtk.Menu()
    item_quit = gtk.MenuItem('Quit')
    item_quit.connect('activate', quit)
    menu.append(item_quit)
    menu.show_all()
    return menu

def quit(source):
    if th_background_stop:
        th_background_stop.set()
    gtk.main_quit()

def run_forever():
    gtk.main()

def run():
    signal.signal(signal.SIGINT, signal.SIG_DFL)
    setup_indicator(
        os.path.abspath('ic_battery_charging_white_48dp.png'))
    run_background_monitoring()
    run_forever()

if __name__ == "__main__":
    run()
<|endoftext|>


<commit_before>from kivy.app import App
from kivy.properties import *
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.button import Button
from kivy.clock import Clock
from kivy.core.window import Window
from kivy.core.audio import SoundLoader
from kivy.uix.screenmanager import Screen, ScreenManager, FadeTransition, SlideTransition, NoTransition
from kivy.uix.settings import SettingsWithSidebar
from kivy.uix.popup import Popup
from kivy.metrics import dp
from kivy import platform
from kivy.clock import Clock
from jsonConfig import settingsjson
import datetime
import pickle

from gameplay import GamePlayScreen
from PlayerNamePopup import PlayerNamePopup

class TutorialScreen(Popup):
    pass

class EndGameScreen(Screen):
    name_of_players = ListProperty(['','','',''])
    scores_of_players = ListProperty()
    screenManager = ObjectProperty()
    number_of_players = NumericProperty()
    game = ObjectProperty()

    def on_enter(self,*args):
        self.name_of_players = [x for y,x in sorted(zip(self.scores_of_players,self.name_of_players))][::-1]
        self.scores_of_players = sorted(self.scores_of_players)[::-1]

class PlayerSection(Button):
    myvalue = NumericProperty(4)

    def __init__(self, **kwargs):
        super(PlayerSection, self).__init__(**kwargs)
        self.size = Window.size[0] // 6, Window.size[1] // 6

class GameLayout(ScreenManager):
    ''' This class manages the movements between the various screen and the sound '''

    name_of_players = ListProperty(['Score', '', '', ''])
    number_of_players = NumericProperty(1)
    scores_of_players = ListProperty([0, 0, 0, 0])

    # True if there is a game going on
    active = BooleanProperty(False)
    soundActivated = BooleanProperty(False)

    directory = StringProperty()

    def __init__(self, **kwargs):
        super(GameLayout, self).__init__(**kwargs)
        self.playscreen = self.get_screen('game')
        self.sound = SoundLoader.load('set_song.wav')
        self.transition = FadeTransition()

    # screen play navigation
    def goToIntro(self, *arg):
        self.transition = NoTransition()
        self.current = 'intro'

    def goToGameScreen(self, *arg):
        self.transition = FadeTransition()
        self.current = 'game'

    # Dealing with Sound
    def on_soundActivated(self, obj, value):
        ''' Turn the intro song on or off '''
        if value:
            self.sound.loop = True
            self.sound.play()
        else:
            self.sound.stop()

    def pickleFile(self):
        return self.directory + "name_of_players.pkl"

    def on_name_of_players(self,value, obj):
        if platform == 'macosx' or platform == 'ios':
            with open(self.pickleFile(), "wb") as playersNamePickle:
                pickle.dump(list(self.name_of_players), playersNamePickle)

    def player_name_popup(self, numPlayers):
        '''called after selecting number of players'''
        self.number_of_players = numPlayers
        if self.number_of_players == 1:
            self.goToGameScreen()
            return
        tempNames = ['John', 'Sally', 'Sam', 'Joey']
        if platform == 'macosx' or platform == 'ios':
            try:
                names = pickle.load(open(self.pickleFile(), "rb"))
                for index, name in enumerate(names):
                    tempNames[index] = name
            except Exception as exception:
                print("Loading from Pickling went wrong, using default names")

        self.name_of_players = tempNames
        playername = PlayerNamePopup(self.name_of_players, numPlayers)
        playername.open()
        playername.bind(on_dismiss = self.goToGameScreen)

    def quit(self):
        ''' You are quiting the current game '''
        if self.active:
            #self.playscreen.scores_of_players = [0,0,0,0]
            self.playscreen.aiScore = 0
            self.goToIntro()
            self.playscreen.stopRotation()
            self.active = False

def boolFromJS(value):
    ''' JSON config returns '1' and '0' for True and False'''
    return True if value == '1' else False

class ShugouApp(App):
    active = BooleanProperty(False)

    def build(self):
        Clock.max_iteration = 50
        self.use_kivy_settings = False
        self.gamelayout = GameLayout()
        self.settings_cls = SettingsWithSidebar
        self.loadSettings()
        self.gamelayout.bind(active=self.changeActive)
        self.gamelayout.directory = self.whereToSave()
        return self.gamelayout

    def whereToSave(self):
        # Returns in which directory you can store files
        return self.get_application_config().rstrip("shugou.ini")

    def changeActive(self,instance,value):
        # This doesn't work.. crashes if the build_settings wasn't launched first
        #self.quitButton.disabled = not self.gamelayout.active
        pass

    def loadSettings(self):
        # Load the values already stored into the file
        self.gamelayout.soundActivated = boolFromJS(
            self.config.get('settings', 'sound'))
        speedSettings = {'slow':10, 'normal':5, 'fast':1, 'off': 0, 'True': 1}
        speed = speedSettings[self.config.get('settings', 'hint')]
        if speed != 0:
            self.gamelayout.playscreen.displayHintTimer = speed
            self.gamelayout.playscreen.hintActivated = True
        else:
            self.gamelayout.playscreen.hintActivated = False

        self.gamelayout.playscreen.aiActivated = boolFromJS(
            self.config.get('settings', 'ai'))

    def build_config(self, config):
        config.setdefaults('settings', {'sound': False,
                                        'ai': False,
                                        'hint': 'off'})

    def build_settings(self, settings):
        self.settings = settings
        self.settings.interface.menu.width = dp(100)
        settings.add_json_panel('Settings', self.config, data=settingsjson)
        settingsCloseButton = settings.interface.ids.menu.ids.button
        self.settingsCloseButton = settingsCloseButton
        self.settingsCloseButton.text = "Return"
        settingsCloseButton.on_press = self.leaveSettingsPanel
        settings.interface.ids.menu.add_widget(Button(text="Tutorial",
                                                      size_hint = (None, None),
                                                      x= settingsCloseButton.x,
                                                      y = settingsCloseButton.top + 10,
                                                      size = settingsCloseButton.size,
                                                      on_press= self.moveToTutorial))

        self.quitButton = Button(text="Quit",
                              background_color = [1,0,0,1],
                              size_hint = (None, None),
                              x= settingsCloseButton.x,
                              y = settingsCloseButton.top + settingsCloseButton.height + 20,
                              size = settingsCloseButton.size,
                              disabled = False,
                              on_press= self.quit)

        settings.interface.ids.menu.add_widget(self.quitButton)
        settings.on_close = self.quit

    def quit(self, *arg):
        self.gamelayout.quit()
        self.settingsCloseButton.trigger_action()

    def moveToTutorial(self, buttonInstance):
        tutorial = TutorialScreen(title="Tutorial")
        tutorial.open()

    def leaveSettingsPanel(self, *arg):
        ''' activated when you exit the setting panels'''
        self.gamelayout.playscreen.setUpHint()
        self.gamelayout.playscreen.setUpAI()

    def on_config_change(self, config, section, key, value):
        if key == 'sound':
            self.gamelayout.soundActivated = boolFromJS(value)
        if key == 'hint':
            speedSettings = {u'slow':10, u'normal':5, u'fast':1, u'off': 0}
            speed = speedSettings[value]
            if speed != 0:
                self.gamelayout.playscreen.displayHintTimer = speed
                self.gamelayout.playscreen.hintActivated = True
            else:
                self.gamelayout.playscreen.hintActivated = False
        if key == 'ai':
            self.gamelayout.playscreen.aiActivated = boolFromJS(value)

# To test the screen size you can use:
# kivy main.py -m screen:ipad3

if __name__ == '__main__':
    ShugouApp().run()
<commit_msg>Stop passing goToIntro to the gameplayscreen<commit_after>from kivy.app import App
from kivy.properties import *
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.button import Button
from kivy.clock import Clock
from kivy.core.window import Window
from kivy.core.audio import SoundLoader
from kivy.uix.screenmanager import Screen, ScreenManager, FadeTransition, SlideTransition, NoTransition
from kivy.uix.settings import SettingsWithSidebar
from kivy.uix.popup import Popup
from kivy.metrics import dp
from kivy import platform
from kivy.clock import Clock
from jsonConfig import settingsjson
import datetime
import pickle

from gameplay import GamePlayScreen
from PlayerNamePopup import PlayerNamePopup

class TutorialScreen(Popup):
    pass

class EndGameScreen(Screen):
    name_of_players = ListProperty(['','','',''])
    scores_of_players = ListProperty()
    screenManager = ObjectProperty()
    number_of_players = NumericProperty()
    game = ObjectProperty()

    def on_enter(self,*args):
        self.name_of_players = [x for y,x in sorted(zip(self.scores_of_players,self.name_of_players))][::-1]
        self.scores_of_players = sorted(self.scores_of_players)[::-1]

class PlayerSection(Button):
    myvalue = NumericProperty(4)

    def __init__(self, **kwargs):
        super(PlayerSection, self).__init__(**kwargs)
        self.size = Window.size[0] // 6, Window.size[1] // 6

class GameLayout(ScreenManager):
    ''' This class manages the movements between the various screen and the sound '''

    name_of_players = ListProperty(['Score', '', '', ''])
    number_of_players = NumericProperty(1)

    # True if there is a game going on
    active = BooleanProperty(False)
    soundActivated = BooleanProperty(False)

    directory = StringProperty()

    def __init__(self, **kwargs):
        super(GameLayout, self).__init__(**kwargs)
        self.playscreen = self.get_screen('game')
        self.sound = SoundLoader.load('set_song.wav')
        self.transition = FadeTransition()

    # screen play navigation
    def goToIntro(self, *arg):
        self.transition = NoTransition()
        self.current = 'intro'

    def goToGameScreen(self, *arg):
        self.transition = FadeTransition()
        self.current = 'game'

    # Dealing with Sound
    def on_soundActivated(self, obj, value):
        ''' Turn the intro song on or off '''
        if value:
            self.sound.loop = True
            self.sound.play()
        else:
            self.sound.stop()

    def pickleFile(self):
        return self.directory + "name_of_players.pkl"

    def on_name_of_players(self,value, obj):
        if platform == 'macosx' or platform == 'ios':
            with open(self.pickleFile(), "wb") as playersNamePickle:
                pickle.dump(list(self.name_of_players), playersNamePickle)

    def player_name_popup(self, numPlayers):
        '''called after selecting number of players'''
        self.number_of_players = numPlayers
        if self.number_of_players == 1:
            self.goToGameScreen()
            return
        tempNames = ['John', 'Sally', 'Sam', 'Joey']
        if platform == 'macosx' or platform == 'ios':
            try:
                names = pickle.load(open(self.pickleFile(), "rb"))
                for index, name in enumerate(names):
                    tempNames[index] = name
            except Exception as exception:
                print("Loading from Pickling went wrong, using default names")

        self.name_of_players = tempNames
        playername = PlayerNamePopup(self.name_of_players, numPlayers)
        playername.open()
        playername.bind(on_dismiss = self.goToGameScreen)

    def quit(self):
        ''' You are quiting the current game '''
        if self.active:
            #self.playscreen.scores_of_players = [0,0,0,0]
            self.playscreen.aiScore = 0
            self.goToIntro()
            self.playscreen.stopRotation()
            self.active = False

def boolFromJS(value):
    ''' JSON config returns '1' and '0' for True and False'''
    return True if value == '1' else False

class ShugouApp(App):
    active = BooleanProperty(False)

    def build(self):
        Clock.max_iteration = 50
        self.use_kivy_settings = False
        self.gamelayout = GameLayout()
        self.settings_cls = SettingsWithSidebar
        self.loadSettings()
        self.gamelayout.bind(active=self.changeActive)
        self.gamelayout.directory = self.whereToSave()
        return self.gamelayout

    def whereToSave(self):
        # Returns in which directory you can store files
        return self.get_application_config().rstrip("shugou.ini")

    def changeActive(self,instance,value):
        # This doesn't work.. crashes if the build_settings wasn't launched first
        #self.quitButton.disabled = not self.gamelayout.active
        pass

    def loadSettings(self):
        # Load the values already stored into the file
        self.gamelayout.soundActivated = boolFromJS(
            self.config.get('settings', 'sound'))
        speedSettings = {'slow':10, 'normal':5, 'fast':1, 'off': 0, 'True': 1}
        speed = speedSettings[self.config.get('settings', 'hint')]
        if speed != 0:
            self.gamelayout.playscreen.displayHintTimer = speed
            self.gamelayout.playscreen.hintActivated = True
        else:
            self.gamelayout.playscreen.hintActivated = False

        self.gamelayout.playscreen.aiActivated = boolFromJS(
            self.config.get('settings', 'ai'))

    def build_config(self, config):
        config.setdefaults('settings', {'sound': False,
                                        'ai': False,
                                        'hint': 'off'})

    def build_settings(self, settings):
        self.settings = settings
        self.settings.interface.menu.width = dp(100)
        settings.add_json_panel('Settings', self.config, data=settingsjson)
        settingsCloseButton = settings.interface.ids.menu.ids.button
        self.settingsCloseButton = settingsCloseButton
        self.settingsCloseButton.text = "Return"
        settingsCloseButton.on_press = self.leaveSettingsPanel
        settings.interface.ids.menu.add_widget(Button(text="Tutorial",
                                                      size_hint = (None, None),
                                                      x= settingsCloseButton.x,
                                                      y = settingsCloseButton.top + 10,
                                                      size = settingsCloseButton.size,
                                                      on_press= self.moveToTutorial))

        self.quitButton = Button(text="Quit",
                              background_color = [1,0,0,1],
                              size_hint = (None, None),
                              x= settingsCloseButton.x,
                              y = settingsCloseButton.top + settingsCloseButton.height + 20,
                              size = settingsCloseButton.size,
                              disabled = False,
                              on_press= self.quit)

        settings.interface.ids.menu.add_widget(self.quitButton)
        settings.on_close = self.quit

    def quit(self, *arg):
        self.gamelayout.quit()
        self.settingsCloseButton.trigger_action()

    def moveToTutorial(self, buttonInstance):
        tutorial = TutorialScreen(title="Tutorial")
        tutorial.open()

    def leaveSettingsPanel(self, *arg):
        ''' activated when you exit the setting panels'''
        self.gamelayout.playscreen.setUpHint()
        self.gamelayout.playscreen.setUpAI()

    def on_config_change(self, config, section, key, value):
        if key == 'sound':
            self.gamelayout.soundActivated = boolFromJS(value)
        if key == 'hint':
            speedSettings = {u'slow':10, u'normal':5, u'fast':1, u'off': 0}
            speed = speedSettings[value]
            if speed != 0:
                self.gamelayout.playscreen.displayHintTimer = speed
                self.gamelayout.playscreen.hintActivated = True
            else:
                self.gamelayout.playscreen.hintActivated = False
        if key == 'ai':
            self.gamelayout.playscreen.aiActivated = boolFromJS(value)

# To test the screen size you can use:
# kivy main.py -m screen:ipad3

if __name__ == '__main__':
    ShugouApp().run()
<|endoftext|>


<commit_before>import json
import logging
import urllib
import urllib2
import datetime
# import requests

import key

# for sending images
#from PIL import Image
#import multipart

# standard app engine imports
from google.appengine.api import urlfetch
from google.appengine.ext import ndb
import webapp2

# import MySQLdb
# see https://cloud.google.com/appengine/docs/python/cloud-sql/#Python_Connect_to_your_database

# TIME_FORMAT = '%Y-%m-%d %H:%M:%S'

BASE_URL = 'https://api.telegram.org/bot' + key.TOKEN + '/'

STATES = {
    -1: 'Initial',
    0: 'Started',
    20: 'PassengerAskedForLoc',
    21: 'PassengerBored',
    22: 'PassengerEngaged',
    30: 'DriverAskedForLoc',
    31: 'DriverBored',
    32: 'DriverEngaged',
};

# ================================

class Person(ndb.Model):
    name = ndb.StringProperty()
    last_mod = ndb.DateTimeProperty(auto_now=True)
    last_seen = ndb.DateTimeProperty()
    chat_id = ndb.IntegerProperty()
    state = ndb.IntegerProperty()
    last_type = ndb.StringProperty()
    location = ndb.StringProperty()

# ================================

def addPerson(chat_id, name):
    p = Person.get_or_insert(str(chat_id))
    p.name = name
    p.chat_id = chat_id
    p.state = -1
    p.put()
    return p

def setType(p, type):
    p.last_type = type
    p.put()

def setState(p, state):
    p.state = state
    p.put()

def setLocation(p, loc):
    p.location = loc
    p.put()

def getUsers():
    query = Person.query().order(-Person.last_mod)
    if query.get() is None:
        return "No users found"
    text = ""
    for p in query.iter():
        text = text + p.name + " (" + str(p.state) + ") " + get_date_string(p.last_mod) + "\n"
    return text

def get_date_string(date):
    newdate = date + datetime.timedelta(hours=2)
    time_day = str(newdate).split(" ")
    time = time_day[1].split(".")[0]
    day = time_day[0]
    return day + " " + time

def get_time_string(date):
    newdate = date + datetime.timedelta(hours=2)
    return str(newdate).split(" ")[1].split(".")[0]

def restart(person):
    tell(person.chat_id, "Press START if you want to restart.", kb=[['START']])
    setState(person, -1)

def putDriverOnHold(driver):
    tell(driver.chat_id, "No passanger needs a ride for the moment. Waiting...", kb=[['Abort']])
    setState(driver, 31)

def putPassengerOnHold(passenger):
    tell(passenger.chat_id, "Waiting for a driver!", kb=[['Abort']])
    setState(passenger, 21)

def check_available_drivers(passenger):
    passenger.last_seen = datetime.datetime.now()
    qry = Person.query(Person.location == passenger.location, Person.state.IN([31,32]))
    for d in qry:
        if (d.state==31):
            setState(d, 32)
            tell(d.chat_id,
                 "There is now somebody waiting for you there!", # + get_time_string(passenger.last_mod) + ")",
                kb=[['List Passengers', 'Gave the Ride!'],['Abort']], hideKb=False)
    return qry.get() is not None

def check_available_passenger(driver):
    driver.last_seen = datetime.datetime.now()
    qry = Person.query(Person.location == driver.location, Person.state.IN([21, 22]))
    for p in qry:
        if (p.state==21):
            setState(p, 22)
        tell(p.chat_id, "A driver coming: " + driver.name + " (" + get_time_string(driver.last_mod) + ")",
            kb=[['List Drivers', 'Got the Ride!'],['Abort']])
    return qry.get() is not None

def listDrivers(passenger):
    qry = Person.query(Person.location == passenger.location, Person.state==32) #.order(-Person.last_mod)
    if qry.get() is None:
        return "No drivers found in your location"
    else:
        text = ""
        for d in qry:
            text = text + d.name + " " + get_time_string(d.last_seen) + "\n"
        return text

def listAllDrivers():
    qry = Person.query().filter(Person.state.IN([31, 32])).order(-Person.last_mod)
    if qry.get() is None:
        return "No drivers found"
    else:
        text = ""
        for d in qry:
            text = text + d.name + " " + d.location + " (" + str(d.state) + ") " + get_time_string(d.last_seen) + "\n"
        return text

def listPassengers(driver):
    qry = Person.query(Person.location == driver.location, Person.state.IN([21, 22])) #.order(-Person.last_mod)
    if qry.get() is None:
        return "No passengers found in your location"
    else:
        text = ""
        for p in qry:
            text = text + p.name + " " + get_time_string(p.last_seen) + "\n"
        return text

def listAllPassengers():
    qry = Person.query().filter(Person.state.IN([21, 22])).order(-Person.last_mod)
    if qry.get() is None:
        return "No passangers found"
    else:
        text = ""
        for p in qry:
            text = text + p.name + " " + p.location + " (" + str(p.state) + ") " + get_time_string(p.last_seen) + "\n"
        return text

def removePassenger(p):
    restart(p)
    qry = Person.query().filter(Person.state.IN([21, 22]), Person.location==p.location)
    if qry.get() is None:
        # there are no more passengers in that location
        qry = Person.query().filter(Person.state == 32, Person.location==p.location)
        for d in qry:
            tell(d.chat_id, "Oops... all gone!")
            putDriverOnHold(d)

def removeDriver(d):
    restart(d)
    qry = Person.query().filter(Person.state==32, Person.location==d.location)
    if qry.get() is None:
        # there are no more drivers in that location
        qry = Person.query().filter(Person.state == 22, Person.location==d.location)
        for p in qry:
            tell(p.chat_id, "Oops... all gone!")
            putPassengerOnHold(p)

def tell(chat_id, msg, kb=None, hideKb=True):
    if kb:
        resp = urllib2.urlopen(BASE_URL + 'sendMessage', urllib.urlencode({
            'chat_id': chat_id,
            'text': msg.encode('utf-8'),
            'disable_web_page_preview': 'true',
            #'reply_to_message_id': str(message_id),
            'reply_markup': json.dumps({
                #'one_time_keyboard': True,
                'resize_keyboard': True,
                'keyboard': kb,  # [['Test1','Test2'],['Test3','Test8']]
            }),
        })).read()
    else:
        if hideKb:
            resp = urllib2.urlopen(BASE_URL + 'sendMessage', urllib.urlencode({
                'chat_id': str(chat_id),
                'text': msg.encode('utf-8'),
                #'disable_web_page_preview': 'true',
                #'reply_to_message_id': str(message_id),
                'reply_markup': json.dumps({'hide_keyboard': True}),
            })).read()
        else:
            resp = urllib2.urlopen(BASE_URL + 'sendMessage', urllib.urlencode({
                'chat_id': str(chat_id),
                'text': msg.encode('utf-8'),
                #'disable_web_page_preview': 'true',
                #'reply_to_message_id': str(message_id),
                #'reply_markup': json.dumps({'hide_keyboard': True}),
            })).read()

    logging.info('send response: ')
    logging.info(resp)

# ================================

class MeHandler(webapp2.RequestHandler):
    def get(self):
        urlfetch.set_default_fetch_deadline(60)
        self.response.write(json.dumps(json.load(urllib2.urlopen(BASE_URL + 'getMe'))))

class GetUpdatesHandler(webapp2.RequestHandler):
    def get(self):
        urlfetch.set_default_fetch_deadline(60)
        self.response.write(json.dumps(json.load(urllib2.urlopen(BASE_URL + 'getUpdates'))))

class SetWebhookHandler(webapp2.RequestHandler):
    def get(self):
        urlfetch.set_default_fetch_deadline(60)
        url = self.request.get('url')
        if url:
            self.response.write(
                json.dumps(json.load(urllib2.urlopen(BASE_URL + 'setWebhook', urllib.urlencode({'url': url})))))

class WebhookHandler(webapp2.RequestHandler):
    def post(self):
        urlfetch.set_default_fetch_deadline(60)
        body = json.loads(self.request.body)
        logging.info('request body:')
        logging.info(body)
        self.response.write(json.dumps(body))

        # update_id = body['update_id']
        message = body['message']
        message_id = message.get('message_id')
        # date = message.get('date')
        text = message.get('text')
        # fr = message.get('from')
        chat = message['chat']
        chat_id = chat['id']
        first_name = chat["first_name"]
        #user_id = chat["id"]
        name = str(first_name)

        if not text:
            logging.info('no text')
            return

        def reply(msg=None, kb=None, hideKb=True):
            tell(chat_id, msg, kb, hideKb)

        instructions = ('I\'m your Trento <-> Povo travelling assistant.\n' \
                        'You can write /start to get or offer a ride\n' \
                        'You can write /help to see this message again'.encode('utf-8'))

        p = ndb.Key(Person, str(chat_id)).get()
        if p is None:
            # new user
            tell(key.MASTER_CHAT_ID, msg = "New user: " + name)
            p = addPerson(chat_id, name)
            if text == '/help':
                reply(instructions)
            if text == '/start':
                reply("Hi " + name + "! Are you a driver or a passenger?", kb=[['Driver', 'Passenger'],['Abort']])
                setState(p,0)
            else:
                reply("Hi " + name + ", welcome!")
                reply(instructions)
        else:
            # known user
            if text=='/state':
              reply("You are in state " + str(p.state) + ": " + STATES[p.state], hideKb=False);
            elif p.state == -1:
                # INITIAL STATE
                if text == '/help':
                    reply(instructions)
                elif text in ['/start','START']:
                    reply("Hi " + name + "! Are you a driver or a passenger?", kb=[['Driver', 'Passenger'],['Abort']])
                    setState(p,0)
                elif text == '/users':
                    reply(getUsers(), hideKb=False)
                elif text == '/alldrivers':
                    reply(listAllDrivers(), hideKb=False)
                elif text == '/allpassengers':
                    reply(listAllPassengers(), hideKb=False)
                else:
                    reply('What command? I only understnad /help or /start.\n')
            elif p.state == 0:
                # AFTER TYPING START
                if text == 'Passenger':
                    setState(p, 20)
                    setType(p, text)
                    reply("Hi! I can try to help you to get a ride. Where are you?", kb=[['Povo', 'Trento'],['Abort']])
                elif text == 'Driver':
                    setState(p, 30)
                    setType(p, text)
                    reply("Hi! Glad you can give a ride. Where are you?", kb=[['Povo', 'Trento'],['Abort']])
                elif text == 'Abort':
                    reply("Passage aborted.")
                    restart(p);
                    # state = -1
                else: reply("Eh? I don't understand you. Are you a Driver or a Passenger?", hideKb=False)
            elif p.state == 20:
                # PASSANGERS, ASKED FOR LOCATION
                if text in ['Povo','Trento']:
                    setLocation(p, text)
                    if check_available_drivers(p):
                        reply("There is a driver coming!", kb=[['List Drivers', 'Got the Ride!'],['Abort']])
                        setState(p, 22)
                    else:
                        putPassengerOnHold(p)
                        # state = 21
                elif text == 'Abort':
                    reply("Passage aborted.")
                    restart(p);
                    # state = -1
                else: reply("Eh? I don't understand you. Trento or Povo?", hideKb=False)
            elif p.state == 21:
                # PASSENGERS WAITING FOR DRIVERS
                if text == 'Abort':
                    reply("Passage aborted.")
                    restart(p);
                    # state = -1
                else:
                    reply("Eh? If you want to Abort press the button!", kb=[['Abort']])
            elif p.state == 22:
                # PASSENGERS NOTIFIED THERE IS A DRIVER
                if text == 'Got the Ride!':
                    reply("Great! Have a good ride!")
                    removePassenger(p)
                    # state = -1
                elif text == 'List Drivers':
                    reply(listDrivers(p), hideKb=False)
                elif text == 'Abort':
                    reply("Passage aborted.")
                    removePassenger(p)
                    # state = -1
                else:
                    reply("Eh? I don't understand you. A driver is supposed to come, be patient!", hideKb=False)
            elif p.state == 30:
                # DRIVERS, ASKED FOR LOCATION
                if text in ['Povo','Trento']:
                    setLocation(p, text)
                    # CHECK AND NOTIFY PASSENGER WAIING IN THE SAME LOCATION
                    if check_available_passenger(p):
                        reply("There is someone waiting for you there!", kb=[['List Passengers', 'Gave the Ride!'],['Abort']])
                        setState(p, 32)
                    else:
                        putDriverOnHold(p);
                        # state = 31
                elif text == 'Abort':
                    reply("Passage aborted.")
                    restart(p);
                    # state = -1
                else: reply("Eh? Trento or Povo?", hideKb=False)
            elif p.state == 31:
                # DRIVERS WAITING FOR NEW PASSENGERS
                if text == 'Abort':
                    reply("Passage aborted.")
                    restart(p);
                    # state = -1
                else:
                    reply("Eh? I don't understand you.", hideKb=False)
            elif p.state == 32:
                # DRIVERS NOTIFIED THERE ARE PASSENGERS WAITING
                if text == 'Gave the Ride!':
                    reply("Great! Have a good ride!")
                    removeDriver(p)
                    # set state -1
                elif text == 'List Passengers':
                    reply(listPassengers(p), hideKb=False)
                elif text == 'Abort':
                    reply("Passage aborted..")
                    removeDriver(p)
                    # state = -1
                else:
                    reply("Eh? I don't understand you. (" + text + ")", hideKb=False)
            else:
                reply("Something is wrong with your state (" + str(p.state) + "). Contact the admin!")

app = webapp2.WSGIApplication([
    ('/me', MeHandler),
    ('/updates', GetUpdatesHandler),
    ('/set_webhook', SetWebhookHandler),
    ('/webhook', WebhookHandler),
], debug=True)
<commit_msg>test auto gae deploy<commit_after>import json
import logging
import urllib
import urllib2
import datetime
# import requests

import key

# for sending images
#from PIL import Image
#import multipart

# standard app engine imports
from google.appengine.api import urlfetch
from google.appengine.ext import ndb
import webapp2

# import MySQLdb
# see https://cloud.google.com/appengine/docs/python/cloud-sql/#Python_Connect_to_your_database

# TIME_FORMAT = '%Y-%m-%d %H:%M:%S'

BASE_URL = 'https://api.telegram.org/bot' + key.TOKEN + '/'

STATES = {
    -1: 'Initial',
    0: 'Started',
    20: 'PassengerAskedForLoc',
    21: 'PassengerBored',
    22: 'PassengerEngaged',
    30: 'DriverAskedForLoc',
    31: 'DriverBored',
    32: 'DriverEngaged',
};

# ================================

class Person(ndb.Model):
    name = ndb.StringProperty()
    last_mod = ndb.DateTimeProperty(auto_now=True)
    last_seen = ndb.DateTimeProperty()
    chat_id = ndb.IntegerProperty()
    state = ndb.IntegerProperty()
    last_type = ndb.StringProperty()
    location = ndb.StringProperty()

# ================================

def addPerson(chat_id, name):
    p = Person.get_or_insert(str(chat_id))
    p.name = name
    p.chat_id = chat_id
    p.state = -1
    p.put()
    return p

def setType(p, type):
    p.last_type = type
    p.put()

def setState(p, state):
    p.state = state
    p.put()

def setLocation(p, loc):
    p.location = loc
    p.put()

def getUsers():
    query = Person.query().order(-Person.last_mod)
    if query.get() is None:
        return "No users found"
    text = ""
    for p in query.iter():
        text = text + p.name + " (" + str(p.state) + ") " + get_date_string(p.last_mod) + "\n"
    return text

def get_date_string(date):
    newdate = date + datetime.timedelta(hours=2)
    time_day = str(newdate).split(" ")
    time = time_day[1].split(".")[0]
    day = time_day[0]
    return day + " " + time

def get_time_string(date):
    newdate = date + datetime.timedelta(hours=2)
    return str(newdate).split(" ")[1].split(".")[0]

def restart(person):
    tell(person.chat_id, "Press START if you want to restart.", kb=[['START']])
    setState(person, -1)

def putDriverOnHold(driver):
    tell(driver.chat_id, "No passanger needs a ride for the moment. Waiting...", kb=[['Abort']])
    setState(driver, 31)

def putPassengerOnHold(passenger):
    tell(passenger.chat_id, "Waiting for a driver!", kb=[['Abort']])
    setState(passenger, 21)

def check_available_drivers(passenger):
    passenger.last_seen = datetime.datetime.now()
    qry = Person.query(Person.location == passenger.location, Person.state.IN([31,32]))
    for d in qry:
        if (d.state==31):
            setState(d, 32)
            tell(d.chat_id,
                 "There is now somebody waiting for you there!", # + get_time_string(passenger.last_mod) + ")",
                kb=[['List Passengers', 'Gave the Ride!'],['Abort']], hideKb=False)
    return qry.get() is not None

def check_available_passenger(driver):
    driver.last_seen = datetime.datetime.now()
    qry = Person.query(Person.location == driver.location, Person.state.IN([21, 22]))
    for p in qry:
        if (p.state==21):
            setState(p, 22)
        tell(p.chat_id, "A driver coming: " + driver.name + " (" + get_time_string(driver.last_mod) + ")",
            kb=[['List Drivers', 'Got the Ride!'],['Abort']])
    return qry.get() is not None

def listDrivers(passenger):
    qry = Person.query(Person.location == passenger.location, Person.state==32) #.order(-Person.last_mod)
    if qry.get() is None:
        return "No drivers found in your location"
    else:
        text = ""
        for d in qry:
            text = text + d.name + " " + get_time_string(d.last_seen) + "\n"
        return text

def listAllDrivers():
    qry = Person.query().filter(Person.state.IN([31, 32])).order(-Person.last_mod)
    if qry.get() is None:
        return "No drivers found"
    else:
        text = ""
        for d in qry:
            text = text + d.name + " " + d.location + " (" + str(d.state) + ") " + get_time_string(d.last_seen) + "\n"
        return text

def listPassengers(driver):
    qry = Person.query(Person.location == driver.location, Person.state.IN([21, 22])) #.order(-Person.last_mod)
    if qry.get() is None:
        return "No passengers found in your location"
    else:
        text = ""
        for p in qry:
            text = text + p.name + " " + get_time_string(p.last_seen) + "\n"
        return text

def listAllPassengers():
    qry = Person.query().filter(Person.state.IN([21, 22])).order(-Person.last_mod)
    if qry.get() is None:
        return "No passangers found"
    else:
        text = ""
        for p in qry:
            text = text + p.name + " " + p.location + " (" + str(p.state) + ") " + get_time_string(p.last_seen) + "\n"
        return text

def removePassenger(p):
    restart(p)
    qry = Person.query().filter(Person.state.IN([21, 22]), Person.location==p.location)
    if qry.get() is None:
        # there are no more passengers in that location
        qry = Person.query().filter(Person.state == 32, Person.location==p.location)
        for d in qry:
            tell(d.chat_id, "Oops... all gone!")
            putDriverOnHold(d)

def removeDriver(d):
    restart(d)
    qry = Person.query().filter(Person.state==32, Person.location==d.location)
    if qry.get() is None:
        # there are no more drivers in that location
        qry = Person.query().filter(Person.state == 22, Person.location==d.location)
        for p in qry:
            tell(p.chat_id, "Oops... all gone!")
            putPassengerOnHold(p)

def tell(chat_id, msg, kb=None, hideKb=True):
    if kb:
        resp = urllib2.urlopen(BASE_URL + 'sendMessage', urllib.urlencode({
            'chat_id': chat_id,
            'text': msg.encode('utf-8'),
            'disable_web_page_preview': 'true',
            #'reply_to_message_id': str(message_id),
            'reply_markup': json.dumps({
                #'one_time_keyboard': True,
                'resize_keyboard': True,
                'keyboard': kb,  # [['Test1','Test2'],['Test3','Test8']]
            }),
        })).read()
    else:
        if hideKb:
            resp = urllib2.urlopen(BASE_URL + 'sendMessage', urllib.urlencode({
                'chat_id': str(chat_id),
                'text': msg.encode('utf-8'),
                #'disable_web_page_preview': 'true',
                #'reply_to_message_id': str(message_id),
                'reply_markup': json.dumps({'hide_keyboard': True}),
            })).read()
        else:
            resp = urllib2.urlopen(BASE_URL + 'sendMessage', urllib.urlencode({
                'chat_id': str(chat_id),
                'text': msg.encode('utf-8'),
                #'disable_web_page_preview': 'true',
                #'reply_to_message_id': str(message_id),
                #'reply_markup': json.dumps({'hide_keyboard': True}),
            })).read()

    logging.info('send response: ')
    logging.info(resp)

# ================================

class MeHandler(webapp2.RequestHandler):
    def get(self):
        urlfetch.set_default_fetch_deadline(60)
        self.response.write(json.dumps(json.load(urllib2.urlopen(BASE_URL + 'getMe'))))

class GetUpdatesHandler(webapp2.RequestHandler):
    def get(self):
        urlfetch.set_default_fetch_deadline(60)
        self.response.write(json.dumps(json.load(urllib2.urlopen(BASE_URL + 'getUpdates'))))

class SetWebhookHandler(webapp2.RequestHandler):
    def get(self):
        urlfetch.set_default_fetch_deadline(60)
        url = self.request.get('url')
        if url:
            self.response.write(
                json.dumps(json.load(urllib2.urlopen(BASE_URL + 'setWebhook', urllib.urlencode({'url': url})))))

class WebhookHandler(webapp2.RequestHandler):
    def post(self):
        urlfetch.set_default_fetch_deadline(60)
        body = json.loads(self.request.body)
        logging.info('request body:')
        logging.info(body)
        self.response.write(json.dumps(body))

        # update_id = body['update_id']
        message = body['message']
        message_id = message.get('message_id')
        # date = message.get('date')
        text = message.get('text')
        # fr = message.get('from')
        chat = message['chat']
        chat_id = chat['id']
        first_name = chat["first_name"]
        #user_id = chat["id"]
        name = str(first_name)

        if not text:
            logging.info('no text')
            return

        def reply(msg=None, kb=None, hideKb=True):
            tell(chat_id, msg, kb, hideKb)

        instructions = ('I\'m your Trento <---> Povo travelling assistant.\n' \
                        'You can write /start to get or offer a ride\n' \
                        'You can write /help to see this message again'.encode('utf-8'))

        p = ndb.Key(Person, str(chat_id)).get()
        if p is None:
            # new user
            tell(key.MASTER_CHAT_ID, msg = "New user: " + name)
            p = addPerson(chat_id, name)
            if text == '/help':
                reply(instructions)
            if text == '/start':
                reply("Hi " + name + "! Are you a driver or a passenger?", kb=[['Driver', 'Passenger'],['Abort']])
                setState(p,0)
            else:
                reply("Hi " + name + ", welcome!")
                reply(instructions)
        else:
            # known user
            if text=='/state':
              reply("You are in state " + str(p.state) + ": " + STATES[p.state], hideKb=False);
            elif p.state == -1:
                # INITIAL STATE
                if text == '/help':
                    reply(instructions)
                elif text in ['/start','START']:
                    reply("Hi " + name + "! Are you a driver or a passenger?", kb=[['Driver', 'Passenger'],['Abort']])
                    setState(p,0)
                elif text == '/users':
                    reply(getUsers(), hideKb=False)
                elif text == '/alldrivers':
                    reply(listAllDrivers(), hideKb=False)
                elif text == '/allpassengers':
                    reply(listAllPassengers(), hideKb=False)
                else:
                    reply('What command? I only understnad /help or /start.\n')
            elif p.state == 0:
                # AFTER TYPING START
                if text == 'Passenger':
                    setState(p, 20)
                    setType(p, text)
                    reply("Hi! I can try to help you to get a ride. Where are you?", kb=[['Povo', 'Trento'],['Abort']])
                elif text == 'Driver':
                    setState(p, 30)
                    setType(p, text)
                    reply("Hi! Glad you can give a ride. Where are you?", kb=[['Povo', 'Trento'],['Abort']])
                elif text == 'Abort':
                    reply("Passage aborted.")
                    restart(p);
                    # state = -1
                else: reply("Eh? I don't understand you. Are you a Driver or a Passenger?", hideKb=False)
            elif p.state == 20:
                # PASSANGERS, ASKED FOR LOCATION
                if text in ['Povo','Trento']:
                    setLocation(p, text)
                    if check_available_drivers(p):
                        reply("There is a driver coming!", kb=[['List Drivers', 'Got the Ride!'],['Abort']])
                        setState(p, 22)
                    else:
                        putPassengerOnHold(p)
                        # state = 21
                elif text == 'Abort':
                    reply("Passage aborted.")
                    restart(p);
                    # state = -1
                else: reply("Eh? I don't understand you. Trento or Povo?", hideKb=False)
            elif p.state == 21:
                # PASSENGERS WAITING FOR DRIVERS
                if text == 'Abort':
                    reply("Passage aborted.")
                    restart(p);
                    # state = -1
                else:
                    reply("Eh? If you want to Abort press the button!", kb=[['Abort']])
            elif p.state == 22:
                # PASSENGERS NOTIFIED THERE IS A DRIVER
                if text == 'Got the Ride!':
                    reply("Great! Have a good ride!")
                    removePassenger(p)
                    # state = -1
                elif text == 'List Drivers':
                    reply(listDrivers(p), hideKb=False)
                elif text == 'Abort':
                    reply("Passage aborted.")
                    removePassenger(p)
                    # state = -1
                else:
                    reply("Eh? I don't understand you. A driver is supposed to come, be patient!", hideKb=False)
            elif p.state == 30:
                # DRIVERS, ASKED FOR LOCATION
                if text in ['Povo','Trento']:
                    setLocation(p, text)
                    # CHECK AND NOTIFY PASSENGER WAIING IN THE SAME LOCATION
                    if check_available_passenger(p):
                        reply("There is someone waiting for you there!", kb=[['List Passengers', 'Gave the Ride!'],['Abort']])
                        setState(p, 32)
                    else:
                        putDriverOnHold(p);
                        # state = 31
                elif text == 'Abort':
                    reply("Passage aborted.")
                    restart(p);
                    # state = -1
                else: reply("Eh? Trento or Povo?", hideKb=False)
            elif p.state == 31:
                # DRIVERS WAITING FOR NEW PASSENGERS
                if text == 'Abort':
                    reply("Passage aborted.")
                    restart(p);
                    # state = -1
                else:
                    reply("Eh? I don't understand you.", hideKb=False)
            elif p.state == 32:
                # DRIVERS NOTIFIED THERE ARE PASSENGERS WAITING
                if text == 'Gave the Ride!':
                    reply("Great! Have a good ride!")
                    removeDriver(p)
                    # set state -1
                elif text == 'List Passengers':
                    reply(listPassengers(p), hideKb=False)
                elif text == 'Abort':
                    reply("Passage aborted..")
                    removeDriver(p)
                    # state = -1
                else:
                    reply("Eh? I don't understand you. (" + text + ")", hideKb=False)
            else:
                reply("Something is wrong with your state (" + str(p.state) + "). Contact the admin!")

app = webapp2.WSGIApplication([
    ('/me', MeHandler),
    ('/updates', GetUpdatesHandler),
    ('/set_webhook', SetWebhookHandler),
    ('/webhook', WebhookHandler),
], debug=True)
<|endoftext|>


<commit_before>"""
STIMULUS GENERATOR

Created on Tue Sep  5 10:42:19 2017

@author: Emerson

Class with built-in methods for generating commonly used stimuli and writing
them to ATF files for use with AxonInstruments hardware.

Example usage:

    # Initialize the class and simulate a synaptic current.
    s = Stim('Slow EPSC')
    s.generate_PS(duration = 200, ampli = 10, tau_rise = 1.5, tau_decay = 15)

    # Display some information about the generated waveform.
    print(s)
    s.plot()

    # Create a set of synaptic-like currents of increasing amplitude.
    s.set_replicates(5)
    s.command *= np.arange(1, 6)
    s.plot()

    # Write the stimulus to an ATF file.
    s.write_ATF()
"""

#%% IMPORT PREREQUISITE MODULES

import numpy as np
import numba as nb
import matplotlib.pyplot as plt

#%% DEFINE MAIN STIM CLASS

class Stim(object):

    """
    Class with built-in methods for generating commonly used stimuli and writing them to ATF files for use with AxonInstruments hardware.

    Attributes:

        label           -- string descriptor of the class instance
        stim_type       -- string descriptor of the type of stimulus.
        dt              -- size of the time step in ms.
        command         -- 2D array containing stimuli; time across rows, sweeps across cols.
        time            -- time support vector.

    Methods:

        generate_PS         -- generate a synaptic current/potential-like waveform.
        generate_OU         -- generate Ornstein-Uhlenbeck noise.
        set_replicates      -- set the number of replicates of the stimulus.
        plot                -- plot the stimulus.
        write_ATF           -- write the stimulus to an ATF file.

    Example usage:

        # Initialize the class and simulate a synaptic current.
        s = Stim('Slow EPSC')
        s.generate_PS(duration = 200, ampli = 10, tau_rise = 1.5, tau_decay = 15)

        # Display some information about the generated waveform.
        print(s)
        s.plot()

        # Create a set of synaptic-like currents of increasing amplitude.
        s.set_replicates(5)
        s.command *= np.arange(1, 6)
        s.plot()

        # Write the stimulus to an ATF file.
        s.write_ATF()
    """

    ### MAGIC METHODS

    # Initialize class instance.
    def __init__(self, label, dt = 0.1):

        self.label      = label
        self.stim_type  = 'Empty'

        self.dt         = dt        # Sampling interval in ms.

        self.command    = None      # Attribute to hold the command (only current is currently supported).
        self.time       = None      # Attribute to hold a time support vector.

    # Method for unambiguous representation of Stim instance.
    def __repr__(self):

        if not self.time is None:
            time_range  = '[' + str(self.time[0]) + ', ' + str(self.time[-1]) + ']'
            command_str   = np.array2string(self.command)
        else:
            time_range  = str(self.time)
            command_str   = str(self.command)

        return 'Stim object\n\nLabel: ' + self.label + '\nStim type: ' + self.stim_type + '\nTime range (ms): ' + time_range + '\nTime step (ms):' + str(self.dt) + '\nCommand:\n' + command_str

    # Pretty print self.command and some important details.
    # (Called by print().)
    def __str__(self):

        header = self.stim_type + ' Stim object'

        # Include more details about the object if it isn't empty.
        if not self.command is None:
            header += ' with ' + str( self.command.shape[1] ) + ' sweeps of ' + str( ( self.time[-1] + self.dt ) / 1000 ) + 's each.\n\n'
            content = np.array2string(self.command)

        else:
            content = ''

        return str(self.label) + '\n\n' + header + content

    ### MAIN METHODS

    # Generate a synaptic current-like waveform.
    def generate_PS(self, duration, ampli, tau_rise, tau_decay):

        """
        Generate a post-synaptic potential/current-like waveform.

        Note that the rise and decay time constants are only good approximations of fitted rise/decay taus (which are more experimentally relevant) if the provided values are separated by at least approx. half an order of magnitude.

        Inputs:
            duration        -- length of the simulated waveform in ms ^ -1.
            ampli           -- peak height of the waveform.
            tau_rise        -- time constant of the rising phase of the waveform in ms ^ -1.
            tau_decay       -- time constant of the falling phase of the waveform in ms ^ -1.
        """

        # Initialize time support vector.
        offset = 500
        self.time = np.arange(0, duration, self.dt)

        # Generate waveform based on time constants then normalize amplitude.
        waveform = np.abs( np.exp( -self.time / tau_rise ) - np.exp( -self.time / tau_decay ) )
        waveform /= np.max(waveform)
        waveform *= ampli

        # Convert waveform into a column vector.
        waveform = np.concatenate( ( np.zeros( (int( offset / self.dt)) ), waveform ), axis = 0 )
        waveform = waveform[np.newaxis].T

        # Assign output.
        self.time = np.arange(0, duration + offset, self.dt)
        self.command    = waveform
        self.stim_type  = "Post-synaptic current-like"

    # Realize OU noise and assign to self.command. (Wrapper for _gen_OU_internal.)
    def generate_OU(self, duration, I0, tau, sigma0, dsigma, sin_per, bias_factor, bias_point):

        """
        Realize Ornstein-Uhlenbeck noise.

        Parameters are provided to allow the noise SD to vary sinusoidally over time.

        sigma[t] = sigma0 * ( 1 + dsigma * sin(2pi * sin_freq)[t] )

        Additionally, the noise distribution may be skewed according to an exponential function. Positive values of bias_factor produce an upwards skew, and zero eliminates skew.

        Inputs:
            duration        -- duration of noise to realize in ms.
            I0              -- mean value of the noise.
            tau             -- noise time constant in ms ^ -1.
            sigma0          -- mean SD of the noise.
            dsigma          -- fractional permutation of noise SD.
            sin_per         -- period of the sinusoidal SD permutation in ms.
            bias_factor     -- slope of the upward bias function.
            bias_point      -- point at which the value of the bias function is 1.
        """

        # Initialize support vectors.
        self.time       = np.arange(0, duration, self.dt)
        self.command    = np.zeros(self.time.shape)
        S               = sigma0 * ( 1 + dsigma * np.sin( (2 * np.pi / sin_per ) * self.time))
        rands           = np.random.standard_normal( len(self.time) )

        # Perform type conversions for vectors.
        self.time.dtype         = np.float64
        self.command.dtype      = np.float64
        S.dtype                 = np.float64
        rands.dtype             = np.float64

        # Perform type conversions for constants.
        self.dt                 = np.float64( self.dt )
        I0                      = np.float64( I0 )
        tau                     = np.float64( tau )
        bias_factor             = np.float64( bias_factor )
        bias_point              = np.float64( bias_point )

        # Realize noise using nb.jit-accelerated function.
        noise = self._gen_OU_internal(self.time,
                                      rands,
                                      self.dt,
                                      I0,
                                      tau,
                                      S,
                                      bias_factor,
                                      bias_point)

        # Convert noise to a column vector.
        noise = noise[np.newaxis].T

        # Assign output.
        self.command    = noise
        self.stim_type  = 'Ornstein-Uhlenbeck noise'

    # Generate sinusoidal input
    def generate_sin(self, duration, I0, ampli, period):

        """
        Generate a sine wave with time-dependent amplitude and/or period.

        Inputs:
            duration        -- duration of the wave in ms.
            I0              -- offset of the wave.
            ampli           -- amplitude of the wave.
            period          -- period of the wave in ms.

        Amplitude and/or period can be time-varied by passing one-dimensional vectors of length duration/dt instead of constants.
        """

        # Initialize time support vector.
        self.time = np.arange(0, duration, self.dt)

        # Convert ampli to a vector if need be;
        # otherwise check that it's the right shape.
        try:
            tmp = iter(ampli); del tmp
            assert len(ampli) == len(self.time)

        except TypeError:
            ampli = np.array( [ampli] * len(self.time) )

        except AssertionError:
            raise ValueError('len of ampli must correspond to duration.')

        # Do the same with period.
        try:
            tmp = iter(period); del tmp
            assert len(period) == len(self.time)

        except TypeError:
            period = np.array( [period] * len(self.time) )

        except AssertionError:
            raise ValueError('len of period must correspond to duration.')

        # Calculate the sine wave over time.
        sinewave = I0 + ampli * np.sin( ( 2 * np.pi / period ) * self.time )

        # Convert sine wave to column vector.
        sinewave = sinewave[np.newaxis].T

        # Assign output.
        self.command    = sinewave
        self.stim_type  = 'Sine wave'

    # Set number of replicates of the command array.
    def set_replicates(self, reps):

        """
        Set number of replicates of the existing command array.
        """

        # Check that command has been initialized.
        try:
            assert not ( self.command   is None )
        except AssertionError:
            raise RuntimeError('No command array to replicate!')

        # Create replicates by tiling.
        self.command = np.tile(self.command, (1, reps))

    # Plot command, time, and additional data.
    def plot(self, **data):

        """
        Plot command (and any additional data) over time.

        Produces a plot of self.command over self.time as its primary output.

        Additional data of interest may be plotted as supplementary plots by passing them to the function as named arguments each containing a numerical vector of the same length as self.command.
        """

        d_keys  = data.keys()
        l_dk    = len(d_keys)

        plt.figure(figsize = (9, 3 + 3 * l_dk))
        plt.suptitle( str(self.label) )

        # Plot generated noise over time.
        plt.subplot(1 + l_dk, 1, 1)
        plt.title('Generated stimulus')
        plt.xlabel('Time (ms)')
        plt.ylabel('Command')

        plt.plot(self.time, self.command, '-k', linewidth = 0.5)

        # Add plots from data passed as named arguments.
        i = 2
        for key in d_keys:

            plt.subplot(1 + l_dk, 1, i)
            plt.title( key )
            plt.xlabel('Time (ms)')

            plt.plot(self.time, data[ key ], '-k', linewidth = 0.5)

            i += 1

        # Final formatting and show plot.
        plt.tight_layout(rect = (0, 0, 1, 0.95))
        plt.show()

    # Write command and time to an ATF file.
    def write_ATF(self):

        """
        Write command and time to an ATF file in the current working directory.
        """

        # Check whether there is any data to write.
        try:
            assert not ( self.command   is None )
            assert not ( self.time      is None )
        except AssertionError:
            raise RuntimeError('Command and time must both exist!')

        fname = self.label + '.ATF'
        header = ''.join(['ATF1.0\n1\t{0}\nType=1\nTime (ms)\t'.format(self.command.shape[1]),
                          *['Current (pA)\t' for sweep in range(self.command.shape[1])], '\n'])

        # Convert numeric arrays to strings.
        str_command     = self.command.astype( np.unicode_ )
        str_time        = self.time.astype( np.unicode_ )

        # Initialize list to hold arrays.
        contentl = []

        # Tab-delimit data one row (i.e., time step) at a time.
        for t in range( len(str_time) ):

            tmp = str_time[t] + '\t' + '\t'.join( str_command[t, :] )
            contentl.append(tmp)

        # Turn the content list into one long string.
        content = '\n'.join(contentl)

        # Write the header and content strings to the file.
        with open(fname, 'w') as f:

            f.write(header)
            f.write(content)
            f.close()

    ### HIDDEN METHODS

    # Fast internal method to realize OU noise. (Called by generate_OU.)
    @staticmethod
    @nb.jit(nb.float64[:](nb.float64[:], nb.float64[:], nb.float64, nb.float64, nb.float64, nb.float64[:], nb.float64, nb.float64), nopython = True)
    def _gen_OU_internal(T, rands, dt, I0, tau, sigma, bias_factor, bias_point):

        I       = np.zeros(T.shape, dtype = np.float64)
        I[0]    = I0

        for t in range(1, len(T)):

            adaptive_term = dt * ( ( I0 - I[t - 1] ) + bias_factor * np.exp(-(I[t - 1] - bias_point) / 5) )/ tau
            random_term = np.sqrt( 2 * dt * sigma[t]**2 / tau ) * rands[t]

            I[t] = I[t - 1] + adaptive_term + random_term

        return I<commit_msg>Replace references to 'current' with 'command'.<commit_after>"""
STIMULUS GENERATOR

Created on Tue Sep  5 10:42:19 2017

@author: Emerson

Class with built-in methods for generating commonly used stimuli and writing
them to ATF files for use with AxonInstruments hardware.

Example usage:

    # Initialize the class and simulate a synaptic current.
    s = Stim('Slow EPSC')
    s.generate_PS(duration = 200, ampli = 10, tau_rise = 1.5, tau_decay = 15)

    # Display some information about the generated waveform.
    print(s)
    s.plot()

    # Create a set of synaptic-like currents of increasing amplitude.
    s.set_replicates(5)
    s.command *= np.arange(1, 6)
    s.plot()

    # Write the stimulus to an ATF file.
    s.write_ATF()
"""

#%% IMPORT PREREQUISITE MODULES

import numpy as np
import numba as nb
import matplotlib.pyplot as plt

#%% DEFINE MAIN STIM CLASS

class Stim(object):

    """
    Class with built-in methods for generating commonly used stimuli and writing them to ATF files for use with AxonInstruments hardware.

    Attributes:

        label           -- string descriptor of the class instance
        stim_type       -- string descriptor of the type of stimulus.
        dt              -- size of the time step in ms.
        command         -- 2D array containing stimuli; time across rows, sweeps across cols.
        time            -- time support vector.

    Methods:

        generate_PS         -- generate a synaptic current/potential-like waveform.
        generate_OU         -- generate Ornstein-Uhlenbeck noise.
        set_replicates      -- set the number of replicates of the stimulus.
        plot                -- plot the stimulus.
        write_ATF           -- write the stimulus to an ATF file.

    Example usage:

        # Initialize the class and simulate a synaptic current.
        s = Stim('Slow EPSC')
        s.generate_PS(duration = 200, ampli = 10, tau_rise = 1.5, tau_decay = 15)

        # Display some information about the generated waveform.
        print(s)
        s.plot()

        # Create a set of synaptic-like currents of increasing amplitude.
        s.set_replicates(5)
        s.command *= np.arange(1, 6)
        s.plot()

        # Write the stimulus to an ATF file.
        s.write_ATF()
    """

    ### MAGIC METHODS

    # Initialize class instance.
    def __init__(self, label, dt = 0.1):

        self.label      = label
        self.stim_type  = 'Empty'

        self.dt         = dt        # Sampling interval in ms.

        self.command    = None      # Attribute to hold the command (only current is currently supported).
        self.time       = None      # Attribute to hold a time support vector.

    # Method for unambiguous representation of Stim instance.
    def __repr__(self):

        if not self.time is None:
            time_range  = '[' + str(self.time[0]) + ', ' + str(self.time[-1]) + ']'
            command_str   = np.array2string(self.command)
        else:
            time_range  = str(self.time)
            command_str   = str(self.command)

        return 'Stim object\n\nLabel: ' + self.label + '\nStim type: ' + self.stim_type + '\nTime range (ms): ' + time_range + '\nTime step (ms):' + str(self.dt) + '\nCommand:\n' + command_str

    # Pretty print self.command and some important details.
    # (Called by print().)
    def __str__(self):

        header = self.stim_type + ' Stim object'

        # Include more details about the object if it isn't empty.
        if not self.command is None:
            header += ' with ' + str( self.command.shape[1] ) + ' sweeps of ' + str( ( self.time[-1] + self.dt ) / 1000 ) + 's each.\n\n'
            content = np.array2string(self.command)

        else:
            content = ''

        return str(self.label) + '\n\n' + header + content

    ### MAIN METHODS

    # Generate a synaptic current-like waveform.
    def generate_PS(self, duration, ampli, tau_rise, tau_decay):

        """
        Generate a post-synaptic potential/current-like waveform.

        Note that the rise and decay time constants are only good approximations of fitted rise/decay taus (which are more experimentally relevant) if the provided values are separated by at least approx. half an order of magnitude.

        Inputs:
            duration        -- length of the simulated waveform in ms ^ -1.
            ampli           -- peak height of the waveform.
            tau_rise        -- time constant of the rising phase of the waveform in ms ^ -1.
            tau_decay       -- time constant of the falling phase of the waveform in ms ^ -1.
        """

        # Initialize time support vector.
        offset = 500
        self.time = np.arange(0, duration, self.dt)

        # Generate waveform based on time constants then normalize amplitude.
        waveform = np.abs( np.exp( -self.time / tau_rise ) - np.exp( -self.time / tau_decay ) )
        waveform /= np.max(waveform)
        waveform *= ampli

        # Convert waveform into a column vector.
        waveform = np.concatenate( ( np.zeros( (int( offset / self.dt)) ), waveform ), axis = 0 )
        waveform = waveform[np.newaxis].T

        # Assign output.
        self.time = np.arange(0, duration + offset, self.dt)
        self.command    = waveform
        self.stim_type  = "Post-synaptic current-like"

    # Realize OU noise and assign to self.command. (Wrapper for _gen_OU_internal.)
    def generate_OU(self, duration, I0, tau, sigma0, dsigma, sin_per, bias_factor, bias_point):

        """
        Realize Ornstein-Uhlenbeck noise.

        Parameters are provided to allow the noise SD to vary sinusoidally over time.

        sigma[t] = sigma0 * ( 1 + dsigma * sin(2pi * sin_freq)[t] )

        Additionally, the noise distribution may be skewed according to an exponential function. Positive values of bias_factor produce an upwards skew, and zero eliminates skew.

        Inputs:
            duration        -- duration of noise to realize in ms.
            I0              -- mean value of the noise.
            tau             -- noise time constant in ms ^ -1.
            sigma0          -- mean SD of the noise.
            dsigma          -- fractional permutation of noise SD.
            sin_per         -- period of the sinusoidal SD permutation in ms.
            bias_factor     -- slope of the upward bias function.
            bias_point      -- point at which the value of the bias function is 1.
        """

        # Initialize support vectors.
        self.time       = np.arange(0, duration, self.dt)
        self.command    = np.zeros(self.time.shape)
        S               = sigma0 * ( 1 + dsigma * np.sin( (2 * np.pi / sin_per ) * self.time))
        rands           = np.random.standard_normal( len(self.time) )

        # Perform type conversions for vectors.
        self.time.dtype         = np.float64
        self.command.dtype      = np.float64
        S.dtype                 = np.float64
        rands.dtype             = np.float64

        # Perform type conversions for constants.
        self.dt                 = np.float64( self.dt )
        I0                      = np.float64( I0 )
        tau                     = np.float64( tau )
        bias_factor             = np.float64( bias_factor )
        bias_point              = np.float64( bias_point )

        # Realize noise using nb.jit-accelerated function.
        noise = self._gen_OU_internal(self.time,
                                      rands,
                                      self.dt,
                                      I0,
                                      tau,
                                      S,
                                      bias_factor,
                                      bias_point)

        # Convert noise to a column vector.
        noise = noise[np.newaxis].T

        # Assign output.
        self.command    = noise
        self.stim_type  = 'Ornstein-Uhlenbeck noise'

    # Generate sinusoidal input
    def generate_sin(self, duration, I0, ampli, period):

        """
        Generate a sine wave with time-dependent amplitude and/or period.

        Inputs:
            duration        -- duration of the wave in ms.
            I0              -- offset of the wave.
            ampli           -- amplitude of the wave.
            period          -- period of the wave in ms.

        Amplitude and/or period can be time-varied by passing one-dimensional vectors of length duration/dt instead of constants.
        """

        # Initialize time support vector.
        self.time = np.arange(0, duration, self.dt)

        # Convert ampli to a vector if need be;
        # otherwise check that it's the right shape.
        try:
            tmp = iter(ampli); del tmp
            assert len(ampli) == len(self.time)

        except TypeError:
            ampli = np.array( [ampli] * len(self.time) )

        except AssertionError:
            raise ValueError('len of ampli must correspond to duration.')

        # Do the same with period.
        try:
            tmp = iter(period); del tmp
            assert len(period) == len(self.time)

        except TypeError:
            period = np.array( [period] * len(self.time) )

        except AssertionError:
            raise ValueError('len of period must correspond to duration.')

        # Calculate the sine wave over time.
        sinewave = I0 + ampli * np.sin( ( 2 * np.pi / period ) * self.time )

        # Convert sine wave to column vector.
        sinewave = sinewave[np.newaxis].T

        # Assign output.
        self.command    = sinewave
        self.stim_type  = 'Sine wave'

    # Set number of replicates of the command array.
    def set_replicates(self, reps):

        """
        Set number of replicates of the existing command array.
        """

        # Check that command has been initialized.
        try:
            assert not ( self.command   is None )
        except AssertionError:
            raise RuntimeError('No command array to replicate!')

        # Create replicates by tiling.
        self.command = np.tile(self.command, (1, reps))

    # Plot command, time, and additional data.
    def plot(self, **data):

        """
        Plot command (and any additional data) over time.

        Produces a plot of self.command over self.time as its primary output.

        Additional data of interest may be plotted as supplementary plots by passing them to the function as named arguments each containing a numerical vector of the same length as self.command.
        """

        d_keys  = data.keys()
        l_dk    = len(d_keys)

        plt.figure(figsize = (9, 3 + 3 * l_dk))
        plt.suptitle( str(self.label) )

        # Plot generated noise over time.
        plt.subplot(1 + l_dk, 1, 1)
        plt.title('Generated stimulus')
        plt.xlabel('Time (ms)')
        plt.ylabel('Command')

        plt.plot(self.time, self.command, '-k', linewidth = 0.5)

        # Add plots from data passed as named arguments.
        i = 2
        for key in d_keys:

            plt.subplot(1 + l_dk, 1, i)
            plt.title( key )
            plt.xlabel('Time (ms)')

            plt.plot(self.time, data[ key ], '-k', linewidth = 0.5)

            i += 1

        # Final formatting and show plot.
        plt.tight_layout(rect = (0, 0, 1, 0.95))
        plt.show()

    # Write command and time to an ATF file.
    def write_ATF(self):

        """
        Write command and time to an ATF file in the current working directory.
        """

        # Check whether there is any data to write.
        try:
            assert not ( self.command   is None )
            assert not ( self.time      is None )
        except AssertionError:
            raise RuntimeError('Command and time must both exist!')

        fname = self.label + '.ATF'
        header = ''.join(['ATF1.0\n1\t{0}\nType=1\nTime (ms)\t'.format(self.command.shape[1]),
                          *['Command (AU)\t' for sweep in range(self.command.shape[1])], '\n'])

        # Convert numeric arrays to strings.
        str_command     = self.command.astype( np.unicode_ )
        str_time        = self.time.astype( np.unicode_ )

        # Initialize list to hold arrays.
        contentl = []

        # Tab-delimit data one row (i.e., time step) at a time.
        for t in range( len(str_time) ):

            tmp = str_time[t] + '\t' + '\t'.join( str_command[t, :] )
            contentl.append(tmp)

        # Turn the content list into one long string.
        content = '\n'.join(contentl)

        # Write the header and content strings to the file.
        with open(fname, 'w') as f:

            f.write(header)
            f.write(content)
            f.close()

    ### HIDDEN METHODS

    # Fast internal method to realize OU noise. (Called by generate_OU.)
    @staticmethod
    @nb.jit(nb.float64[:](nb.float64[:], nb.float64[:], nb.float64, nb.float64, nb.float64, nb.float64[:], nb.float64, nb.float64), nopython = True)
    def _gen_OU_internal(T, rands, dt, I0, tau, sigma, bias_factor, bias_point):

        I       = np.zeros(T.shape, dtype = np.float64)
        I[0]    = I0

        for t in range(1, len(T)):

            adaptive_term = dt * ( ( I0 - I[t - 1] ) + bias_factor * np.exp(-(I[t - 1] - bias_point) / 5) )/ tau
            random_term = np.sqrt( 2 * dt * sigma[t]**2 / tau ) * rands[t]

            I[t] = I[t - 1] + adaptive_term + random_term

        return I<|endoftext|>


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

# This is a python script that emulates a terminal session and runs
# commands from a supplied markdown file..

import optparse
import os
import sys
import time

from cli import Ui
from web import WebUi
import config
from demo import Demo
from environment import Environment

def get_bash_script(script_dir, is_simulation = True, is_automated=False, is_testing=False):
    """
    Reads a README.md file in the indicated directoy and builds an
    executable bash script from the commands contained within.
    """
    if not script_dir.endswith('/'):
        script_dir = script_dir + "/"

    script = ""
    env = Environment(script_dir, False).get()
    for key, value in env.items():
        script += key + "='" + value + "'\n"

    filename = env.get_script_file_name(script_dir)
    in_code_block = False
    in_results_section = False
    lines = list(open(script_dir + filename))
    for line in lines:
        if line.startswith("Results:"):
            # Entering results section
            in_results_section = True
        elif line.startswith("```") and not in_code_block:
            # Entering a code block, if in_results_section = True then it's a results block
            in_code_block = True
        elif line.startswith("```") and in_code_block:
            # Finishing code block
            in_results_section = False
            in_code_block = False
        elif in_code_block and not in_results_section:
            # Executable line
            script += line
        elif line.startswith("#") and not in_code_block and not in_results_section and not is_automated:
            # Heading in descriptive text
            script += "\n"
    return script

def main():
    """SimDem CLI interpreter"""

    commands = config.modes
    command_string = ""
    for command in commands:
        command_string = command_string + command + "|"
    command_string = command_string[0:len(command_string)-1]

    p = optparse.OptionParser("%prog [" + command_string + "] <options> DEMO_NAME", version=config.SIMDEM_VERSION)
    p.add_option('--style', '-s', default="tutorial",
                 help="The style of simulation you want to run. 'tutorial' (the default) will print out all text and pause for user input before running commands. 'simulate' will not print out the text but will still pause for input.")
    p.add_option('--path', '-p', default="demo_scripts/",
                 help="The Path to the demo scripts directory.")
    p.add_option('--auto', '-a', default="False",
                 help="Set to 'true' (or 'yes') to prevent the application waiting for user keypresses between commands. Set to 'no' when running in test mode to allow users to step through each test.")
    p.add_option('--test', '-t', default="False",
                 help="If set to anything other than False the output of the command will be compared to the expected results in the sript. Any failures will be reported")
    p.add_option('--fastfail', default="True",
                 help="If set to anything other than True test execution has will stop on the first failure. This has no affect if running in any mode other than 'test'.")
    p.add_option('--debug', '-d', default="False",
                 help="Turn on debug logging by setting to True.")
    p.add_option('--webui', '-w', default="False",
                 help="If set to anything other than False will interact with the user through a Web UI rather than the CLI.")

    options, arguments = p.parse_args()

    if not options.path.endswith("/"):
        options.path += "/"

    if options.auto == "False":
        is_automatic = False
    else:
        is_automatic = True

    if options.test == "False":
        is_test = False
    else:
        is_test = True

    if options.fastfail == "True":
        is_fast_fail= True
    else:
        is_fast_fail= False

    if options.style == "simulate":
        simulate = True
    elif options.style == 'tutorial':
        simulate = False
    else:
        print("Unknown style (--style, -s): " + options.style)
        exit(1)

    if options.debug.lower() == "true":
        config.is_debug = True

    if len(arguments) == 2:
        script_dir = options.path + arguments[1]
    else:
        script_dir = options.path

    filename = "script.md"
    is_docker = os.path.isfile('/.dockerenv')
    demo = Demo(is_docker, script_dir, filename, simulate, is_automatic, is_test);

    if options.webui == "False":
        ui = Ui()
        if len(arguments) > 0:
            cmd = arguments[0]
            # 'run' is deprecated in the CLI, but not yet removed from code
            if cmd == "tutorial":
                cmd = "run"
    else:
        ui = WebUi()
        while not ui.ready:
            time.sleep(0.25)
            print("Waiting for client connection")
        cmd = None

    demo.set_ui(ui)
    demo.run(cmd)

main()
<commit_msg>initialize cmd<commit_after>#!/usr/bin/env python3

# This is a python script that emulates a terminal session and runs
# commands from a supplied markdown file..

import optparse
import os
import sys
import time

from cli import Ui
from web import WebUi
import config
from demo import Demo
from environment import Environment

def get_bash_script(script_dir, is_simulation = True, is_automated=False, is_testing=False):
    """
    Reads a README.md file in the indicated directoy and builds an
    executable bash script from the commands contained within.
    """
    if not script_dir.endswith('/'):
        script_dir = script_dir + "/"

    script = ""
    env = Environment(script_dir, False).get()
    for key, value in env.items():
        script += key + "='" + value + "'\n"

    filename = env.get_script_file_name(script_dir)
    in_code_block = False
    in_results_section = False
    lines = list(open(script_dir + filename))
    for line in lines:
        if line.startswith("Results:"):
            # Entering results section
            in_results_section = True
        elif line.startswith("```") and not in_code_block:
            # Entering a code block, if in_results_section = True then it's a results block
            in_code_block = True
        elif line.startswith("```") and in_code_block:
            # Finishing code block
            in_results_section = False
            in_code_block = False
        elif in_code_block and not in_results_section:
            # Executable line
            script += line
        elif line.startswith("#") and not in_code_block and not in_results_section and not is_automated:
            # Heading in descriptive text
            script += "\n"
    return script

def main():
    """SimDem CLI interpreter"""

    commands = config.modes
    command_string = ""
    for command in commands:
        command_string = command_string + command + "|"
    command_string = command_string[0:len(command_string)-1]

    p = optparse.OptionParser("%prog [" + command_string + "] <options> DEMO_NAME", version=config.SIMDEM_VERSION)
    p.add_option('--style', '-s', default="tutorial",
                 help="The style of simulation you want to run. 'tutorial' (the default) will print out all text and pause for user input before running commands. 'simulate' will not print out the text but will still pause for input.")
    p.add_option('--path', '-p', default="demo_scripts/",
                 help="The Path to the demo scripts directory.")
    p.add_option('--auto', '-a', default="False",
                 help="Set to 'true' (or 'yes') to prevent the application waiting for user keypresses between commands. Set to 'no' when running in test mode to allow users to step through each test.")
    p.add_option('--test', '-t', default="False",
                 help="If set to anything other than False the output of the command will be compared to the expected results in the sript. Any failures will be reported")
    p.add_option('--fastfail', default="True",
                 help="If set to anything other than True test execution has will stop on the first failure. This has no affect if running in any mode other than 'test'.")
    p.add_option('--debug', '-d', default="False",
                 help="Turn on debug logging by setting to True.")
    p.add_option('--webui', '-w', default="False",
                 help="If set to anything other than False will interact with the user through a Web UI rather than the CLI.")

    options, arguments = p.parse_args()

    if not options.path.endswith("/"):
        options.path += "/"

    if options.auto == "False":
        is_automatic = False
    else:
        is_automatic = True

    if options.test == "False":
        is_test = False
    else:
        is_test = True

    if options.fastfail == "True":
        is_fast_fail= True
    else:
        is_fast_fail= False

    if options.style == "simulate":
        simulate = True
    elif options.style == 'tutorial':
        simulate = False
    else:
        print("Unknown style (--style, -s): " + options.style)
        exit(1)

    if options.debug.lower() == "true":
        config.is_debug = True

    if len(arguments) == 2:
        script_dir = options.path + arguments[1]
    else:
        script_dir = options.path

    filename = "script.md"
    is_docker = os.path.isfile('/.dockerenv')
    demo = Demo(is_docker, script_dir, filename, simulate, is_automatic, is_test);

    cmd = None
    if options.webui == "False":
        ui = Ui()
        if len(arguments) > 0:
            cmd = arguments[0]
            # 'run' is deprecated in the CLI, but not yet removed from code
            if cmd == "tutorial":
                cmd = "run"
    else:
        ui = WebUi()
        while not ui.ready:
            time.sleep(0.25)
            print("Waiting for client connection")
        cmd = None

    demo.set_ui(ui)
    demo.run(cmd)

main()
<|endoftext|>