<commit_before>import praw
import sched
import time

import db
import forum_parse as fparse
import settings

POE_URL = "www.pathofexile.com/forum/view-thread"

def task(next_sched):
    print("starting next run...")
    bot = GGGGobblerBot()
    sleep_time = 0
    try:
        bot.parse_reddit()
    except praw.errors.RateLimitExceeded as e:
        sleep_time = e.sleep_time
        bot.dao.rollback()
        print("Rate Limit Exceeded time = " + str(sleep_time) + ", waiting until next cycle")
    except:
        bot.dao.rollback()
        raise

    # do it again later
    if sleep_time > settings.WAIT_TIME:
        next_sched.enter(sleep_time, 1, task, (next_sched,))
        sleep_time = 0
    else:
        next_sched.enter(settings.WAIT_TIME, 1, task, (next_sched,))
    print("run over...")

def run():
    scheduler = sched.scheduler(time.time, time.sleep)
    scheduler.enter(5, 1, task, (scheduler,))
    scheduler.run()

class GGGGobblerBot:
    def __init__(self):
        self.r = praw.Reddit(user_agent=settings.APP_USER_AGENT)
        self.r.set_oauth_app_info(settings.APP_ID, settings.APP_SECRET, settings.APP_URI)
        self.r.refresh_access_information(settings.APP_REFRESH)
        self.dao = db.DAO()

    def parse_reddit(self):
        if fparse.forum_is_down():
            print("Pathofexile.com is down for maintenance")
            return
        subreddit = self.r.get_subreddit('pathofexile')
        # collect submissions that link to poe.com
        poe_submissions = []
        ids = []
        for submission in subreddit.get_hot(limit=25):
            if POE_URL in submission.url:
                poe_submissions.append(submission)
                ids.append(submission.id)
        for submission in subreddit.get_new(limit=15):
            if POE_URL in submission.url and submission.id not in ids:
                poe_submissions.append(submission)

        self.parse_submissions(poe_submissions)

    def get_comment_by_id(self, submission, comment_id):
        url = submission.permalink + comment_id
        # permalink submission, one comment stored in submission object here
        return self.r.get_submission(url=url).comments[0]

    def parse_submissions(self, submissions):
        for submission in submissions:
            posts = self.get_current_posts(submission.url)

            # only bother doing stuff if we found staff posts
            if posts == []:
                self.dao.commit()
                continue
            comments_to_post = self.create_divided_comments(posts)
            if not self.dao.reddit_thread_exists(submission.id):
                self.dao.add_reddit_thread(submission.id, self.extract_poe_id_from_url(submission.url))

            self.send_replies(submission, comments_to_post)
            # saving db state between submissions
            self.dao.commit()
        self.dao.rollback()
        self.dao.close()

    def get_current_posts(self, url):
        poe_thread_id = self.extract_poe_id_from_url(url)
        # check if we've been to this link before
        if self.dao.poe_thread_exists(poe_thread_id):
            # get posts that have been read in the past and placed in the db
            old_posts = self.dao.get_old_staff_posts_by_thread_id(poe_thread_id)
            current_posts, new_page_count = fparse.get_staff_forum_posts(poe_thread_id)
            new_posts = list(current_posts)
            # remove posts with ids that match an id stored in db
            # and keep the new copy of the post
            for current_post in current_posts:
                for old_post in old_posts:
                    # dont add posts to the db if they're already there
                    if current_post.post_id == old_post.post_id:
                        new_posts.remove(current_post)
                        # update db with new post texts
                        if current_post.md_text != old_post.md_text:
                            self.dao.update_staff_post(current_post)
                            old_posts.remove(old_post)
            if new_posts != []:
                self.dao.add_staff_posts(new_posts)
            return current_posts
        else:
            new_posts, new_page_count = fparse.get_staff_forum_posts(poe_thread_id)
            # add the new thread to the db
            self.dao.add_poe_thread(poe_thread_id, new_page_count)
            self.dao.add_staff_posts(new_posts)
            return new_posts

    def create_divided_comments(self, posts):
        """
        returns a list of strings where each string is a comment no more than COMMENT_CHAR_LIMIT long
        """
        comments = []
        cur_comment = self.create_post_preamble()
        sections = [self.create_ggg_post_section(post) for post in posts]

        for section in sections:
            remaining_space = settings.COMMENT_CHAR_LIMIT - len(cur_comment)
            if remaining_space >= len(section):
                cur_comment += section
            else:
                comments.append(cur_comment)
                if len(section) <= settings.COMMENT_CHAR_LIMIT:
                    cur_comment = section
                else:
                    # lazily split crap up
                    parts = []
                    while len(section) > settings.COMMENT_CHAR_LIMIT:
                        part = section[:settings.COMMENT_CHAR_LIMIT]
                        parts.append(part)
                        section = section[settings.COMMENT_CHAR_LIMIT:]
                    if section != "":
                        parts.append(section)
                    comments.extend(parts)
                    cur_comment = ""
        if cur_comment != "":
            comments.append(cur_comment)
        return comments

    def send_replies(self, submission, comments_to_post):
        existing_comment_ids = self.dao.get_comment_ids_by_thread(submission.id)
        num_existing_comments = len(existing_comment_ids)
        num_new_comments = len(comments_to_post)
        if num_new_comments == 0:
            return
        if num_existing_comments == 0:
            new_comments = []
            # create new comments for thread
            thing_to_reply = submission.add_comment(comments_to_post[0])
            time.sleep(settings.TIME_BETWEEN_COMMENTS)
            comments_to_post.remove(comments_to_post[0])
            new_comments.append(thing_to_reply.id)
            for comment in comments_to_post:
                thing_to_reply = thing_to_reply.reply(comment)
                new_comments.append(thing_to_reply.id)
                time.sleep(settings.TIME_BETWEEN_COMMENTS)
            self.dao.add_comments(submission.id, new_comments)

        elif num_new_comments == num_existing_comments:
            for i in range(num_existing_comments):
                comment = self.get_comment_by_id(submission, existing_comment_ids[i])
                comment.edit(comments_to_post[i])
                time.sleep(settings.TIME_BETWEEN_COMMENTS)

        elif num_new_comments > num_existing_comments:
            for i in range(num_existing_comments):
                comment = self.get_comment_by_id(submission, existing_comment_ids[i])
                comment.edit(comments_to_post[i])
                time.sleep(settings.TIME_BETWEEN_COMMENTS)
            # num_existing_comments is guaranteed to be greater than 0 so the loop is
            # guaranteed to run at least 1 time
            # noinspection PyUnboundLocalVariable
            new_comments = [comment.id]
            # create new comments after existing ones
            for i in range(num_existing_comments + 1, num_existing_comments + num_new_comments + 1):
                # noinspection PyUnboundLocalVariable
                comment = comment.reply(comments_to_post[i])
                new_comments.append(comment.id)
                time.sleep(settings.TIME_BETWEEN_COMMENTS)
            self.dao.add_comments(submission.id, new_comments)

    def create_post_preamble(self):
        return "BEEP BOOP BEEP.  Grinding Gears have been detected in the linked thread:\n\n***\n\n"

    def create_markdown_from_posts(self, posts):
        markdown = ""
        for posts in posts:
            markdown += self.create_ggg_post_section(posts)
        return markdown

    def create_ggg_post_section(self, post):
        markdown = "**" + post.author + " wrote:**\n\n"
        # body text
        body = post.md_text
        markdown += body
        # post separator
        footer = "\n\n***\n\n"
        markdown += footer
        return markdown

    def extract_poe_id_from_url(self, url):
        """
        extracts the id of a thread from the url
        """
        start_index = url.find("view-thread") + len("view-thread") + 1
        end_index = url[start_index:].find("/")
        if end_index == -1:
            return url[start_index:]
        return url[start_index:start_index + end_index]

if __name__ == "__main__":
    run()
<commit_msg>I'm dumb and don't understand SQL databases properly<commit_after>import praw
import sched
import time

import db
import forum_parse as fparse
import settings

POE_URL = "www.pathofexile.com/forum/view-thread"

def task(next_sched):
    print("starting next run...")
    bot = GGGGobblerBot()
    sleep_time = 0
    try:
        bot.parse_reddit()
    except praw.errors.RateLimitExceeded as e:
        sleep_time = e.sleep_time
        bot.dao.rollback()
        print("Rate Limit Exceeded time = " + str(sleep_time) + ", waiting until next cycle")

    # do it again later
    if sleep_time > settings.WAIT_TIME:
        next_sched.enter(sleep_time, 1, task, (next_sched,))
        sleep_time = 0
    else:
        next_sched.enter(settings.WAIT_TIME, 1, task, (next_sched,))
    print("run over...")

def run():
    scheduler = sched.scheduler(time.time, time.sleep)
    scheduler.enter(5, 1, task, (scheduler,))
    scheduler.run()

class GGGGobblerBot:
    def __init__(self):
        self.r = praw.Reddit(user_agent=settings.APP_USER_AGENT)
        self.r.set_oauth_app_info(settings.APP_ID, settings.APP_SECRET, settings.APP_URI)
        self.r.refresh_access_information(settings.APP_REFRESH)
        self.dao = db.DAO()

    def parse_reddit(self):
        if fparse.forum_is_down():
            print("Pathofexile.com is down for maintenance")
            return
        subreddit = self.r.get_subreddit('pathofexile')
        # collect submissions that link to poe.com
        poe_submissions = []
        ids = []
        for submission in subreddit.get_hot(limit=25):
            if POE_URL in submission.url:
                poe_submissions.append(submission)
                ids.append(submission.id)
        for submission in subreddit.get_new(limit=15):
            if POE_URL in submission.url and submission.id not in ids:
                poe_submissions.append(submission)

        self.parse_submissions(poe_submissions)

    def get_comment_by_id(self, submission, comment_id):
        url = submission.permalink + comment_id
        # permalink submission, one comment stored in submission object here
        return self.r.get_submission(url=url).comments[0]

    def parse_submissions(self, submissions):
        for submission in submissions:
            posts = self.get_current_posts(submission.url)

            # only bother doing stuff if we found staff posts
            if posts == []:
                self.dao.commit()
                continue
            comments_to_post = self.create_divided_comments(posts)
            if not self.dao.reddit_thread_exists(submission.id):
                self.dao.add_reddit_thread(submission.id, self.extract_poe_id_from_url(submission.url))

            self.send_replies(submission, comments_to_post)
            # saving db state between submissions
            self.dao.commit()
        self.dao.rollback()
        self.dao.close()

    def get_current_posts(self, url):
        poe_thread_id = self.extract_poe_id_from_url(url)
        # check if we've been to this link before
        if self.dao.poe_thread_exists(poe_thread_id):
            # get posts that have been read in the past and placed in the db
            old_posts = self.dao.get_old_staff_posts_by_thread_id(poe_thread_id)
            current_posts, new_page_count = fparse.get_staff_forum_posts(poe_thread_id)
            new_posts = list(current_posts)
            # remove posts with ids that match an id stored in db
            # and keep the new copy of the post
            for current_post in current_posts:
                for old_post in old_posts:
                    # dont add posts to the db if they're already there
                    if current_post.post_id == old_post.post_id:
                        new_posts.remove(current_post)
                        # update db with new post texts
                        if current_post.md_text != old_post.md_text:
                            self.dao.update_staff_post(current_post)
                            old_posts.remove(old_post)
            if new_posts != []:
                self.dao.add_staff_posts(new_posts)
            return current_posts
        else:
            new_posts, new_page_count = fparse.get_staff_forum_posts(poe_thread_id)
            # add the new thread to the db
            self.dao.add_poe_thread(poe_thread_id, new_page_count)
            self.dao.add_staff_posts(new_posts)
            return new_posts

    def create_divided_comments(self, posts):
        """
        returns a list of strings where each string is a comment no more than COMMENT_CHAR_LIMIT long
        """
        comments = []
        cur_comment = self.create_post_preamble()
        sections = [self.create_ggg_post_section(post) for post in posts]

        for section in sections:
            remaining_space = settings.COMMENT_CHAR_LIMIT - len(cur_comment)
            if remaining_space >= len(section):
                cur_comment += section
            else:
                comments.append(cur_comment)
                if len(section) <= settings.COMMENT_CHAR_LIMIT:
                    cur_comment = section
                else:
                    # lazily split crap up
                    parts = []
                    while len(section) > settings.COMMENT_CHAR_LIMIT:
                        part = section[:settings.COMMENT_CHAR_LIMIT]
                        parts.append(part)
                        section = section[settings.COMMENT_CHAR_LIMIT:]
                    if section != "":
                        parts.append(section)
                    comments.extend(parts)
                    cur_comment = ""
        if cur_comment != "":
            comments.append(cur_comment)
        return comments

    def send_replies(self, submission, comments_to_post):
        existing_comment_ids = self.dao.get_comment_ids_by_thread(submission.id)
        num_existing_comments = len(existing_comment_ids)
        num_new_comments = len(comments_to_post)
        if num_new_comments == 0:
            return
        if num_existing_comments == 0:
            new_comments = []
            # create new comments for thread
            thing_to_reply = submission.add_comment(comments_to_post[0])
            time.sleep(settings.TIME_BETWEEN_COMMENTS)
            comments_to_post.remove(comments_to_post[0])
            new_comments.append(thing_to_reply.id)
            for comment in comments_to_post:
                thing_to_reply = thing_to_reply.reply(comment)
                new_comments.append(thing_to_reply.id)
                time.sleep(settings.TIME_BETWEEN_COMMENTS)
            self.dao.add_comments(submission.id, new_comments)

        elif num_new_comments == num_existing_comments:
            for i in range(num_existing_comments):
                comment = self.get_comment_by_id(submission, existing_comment_ids[i])
                comment.edit(comments_to_post[i])
                time.sleep(settings.TIME_BETWEEN_COMMENTS)

        elif num_new_comments > num_existing_comments:
            for i in range(num_existing_comments):
                comment = self.get_comment_by_id(submission, existing_comment_ids[i])
                comment.edit(comments_to_post[i])
                time.sleep(settings.TIME_BETWEEN_COMMENTS)
            # num_existing_comments is guaranteed to be greater than 0 so the loop is
            # guaranteed to run at least 1 time
            # noinspection PyUnboundLocalVariable
            new_comments = [comment.id]
            # create new comments after existing ones
            for i in range(num_existing_comments + 1, num_existing_comments + num_new_comments + 1):
                # noinspection PyUnboundLocalVariable
                comment = comment.reply(comments_to_post[i])
                new_comments.append(comment.id)
                time.sleep(settings.TIME_BETWEEN_COMMENTS)
            self.dao.add_comments(submission.id, new_comments)

    def create_post_preamble(self):
        return "BEEP BOOP BEEP.  Grinding Gears have been detected in the linked thread:\n\n***\n\n"

    def create_markdown_from_posts(self, posts):
        markdown = ""
        for posts in posts:
            markdown += self.create_ggg_post_section(posts)
        return markdown

    def create_ggg_post_section(self, post):
        markdown = "**" + post.author + " wrote:**\n\n"
        # body text
        body = post.md_text
        markdown += body
        # post separator
        footer = "\n\n***\n\n"
        markdown += footer
        return markdown

    def extract_poe_id_from_url(self, url):
        """
        extracts the id of a thread from the url
        """
        start_index = url.find("view-thread") + len("view-thread") + 1
        end_index = url[start_index:].find("/")
        if end_index == -1:
            return url[start_index:]
        return url[start_index:start_index + end_index]

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


<commit_before>from ggame import App, Color, LineStyle, Sprite
from ggame import RectangleAsset, CircleAsset
import math

SCREEN_WIDTH = 1920
SCREEN_HEIGHT = 1080

red = Color(0xff0000, 1.0)
blue = Color(0x0000ff, 1.0)
green = Color(0x00ff00, 1.0)
black = Color(0x000000, 1.0)
pink = Color(0xff00ff, 1.0)
thinline = LineStyle(0, black)

xcord = range(0, 1500, 1)

mode = input("Which type of function do you have? \nl for linear, t for Trig, p for Parabola ")
if mode == "l":
    k = float(input("f(x) = kx + b, set the value of k: "))
    b = float(input("f(x) = kx + b, set the value of b: "))
elif mode == "t":
    a = float(input("f(x) = a sin h (x + k) , set the value of a: "))
    h = float(input("f(x) = a sin h (x + k) , set the value of h: "))
    k = float(input("f(x) = a sin h (x + k) , set the value of k: "))
elif mode == "p":
    a = float(input("f(x) = ax^2 + bx + c , set the value of a: "))
    b = float(input("f(x) = ax^2 + bx + c , set the value of b: "))
    c = float(input("f(x) = ax^2 + bx + c , set the value of c: "))

class Dot(Sprite):

    asset = CircleAsset(2, thinline, pink)

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

        if mode == "l":
            if k >= 0.5625:
                self.x = (840*k-100*b-580)/k
            elif 0.5625 > k > 0:
                self.x = 0
            elif k <= -0.5625:
                self.x = (840*k+500-100*b)/k
            elif 0 > k > -0.5625:
                self.x = 0
        elif mode == "t":
            self.x = 0
        elif mode == "p":
            self.x = (-b - math.sqrt(b**2-4*a*c))/2*a

        self.loop = False

    def l(self):
        return 500-k*(self.x-840)-100*b

    def t(self):
        return 500-a*100*math.sin(h*(self.x-840+100*k)/100)

    def p(self):
        return 500-(a/100*(self.x-840)**2+100*b*self.x+100*c)

    def step(self):
        if 0 <= self.y:
            if mode == "l":
                n = self.y-(500-k*(self.x-840+1)-100*b)
                if not self.loop:
                    self.y = self.l()
                    sprite = Sprite(Dot.asset, (self.x, self.y))

                if n > 1:
                    self.y -= 2
                    sprite1 = Sprite(Dot.asset, (self.x, self.y))
                    self.loop = True
                    n -= 2
                else:
                    self.loop = False

            elif mode == "t":
                n = self.y-(500-a*100*math.sin(h*(self.x-840+1+100*k)/100))
                if not self.loop:
                    self.y = self.t()
                    sprite = Sprite(Dot.asset, (self.x, self.y))
                    print("1")

                if n > 1:
                    self.y -= 1
                    sprite1 = Sprite(Dot.asset, (self.x, self.y))
                    self.loop = True
                    n -= 1
                    print("2")

                elif n < -1:
                    self.y += 1
                    sprite1 = Sprite(Dot.asset, (self.x, self.y))
                    self.loop = True
                    n += 1
                    print("3")

                else:
                    self.loop = False

                    print("4")

            elif mode == "p":
                print(self.x)
                n = self.y-500+(a/100*(self.x-839)**2+100*b*(self.x+1)+100*c)
                if not self.loop:
                    self.y = self.p()
                    sprite = Sprite(Dot.asset, (self.x, self.y))
                    print("1")

                if n > 1:
                    self.y -= 1
                    sprite1 = Sprite(Dot.asset, (self.x, self.y))
                    self.loop = True
                    n -= 1
                    print("2")

                elif n < -1:
                    self.y += 1
                    sprite1 = Sprite(Dot.asset, (self.x, self.y))
                    self.loop = True
                    n += 1
                    print("3")

                else:
                    self.loop = False
                    print("4")
        if not self.loop:
            self.x += 1

class Grapher(App):
    def __init__(self, width, height):
        super().__init__(width, height)

        xaxis = RectangleAsset(1920,1, thinline, black)
        yaxis = RectangleAsset(1,1080, thinline, black)
        sprite1 = Sprite(xaxis, (0, 500))
        sprite2 = Sprite(yaxis, (840, 0))

        Dot((0,1080))

    def step(self):
        for dot in self.getSpritesbyClass(Dot):
            dot.step()

myapp = Grapher(SCREEN_WIDTH, SCREEN_HEIGHT)
myapp.run()
<commit_msg>Updated from Brython Server: December 7, 2015 2:36:00 PM EST<commit_after>from ggame import App, Color, LineStyle, Sprite
from ggame import RectangleAsset, CircleAsset
import math

SCREEN_WIDTH = 1920
SCREEN_HEIGHT = 1080

red = Color(0xff0000, 1.0)
blue = Color(0x0000ff, 1.0)
green = Color(0x00ff00, 1.0)
black = Color(0x000000, 1.0)
pink = Color(0xff00ff, 1.0)
thinline = LineStyle(0, black)

xcord = range(0, 1500, 1)

mode = input("Which type of function do you have? \nl for linear, t for Trig, p for Parabola ")
if mode == "l":
    k = float(input("f(x) = kx + b, set the value of k: "))
    b = float(input("f(x) = kx + b, set the value of b: "))
elif mode == "t":
    a = float(input("f(x) = a sin h (x + k) , set the value of a: "))
    h = float(input("f(x) = a sin h (x + k) , set the value of h: "))
    k = float(input("f(x) = a sin h (x + k) , set the value of k: "))
elif mode == "p":
    a = float(input("f(x) = ax^2 + bx + c , set the value of a: "))
    b = float(input("f(x) = ax^2 + bx + c , set the value of b: "))
    c = float(input("f(x) = ax^2 + bx + c , set the value of c: "))

class Dot(Sprite):

    asset = CircleAsset(2, thinline, pink)

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

        if mode == "l":
            if k >= 0.5625:
                self.x = (840*k-100*b-580)/k
            elif 0.5625 > k > 0:
                self.x = 0
            elif k <= -0.5625:
                self.x = (840*k+500-100*b)/k
            elif 0 > k > -0.5625:
                self.x = 0
        elif mode == "t":
            self.x = 0
        elif mode == "p":
            self.x = (-b - math.sqrt(b**2-4*a*c))/2*a+840

        self.loop = False

    def l(self):
        return 500-k*(self.x-840)-100*b

    def t(self):
        return 500-a*100*math.sin(h*(self.x-840+100*k)/100)

    def p(self):
        return 500-(a/100*(self.x-840)**2+100*b*self.x+100*c)

    def step(self):
        if 0 <= self.y:
            if mode == "l":
                n = self.y-(500-k*(self.x-840+1)-100*b)
                if not self.loop:
                    self.y = self.l()
                    sprite = Sprite(Dot.asset, (self.x, self.y))

                if n > 1:
                    self.y -= 2
                    sprite1 = Sprite(Dot.asset, (self.x, self.y))
                    self.loop = True
                    n -= 2
                else:
                    self.loop = False

            elif mode == "t":
                n = self.y-(500-a*100*math.sin(h*(self.x-840+1+100*k)/100))
                if not self.loop:
                    self.y = self.t()
                    sprite = Sprite(Dot.asset, (self.x, self.y))
                    print("1")

                if n > 1:
                    self.y -= 1
                    sprite1 = Sprite(Dot.asset, (self.x, self.y))
                    self.loop = True
                    n -= 1
                    print("2")

                elif n < -1:
                    self.y += 1
                    sprite1 = Sprite(Dot.asset, (self.x, self.y))
                    self.loop = True
                    n += 1
                    print("3")

                else:
                    self.loop = False

                    print("4")

            elif mode == "p":
                print(self.x)
                n = self.y-500+(a/100*(self.x-839)**2+100*b*(self.x+1)+100*c)
                if not self.loop:
                    self.y = self.p()
                    sprite = Sprite(Dot.asset, (self.x, self.y))
                    print("1")

                if n > 1:
                    self.y -= 1
                    sprite1 = Sprite(Dot.asset, (self.x, self.y))
                    self.loop = True
                    n -= 1
                    print("2")

                elif n < -1:
                    self.y += 1
                    sprite1 = Sprite(Dot.asset, (self.x, self.y))
                    self.loop = True
                    n += 1
                    print("3")

                else:
                    self.loop = False
                    print("4")
        if not self.loop:
            self.x += 1

class Grapher(App):
    def __init__(self, width, height):
        super().__init__(width, height)

        xaxis = RectangleAsset(1920,1, thinline, black)
        yaxis = RectangleAsset(1,1080, thinline, black)
        sprite1 = Sprite(xaxis, (0, 500))
        sprite2 = Sprite(yaxis, (840, 0))

        Dot((0,1080))

    def step(self):
        for dot in self.getSpritesbyClass(Dot):
            dot.step()

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


<commit_before>#!/usr/bin/python
# -*- coding: utf-8 -*-

# Copyright (c) 2007-2011 Parisson
# Copyright (c) 2007 Olivier Guilyardi <olivier@samalyse.com>
# Copyright (c) 2007-2011 Guillaume Pellerin <pellerin@parisson.com>
# Copyright (c) 2010-2011 Paul Brossier <piem@piem.org>
#
# This file is part of TimeSide.

# TimeSide is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.

# TimeSide is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with TimeSide.  If not, see <http://www.gnu.org/licenses/>.

# Authors: Paul Brossier <piem@piem.org>
#          Guilaume Pellerin <yomguy@parisson.com>

from timeside.core import Processor, implements, interfacedoc
from timeside.api import IDecoder
from timeside.encoder.gstutils import *

import Queue

GST_APPSINK_MAX_BUFFERS = 10
QUEUE_SIZE = 10

class FileDecoder(Processor):
    """ gstreamer-based decoder """
    implements(IDecoder)

    # duration ms, before discovery process times out
    MAX_DISCOVERY_TIME = 3000

    mimetype = ''
    output_nframes    = 8*1024
    output_samplerate = 44100
    output_channels   = 1

    was_discovered    = False

    # IProcessor methods

    @staticmethod
    @interfacedoc
    def id():
        return "gstreamerdec"

    def __init__(self, uri):
        # is this a file?
        import os.path
        if os.path.exists(uri):
            # get the absolute path
            uri = os.path.abspath(uri)
            # first run the file/uri through the discover pipeline
            self.discover(uri)
            if not self.was_discovered:
                raise Exception('discovered was not run')
            # and make a uri of it
            from urllib import quote
            self.uri = 'file://'+quote(uri)
        else:
            self.uri = uri

    def setup(self, channels = None, samplerate = None, nframes = None):
        # the output data format we want
        if nframes:    self.output_nframes    = nframes
        if samplerate: self.output_samplerate = int(samplerate)
        if channels:   self.output_channels   = int(channels)
        uri = self.uri

        self.pipe = ''' uridecodebin uri=%(uri)s
                  ! audioconvert
                  ! audioresample
                  ! appsink name=sink sync=False async=True
                  ''' % locals()
        self.pipeline = gst.parse_launch(self.pipe)

        sink_caps = gst.Caps("""audio/x-raw-float,
            endianness=(int)1234,
            channels=(int)%s,
            width=(int)32,
            rate=(int)%d""" % (int(self.output_channels), int(self.output_samplerate)))

        self.sink = self.pipeline.get_by_name('sink')
        self.sink.set_property("caps", sink_caps)
        self.sink.set_property('max-buffers', GST_APPSINK_MAX_BUFFERS)
        self.sink.set_property("drop", False)
        self.sink.set_property('emit-signals', True)
        self.sink.connect("new-buffer", self.on_new_buffer)

        self.bus = self.pipeline.get_bus()
        self.bus.add_signal_watch()
        self.bus.connect('message', self.on_message)

        self.queue = Queue.Queue(QUEUE_SIZE)

        import threading
        class MainloopThread(threading.Thread):
            def __init__(self, mainloop):
                threading.Thread.__init__(self)
                self.mainloop = mainloop

            def run(self):
                self.mainloop.run()
        self.mainloopthread = MainloopThread(self.mainloop)
        self.mainloopthread.start()

        self.eod = False

        self.last_buffer = None

        # start pipeline
        self.pipeline.set_state(gst.STATE_PLAYING)

    def on_message(self, bus, message):
        t = message.type
        if t == gst.MESSAGE_EOS:
            self.pipeline.set_state(gst.STATE_NULL)
            self.queue.put(gst.MESSAGE_EOS)
            self.mainloop.quit()
        elif t == gst.MESSAGE_ERROR:
            self.pipeline.set_state(gst.STATE_NULL)
            err, debug = message.parse_error()
            self.mainloop.quit()
            print "Error: %s" % err, debug
        elif t == gst.MESSAGE_TAG:
            # TODO
            # msg.parse_tags()
            pass

    def on_new_buffer(self, sink):
        from numpy import concatenate
        buf = sink.emit('pull-buffer')
        new_array = gst_buffer_to_numpy_array(buf, self.output_channels)
        #print 'processing new buffer', new_array.shape
        if self.last_buffer is None:
            self.last_buffer = new_array
        else:
            self.last_buffer = concatenate((self.last_buffer, new_array), axis=0)
        while self.last_buffer.shape[0] >= self.output_nframes:
            new_block = self.last_buffer[:self.output_nframes]
            self.last_buffer = self.last_buffer[self.output_nframes:]
            #print 'queueing', new_block.shape, 'remaining', self.last_buffer.shape
            self.queue.put( [new_block, False ] )

    @interfacedoc
    def process(self, frames = None, eod = False):
        buf = self.queue.get()
        if buf == gst.MESSAGE_EOS:
            return self.last_buffer, True
        frames, eod = buf
        return frames, eod

    @interfacedoc
    def channels(self):
        return  self.output_channels

    @interfacedoc
    def samplerate(self):
        return self.output_samplerate

    @interfacedoc
    def nframes(self):
        return self.output_nframes

    @interfacedoc
    def release(self):
        self.pipeline.set_state(gst.STATE_NULL)
        self.mainloopthread.join()

    def __del__(self):
        self.release()

    ## IDecoder methods

    @interfacedoc
    @interfacedoc
    def format(self):
        # TODO check
        if self.mimetype == 'application/x-id3':
            self.mimetype = 'audio/mpeg'
        return self.mimetype

    @interfacedoc
    def encoding(self):
        # TODO check
        return self.mimetype.split('/')[-1]

    @interfacedoc
    def resolution(self):
        # TODO check: width or depth?
        return self.audiowidth

    @interfacedoc
    def metadata(self):
        # TODO check
        return self.tags

    def duration(self):
        return self.duration

    ## gst.extend discoverer

    def discover(self, path):
        """ gstreamer based helper function to get file attributes """
        from gst.extend import discoverer
        d = discoverer.Discoverer(path, timeout = self.MAX_DISCOVERY_TIME)
        d.connect('discovered', self.discovered)

        # start discover pipeline
        self.mainloop = gobject.MainLoop()
        d.discover()
        self.mainloop.run()
        #d.print_info()

    def discovered(self, d, is_media):
        """ gstreamer based helper executed upon discover() completion """
        from math import ceil
        if is_media and d.is_audio:
            # copy the discoverer attributes to self
            self.mimetype= d.mimetype
            self.input_samplerate = d.audiorate
            self.input_channels = d.audiochannels
            self.input_width = d.audiowidth
            # conversion from time in nanoseconds to seconds
            self.input_duration = d.audiolength * 1.e-9
            # conversion from time in nanoseconds to frames
            self.input_total_frames = int ( ceil (d.audiorate * d.audiolength * 1.e-9) )
            self.input_total_frames = d.audiorate * d.audiolength * 1.e-9
            # copy tags
            self.tags = d.tags
        elif not d.is_audio:
            print "error, no audio found!"
        else:
            print "fail", path
        self.mainloop.quit()
        self.was_discovered = True
<commit_msg>timeside/decoder/core.py: query duration to speed things up, avoiding the use of discover<commit_after>#!/usr/bin/python
# -*- coding: utf-8 -*-

# Copyright (c) 2007-2011 Parisson
# Copyright (c) 2007 Olivier Guilyardi <olivier@samalyse.com>
# Copyright (c) 2007-2011 Guillaume Pellerin <pellerin@parisson.com>
# Copyright (c) 2010-2011 Paul Brossier <piem@piem.org>
#
# This file is part of TimeSide.

# TimeSide is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.

# TimeSide is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with TimeSide.  If not, see <http://www.gnu.org/licenses/>.

# Authors: Paul Brossier <piem@piem.org>
#          Guilaume Pellerin <yomguy@parisson.com>

from timeside.core import Processor, implements, interfacedoc
from timeside.api import IDecoder
from timeside.encoder.gstutils import *

import Queue

GST_APPSINK_MAX_BUFFERS = 10
QUEUE_SIZE = 10

class FileDecoder(Processor):
    """ gstreamer-based decoder """
    implements(IDecoder)

    # duration ms, before discovery process times out
    MAX_DISCOVERY_TIME = 3000

    mimetype = ''
    output_nframes    = 8*1024
    output_samplerate = 44100
    output_channels   = 1

    was_discovered    = False
    pipeline          = None
    mainloopthread    = None
    read_error        = None

    # IProcessor methods

    @staticmethod
    @interfacedoc
    def id():
        return "gstreamerdec"

    def __init__(self, uri):
        # is this a file?
        import os.path
        if os.path.exists(uri):
            # get the absolute path
            uri = os.path.abspath(uri)
            # first run the file/uri through the discover pipeline
            self.discover(uri)
            if not self.was_discovered:
                raise Exception('discovered was not run')
            # and make a uri of it
            from urllib import quote
            self.uri = 'file://'+quote(uri)
        else:
            self.uri = uri

    def setup(self, channels = None, samplerate = None, nframes = None):
        # the output data format we want
        if nframes:    self.output_nframes    = nframes
        if samplerate: self.output_samplerate = int(samplerate)
        if channels:   self.output_channels   = int(channels)
        uri = self.uri

        self.pipe = ''' uridecodebin name=uridecodebin uri=%(uri)s
                  ! audioconvert name=audioconvert
                  ! audioresample
                  ! appsink name=sink sync=False async=True
                  ''' % locals()
        self.pipeline = gst.parse_launch(self.pipe)

        sink_caps = gst.Caps("""audio/x-raw-float,
            endianness=(int)1234,
            channels=(int)%s,
            width=(int)32,
            rate=(int)%d""" % (int(self.output_channels), int(self.output_samplerate)))

        self.decodebin = self.pipeline.get_by_name('uridecodebin')
        self.decodebin.connect("pad-added", self._pad_added_cb)
        self.decodebin.connect("no-more-pads", self._no_more_pads_cb)
        self.decodebin.connect("unknown-type", self._unknown_type_cb)

        self.conv = self.pipeline.get_by_name('audioconvert')
        self.conv.get_pad("sink").connect("notify::caps", self._notify_caps_cb)

        self.sink = self.pipeline.get_by_name('sink')
        self.sink.set_property("caps", sink_caps)
        self.sink.set_property('max-buffers', GST_APPSINK_MAX_BUFFERS)
        self.sink.set_property("drop", False)
        self.sink.set_property('emit-signals', True)
        self.sink.connect("new-buffer", self._on_new_buffer_cb)

        self.bus = self.pipeline.get_bus()
        self.bus.add_signal_watch()
        self.bus.connect('message', self._on_message_cb)

        self.queue = Queue.Queue(QUEUE_SIZE)

        import threading
        class MainloopThread(threading.Thread):
            def __init__(self, mainloop):
                threading.Thread.__init__(self)
                self.mainloop = mainloop

            def run(self):
                self.mainloop.run()
        self.mainloopthread = MainloopThread(self.mainloop)
        self.mainloopthread.start()
        #self.mainloopthread = get_loop_thread()
        ##self.mainloop = self.mainloopthread.mainloop

        self.eod = False

        self.last_buffer = None

        self._pad_found = False

        # start pipeline
        self.pipeline.set_state(gst.STATE_PLAYING)

        if self.read_error:
            self.release()
            raise self.read_error

    def _pad_added_cb(self, decodebin, pad):
        caps = pad.get_caps()
        if caps.to_string().startswith('audio'):
            if not self.conv.get_pad('sink').is_linked():
                self._pad_found = True
                pad.link(self.conv.get_pad('sink'))

    def _no_more_pads_cb(self, decodebin):
        self.pipeline.info("no more pads")
        if not self._pad_found:
            self.read_exc = Exception("no audio stream found")

    def _unknown_type_cb(self, decodebin, pad, caps):
        self.pipeline.debug("unknown type : %s" % caps.to_string())
        if not caps.to_string().startswith('audio'):
            return
        self.read_error = Exception("no known audio stream found")

    def _notify_caps_cb(self, pad, args):
        caps = pad.get_negotiated_caps()
        if not caps:
            pad.info("no negotiated caps available")
            return
        # the caps are fixed
        # We now get the total length of that stream
        q = gst.query_new_duration(gst.FORMAT_TIME)
        pad.info("sending duration query")
        if pad.get_peer().query(q):
            format, length = q.parse_duration()
            if format == gst.FORMAT_TIME:
                pad.info("got duration (time) : %s" % (gst.TIME_ARGS(length),))
            else:
                pad.info("got duration : %d [format:%d]" % (length, format))
        else:
            length = -1
            gst.warning("duration query failed")

        # We store the caps and length in the proper location
        if "audio" in caps.to_string():
            self.input_samplerate = caps[0]["rate"]
            self.input_channels = caps[0]["channels"]
            self.input_duration = length / 1.e9
            self.input_total_frames = int(self.input_duration * self.input_samplerate)
            if "x-raw-float" in caps.to_string():
                self.input_width = caps[0]["width"]
            else:
                self.input_width = caps[0]["depth"]

    def _on_message_cb(self, bus, message):
        t = message.type
        if t == gst.MESSAGE_EOS:
            self.pipeline.set_state(gst.STATE_NULL)
            self.queue.put(gst.MESSAGE_EOS)
            self.mainloop.quit()
        elif t == gst.MESSAGE_ERROR:
            self.pipeline.set_state(gst.STATE_NULL)
            err, debug = message.parse_error()
            self.mainloop.quit()
            print "Error: %s" % err, debug
        elif t == gst.MESSAGE_TAG:
            # TODO
            # msg.parse_tags()
            pass

    def _on_new_buffer_cb(self, sink):
        from numpy import concatenate
        buf = sink.emit('pull-buffer')
        new_array = gst_buffer_to_numpy_array(buf, self.output_channels)
        #print 'processing new buffer', new_array.shape
        if self.last_buffer is None:
            self.last_buffer = new_array
        else:
            self.last_buffer = concatenate((self.last_buffer, new_array), axis=0)
        while self.last_buffer.shape[0] >= self.output_nframes:
            new_block = self.last_buffer[:self.output_nframes]
            self.last_buffer = self.last_buffer[self.output_nframes:]
            #print 'queueing', new_block.shape, 'remaining', self.last_buffer.shape
            self.queue.put( [new_block, False ] )

    @interfacedoc
    def process(self, frames = None, eod = False):
        buf = self.queue.get()
        if buf == gst.MESSAGE_EOS:
            return self.last_buffer, True
        frames, eod = buf
        return frames, eod

    @interfacedoc
    def channels(self):
        return  self.output_channels

    @interfacedoc
    def samplerate(self):
        return self.output_samplerate

    @interfacedoc
    def nframes(self):
        return self.input_total_frames

    @interfacedoc
    def release(self):
        if self.pipeline: self.pipeline.set_state(gst.STATE_NULL)
        if self.mainloopthread: self.mainloopthread.join()

    def __del__(self):
        self.release()

    ## IDecoder methods

    @interfacedoc
    @interfacedoc
    def format(self):
        # TODO check
        if self.mimetype == 'application/x-id3':
            self.mimetype = 'audio/mpeg'
        return self.mimetype

    @interfacedoc
    def encoding(self):
        # TODO check
        return self.mimetype.split('/')[-1]

    @interfacedoc
    def resolution(self):
        # TODO check: width or depth?
        return self.audiowidth

    @interfacedoc
    def metadata(self):
        # TODO check
        return self.tags

    def duration(self):
        return self.duration

    ## gst.extend discoverer

    def discover(self, path):
        """ gstreamer based helper function to get file attributes """
        from gst.extend import discoverer
        d = discoverer.Discoverer(path, timeout = self.MAX_DISCOVERY_TIME)
        d.connect('discovered', self.discovered)

        # start discover pipeline
        self.mainloop = gobject.MainLoop()
        d.discover()
        self.mainloop.run()
        #d.print_info()

    def discovered(self, d, is_media):
        """ gstreamer based helper executed upon discover() completion """
        from math import ceil
        if is_media and d.is_audio:
            # copy the discoverer attributes to self
            self.mimetype= d.mimetype
            self.input_samplerate = d.audiorate
            self.input_channels = d.audiochannels
            self.input_width = d.audiowidth
            # conversion from time in nanoseconds to seconds
            self.input_duration = d.audiolength * 1.e-9
            # conversion from time in nanoseconds to frames
            self.input_total_frames = int ( ceil (d.audiorate * d.audiolength * 1.e-9) )
            self.input_total_frames = d.audiorate * d.audiolength * 1.e-9
            # copy tags
            self.tags = d.tags
        elif not d.is_audio:
            print "error, no audio found!"
        else:
            print "fail", path
        self.mainloop.quit()
        self.was_discovered = True
<|endoftext|>


<commit_before>import sublime, sublime_plugin
import gscommon as gs
import re, os, httplib

DOMAIN = "GsShell"
GO_RUN_PAT = re.compile(r'^go\s+(run|play)$', re.IGNORECASE)
GO_SHARE_PAT = re.compile(r'^go\s+share$', re.IGNORECASE)
GO_PLAY_PAT = re.compile(r'(\b)go\s+play(\b)', re.IGNORECASE)

class Prompt(object):
	def __init__(self, view, change_history=True):
		self.view = view
		self.panel = None
		self.subcommands = [
			'go run', 'go build', 'go clean', 'go fix',
			'go install', 'go test', 'go fmt', 'go vet', 'go tool',
			'go share', 'go play',
		]
		self.settings = sublime.load_settings('GoSublime-GsShell.sublime-settings')
		self.change_history = change_history

	def on_done(self, s):
		fn = self.view.file_name()
		win = self.view.window()
		if fn and win:
			basedir = os.path.dirname(fn)
			for v in win.views():
				vfn = v.file_name()
				if os.path.dirname(vfn) == basedir and vfn.endswith('.go'):
					v.run_command('gs_fmt_save')

		# above we do some saves - thus creating a race so push this back to the end of the queue
		def cb(s):
			file_name = self.view.file_name()
			s = GO_PLAY_PAT.sub(r'\1go run\2', s)
			s = s.strip()
			if s and self.change_history:
				self.settings.set('last_command', s)
				sublime.save_settings('GoSublime-GsShell.sublime-settings')

			if GO_SHARE_PAT.match(s):
				s = ''
				host = "play.golang.org"
				warning = 'Are you sure you want to share this file. It will be public on %s' % host
				if not sublime.ok_cancel_dialog(warning):
					return

				try:
					c = httplib.HTTPConnection(host)
					src = self.view.substr(sublime.Region(0, self.view.size()))
					c.request('POST', '/share', src, {'User-Agent': 'GoSublime'})
					s = 'http://%s/p/%s' % (host, c.getresponse().read())
				except Exception as ex:
					s = 'Error: %s' % ex

				self.show_output(s)
				return

			if GO_RUN_PAT.match(s):
				if not file_name:
					# todo: clean this up after the command runs
					f, err = gs.temp_file(suffix='.go', prefix=DOMAIN+'-play.', delete=False)
					if err:
						self.show_output(err)
						return
					else:
						try:
							src = self.view.substr(sublime.Region(0, self.view.size()))
							if isinstance(src, unicode):
								src = src.encode('utf-8')
							f.write(src)
							f.close()
						except Exception as ex:
							self.show_output('Error: %s' % ex)
							return
						file_name = f.name
				s = 'go run "%s"' % file_name
			else:
				gpat = ' *.go'
				if gpat in s:
					fns = []
					for fn in os.listdir(os.path.dirname(self.view.file_name())):
						if fn.endswith('.go') and fn[0] not in ('.', '_') and not fn.endswith('_test.go'):
							fns.append('"%s"' % fn)
					fns = ' '.join(fns)
					if fns:
						s = s.replace(gpat, ' '+fns)
			self.view.window().run_command("exec", { 'kill': True })
			self.view.window().run_command("exec", {
				'shell': True,
				'env': gs.env(),
				'cmd': [s],
				'file_regex': '^(.+\.go):([0-9]+):(?:([0-9]+):)?\s*(.*)',
			})

		sublime.set_timeout(lambda: cb(s), 0)

	def show_output(self, s):
		panel_name = DOMAIN+'-share'
		panel = self.view.window().get_output_panel(panel_name)
		edit = panel.begin_edit()
		try:
			panel.set_read_only(False)
			panel.sel().clear()
			panel.replace(edit, sublime.Region(0, panel.size()), s)
			panel.sel().add(sublime.Region(0, panel.size()))
			panel.set_read_only(True)
		finally:
			panel.end_edit(edit)
		print('%s output: %s' % (DOMAIN, s))
		self.view.window().run_command("show_panel", {"panel": "output.%s" % panel_name})

	def on_change(self, s):
		if self.panel:
			size = self.view.size()
			if s.endswith('\t'):
				lc = self.settings.get('last_command', 'go ')
				s = s.strip()
				if s and s not in ('', 'go'):
					l = []
					for i in self.subcommands:
						if i.startswith(s):
							l.append(i)
					if len(l) == 1:
						s = '%s ' % l[0]
				elif lc:
					s = '%s ' % lc
				edit = self.panel.begin_edit()
				try:
					self.panel.replace(edit, sublime.Region(0, self.panel.size()), s)
				finally:
					self.panel.end_edit(edit)

class GsShellCommand(sublime_plugin.WindowCommand):
	def is_enabled(self):
		view = gs.active_valid_go_view(self.window)
		return bool(view)

	def run(self, prompt="go ", run=""):
		view = gs.active_valid_go_view(self.window)
		if not view:
			gs.notice(DOMAIN, "this not a source.go view")
			return

		run = run.strip()
		p = Prompt(view, run == "")
		if run:
			p.on_done(run)
		else:
			p.panel = self.window.show_input_panel("GsShell", prompt, p.on_done, p.on_change, None)
<commit_msg>fix undefined (None) file_name<commit_after>import sublime, sublime_plugin
import gscommon as gs
import re, os, httplib

DOMAIN = "GsShell"
GO_RUN_PAT = re.compile(r'^go\s+(run|play)$', re.IGNORECASE)
GO_SHARE_PAT = re.compile(r'^go\s+share$', re.IGNORECASE)
GO_PLAY_PAT = re.compile(r'(\b)go\s+play(\b)', re.IGNORECASE)

class Prompt(object):
	def __init__(self, view, change_history=True):
		self.view = view
		self.panel = None
		self.subcommands = [
			'go run', 'go build', 'go clean', 'go fix',
			'go install', 'go test', 'go fmt', 'go vet', 'go tool',
			'go share', 'go play',
		]
		self.settings = sublime.load_settings('GoSublime-GsShell.sublime-settings')
		self.change_history = change_history

	def on_done(self, s):
		fn = self.view.file_name()
		win = self.view.window()
		if fn and win:
			basedir = os.path.dirname(fn)
			for v in win.views():
				vfn = v.file_name()
				if vfn and os.path.dirname(vfn) == basedir and vfn.endswith('.go'):
					v.run_command('gs_fmt_save')

		# above we do some saves - thus creating a race so push this back to the end of the queue
		def cb(s):
			file_name = self.view.file_name()
			s = GO_PLAY_PAT.sub(r'\1go run\2', s)
			s = s.strip()
			if s and self.change_history:
				self.settings.set('last_command', s)
				sublime.save_settings('GoSublime-GsShell.sublime-settings')

			if GO_SHARE_PAT.match(s):
				s = ''
				host = "play.golang.org"
				warning = 'Are you sure you want to share this file. It will be public on %s' % host
				if not sublime.ok_cancel_dialog(warning):
					return

				try:
					c = httplib.HTTPConnection(host)
					src = self.view.substr(sublime.Region(0, self.view.size()))
					c.request('POST', '/share', src, {'User-Agent': 'GoSublime'})
					s = 'http://%s/p/%s' % (host, c.getresponse().read())
				except Exception as ex:
					s = 'Error: %s' % ex

				self.show_output(s)
				return

			if GO_RUN_PAT.match(s):
				if not file_name:
					# todo: clean this up after the command runs
					f, err = gs.temp_file(suffix='.go', prefix=DOMAIN+'-play.', delete=False)
					if err:
						self.show_output(err)
						return
					else:
						try:
							src = self.view.substr(sublime.Region(0, self.view.size()))
							if isinstance(src, unicode):
								src = src.encode('utf-8')
							f.write(src)
							f.close()
						except Exception as ex:
							self.show_output('Error: %s' % ex)
							return
						file_name = f.name
				s = 'go run "%s"' % file_name
			else:
				gpat = ' *.go'
				if gpat in s:
					fns = []
					for fn in os.listdir(os.path.dirname(self.view.file_name())):
						if fn.endswith('.go') and fn[0] not in ('.', '_') and not fn.endswith('_test.go'):
							fns.append('"%s"' % fn)
					fns = ' '.join(fns)
					if fns:
						s = s.replace(gpat, ' '+fns)
			self.view.window().run_command("exec", { 'kill': True })
			self.view.window().run_command("exec", {
				'shell': True,
				'env': gs.env(),
				'cmd': [s],
				'file_regex': '^(.+\.go):([0-9]+):(?:([0-9]+):)?\s*(.*)',
			})

		sublime.set_timeout(lambda: cb(s), 0)

	def show_output(self, s):
		panel_name = DOMAIN+'-share'
		panel = self.view.window().get_output_panel(panel_name)
		edit = panel.begin_edit()
		try:
			panel.set_read_only(False)
			panel.sel().clear()
			panel.replace(edit, sublime.Region(0, panel.size()), s)
			panel.sel().add(sublime.Region(0, panel.size()))
			panel.set_read_only(True)
		finally:
			panel.end_edit(edit)
		print('%s output: %s' % (DOMAIN, s))
		self.view.window().run_command("show_panel", {"panel": "output.%s" % panel_name})

	def on_change(self, s):
		if self.panel:
			size = self.view.size()
			if s.endswith('\t'):
				lc = self.settings.get('last_command', 'go ')
				s = s.strip()
				if s and s not in ('', 'go'):
					l = []
					for i in self.subcommands:
						if i.startswith(s):
							l.append(i)
					if len(l) == 1:
						s = '%s ' % l[0]
				elif lc:
					s = '%s ' % lc
				edit = self.panel.begin_edit()
				try:
					self.panel.replace(edit, sublime.Region(0, self.panel.size()), s)
				finally:
					self.panel.end_edit(edit)

class GsShellCommand(sublime_plugin.WindowCommand):
	def is_enabled(self):
		view = gs.active_valid_go_view(self.window)
		return bool(view)

	def run(self, prompt="go ", run=""):
		view = gs.active_valid_go_view(self.window)
		if not view:
			gs.notice(DOMAIN, "this not a source.go view")
			return

		run = run.strip()
		p = Prompt(view, run == "")
		if run:
			p.on_done(run)
		else:
			p.panel = self.window.show_input_panel("GsShell", prompt, p.on_done, p.on_change, None)
<|endoftext|>


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

"""" Gaussian Process Regression model

Implementation is based on Algorithm 2.1 (pg. 19) of
"Gaussian Processes for Machine Learning" by Rasmussen and Williams

Simon Batzner
"""
import os
import math
import numpy as np
from scipy.linalg import solve_triangular
from scipy.optimize import minimize

# from dev.two_body import two_body
# from dev.kern_help import get_envs
from env import ChemicalEnvironment, two_body, two_body_py
from otf import parse_qe_input, parse_qe_forces, Structure

def get_outfiles(root_dir, out=True):
    """Find all files matching *.out or *.in.

    :param root_dir: dir to walk from
    :type root_dir: str
    :param out: whether to look for .out or .in files
    :type out: bool
    :return: files in root_dir ending with .out
    :rtype: list<str>
    """
    matching = []

    for root, _, filenames in os.walk(root_dir):
        for filename in filenames:

            if out:
                if filename.endswith('.out'):
                    matching.append(os.path.join(root, filename))

            else:
                if filename.endswith('.in'):
                    matching.append(os.path.join(root, filename))

    return matching

def minus_like_hyp(hyp, gp):
    """Get minus likelihood as a function of hyperparameters

    """
    like = gp.like_hyp(hyp)
    minus_like = -like

    return minus_like

class GaussianProcess:
    """ Gaussian Process Regression Model """

    def __init__(self, kernel):
        """Initialize GP parameters and training data.

        :param kernel: covariance/ kernel function used
        :type kernel: str
        """

        # predictive mean and variance
        self.pred_mean = None
        self.pred_var = None

        # gp kernel and hyperparameters
        self.kernel_type = kernel
        self.length_scale = 1
        self.sigma_n = 1
        self.sigma_f = 0.01

        # quantities used in GPR algorithm
        self.k_mat = None
        self.l_mat = None
        self.alpha = None

        # training set
        self.training_data = []
        self.training_labels = []
        self.training_labels_np = np.empty(0,)

    def kernel(self, env1, env2, d1, d2, sig, ls):
        """Evaluate specified kernel."""

        if self.kernel_type == 'two_body':
            return two_body(env1, env2, d1, d2, sig, ls)

        elif self.kernel_type == 'two_body_py':
            return two_body_py(env1, env2, d1, d2, sig, ls)

        else:
            raise ValueError('{} is not a valid kernel'.format(self.kernel))

    def update_db(self, struc, forces):
        """Given structure and forces, add to training set.

        :param struc: [description]
        :type struc: [type]
        :param forces: [description]
        :type forces: [type]
        """

        noa = len(struc.positions)

        for atom in range(noa):
            env_curr = ChemicalEnvironment(struc, atom)
            forces_curr = forces[atom]

            self.training_data.append(env_curr)
            self.training_labels.append(forces_curr)

        # create numpy array of training labels
        self.training_labels_np = self.force_list_to_np(self.training_labels)

    @staticmethod
    def force_list_to_np(forces):
        forces_np = []
        for m in range(len(forces)):
            for n in range(3):
                forces_np.append(forces[m][n])
        forces_np = np.array(forces_np)
        return forces_np

    def train(self):
        """ Train Gaussian Process model on training data """

        # optimize hyperparameters
        self.opt_hyper()

        # following: Algorithm 2.1 (pg. 19) of
        # "Gaussian Processes for Machine Learning" by Rasmussen and Williams
        self.set_kernel(sigma_f=self.sigma_f,
                        length_scale=self.length_scale,
                        sigma_n=self.sigma_n)

        # get alpha and likelihood
        self.set_alpha()

    def opt_hyper(self):
        """
        Optimize hyperparameters of GP by minimizing minus log likelihood
        """
        # initial guess
        x_0 = np.array([self.sigma_f, self.length_scale, self.sigma_n])

        # nelder-mead optimization
        args = (self)
        res = minimize(minus_like_hyp, x_0, args,
                       method='nelder-mead',
                       options={'xtol': 1e-8, 'disp': True})

        self.sigma_f = res.x[0]
        self.length_scale = res.x[1]
        self.sigma_n = res.x[2]

    def predict(self, x_t, d):
        """Make GP prediction with SE kernel

        :param x_t: data point to predict on
        :type x_t:
        :param d: kernel parameter
        :type d:
        """

        # get kernel vector
        k_v = self.get_kernel_vector(x=x_t, d_1=1)

        # get predictive mean
        self.pred_mean = np.matmul(k_v.transpose(), self.alpha)

        # get predictive variance
        v_vec = solve_triangular(self.l_mat, k_v, lower=True)
        self_kern = self.kernel(x_t, x_t, d, d,
                                self.sigma_f, self.length_scale)
        self.pred_var = self_kern - np.matmul(v_vec.transpose(), v_vec)

    def like_hyp(self, hyp):
        """ Get likelihood as a function of hyperparameters

        :param hyp: hyperparameters to optimize
        :type hyp: list<float>
        :return: likelihood
        :rtype:
        """

        # unpack hyperparameters
        sigma_f = hyp[0]
        length_scale = hyp[1]
        sigma_n = hyp[2]

        # calculate likelihood
        self.set_kernel(sigma_f=sigma_f,
                        length_scale=length_scale,
                        sigma_n=sigma_n)

        self.set_alpha()
        like = self.get_likelihood()

        return like

    def get_likelihood(self):
        """
        Get log marginal likelihood

        :return: likelihood
        :rtype:
        """
        like = -(1 / 2) * \
            np.matmul(self.training_labels_np.transpose(), self.alpha) - \
            np.sum(np.log(np.diagonal(self.l_mat))) - np.log(2 * np.pi) * \
            self.k_mat.shape[1] / 2

        return like

    def set_kernel(self, sigma_f, length_scale, sigma_n):
        """ Compute 3Nx3N noiseless kernel matrix

        :param sigma_f: signal variance
        :type sigma_f: float
        :param length_scale: length scale of the GP
        :type length_scale: float
        :param sigma_n: noise variance
        :type sigma_n: float

        """

        # initialize matrix
        size = len(self.training_data)*3
        k_mat = np.zeros([size, size])

        ds = [1, 2, 3]

        # calculate elements
        for m_index in range(size):
            x_1 = self.training_data[int(math.floor(m_index / 3))]
            d_1 = ds[m_index % 3]

            for n_index in range(m_index, size):
                x_2 = self.training_data[int(math.floor(n_index / 3))]
                d_2 = ds[n_index % 3]

                # calculate kernel
                cov = self.kernel(x_1, x_2, d_1, d_2, sigma_f, length_scale)
                k_mat[m_index, n_index] = cov
                k_mat[n_index, m_index] = cov

        # perform cholesky decomposition and store
        self.l_mat = np.linalg.cholesky(k_mat + sigma_n ** 2 * np.eye(size))
        self.k_mat = k_mat

    def get_kernel_vector(self, x, d_1):
        """ Compute kernel vector

        :param x: data point to compare against kernel matrix
        :type x:
        :param d_1:
        :type d_1:
        :return: kernel vector
        :rtype:
        """
        size = len(self.training_data)
        k_v = np.zeros([size, 1])

        # hard-coded for testing purposes
        d_2 = 1

        for m_index in range(size):
            x_2 = self.training_data[m_index]
            k_v[m_index] = self.kernel(x, x_2, d_1, d_2,
                                       self.sigma_f, self.length_scale)

        return k_v

    def set_alpha(self):
        """ Set weight vector alpha """
        ts1 = solve_triangular(self.l_mat, self.training_labels_np, lower=True)
        self.alpha = solve_triangular(self.l_mat.transpose(), ts1)

# def update_db_from_directory(self):
#     pos, _, _, frcs = [], [], [], []

#     for file in get_outfiles(root_dir=kwargs['root_dir'], out=False):
#         pos = parse_qe_input(file)[0]

#     for file in get_outfiles(root_dir=kwargs['root_dir'], out=True):
#         frcs = parse_qe_forces(file)

#     self.training_data = np.asarray(get_envs(
#                                         pos=pos,
#                                         brav_mat=kwargs['brav_mat'],
#                                         brav_inv=kwargs['brav_inv'],
#                                         vec1=kwargs['vec1'],
#                                         vec2=kwargs['vec2'],
#                                         vec3=kwargs['vec3'],
#                                         cutoff=kwargs['cutoff']))
#     self.training_labels = np.asarray(frcs)

if __name__ == "__main__":

    # create a random test structure
    cell = np.eye(3)
    unique_species = ['B', 'A']
    cutoff = 0.8

    noa = 10
    positions = []
    forces = []
    species = []
    for n in range(noa):
        positions.append(np.random.uniform(-1, 1, 3))
        forces.append(np.random.uniform(-1, 1, 3))
        species.append(unique_species[np.random.randint(0, 2)])

    test_structure = Structure(cell, species, positions, cutoff)
    gaussian = GaussianProcess(kernel='two_body')

    # test update_db
    gaussian.update_db(test_structure, forces)
    assert(len(gaussian.training_data) == noa)
    assert(len(gaussian.training_data) == len(gaussian.training_data))
    assert(len(gaussian.training_labels_np) == len(gaussian.training_data * 3))

    # test get_kernel
    gaussian.set_kernel(sigma_f=1, length_scale=1, sigma_n=0.1)
    print(gaussian.k_mat.shape)
    print(gaussian.l_mat.shape)

    # # set up a few test environments
    # n_train = 10
    # cell = np.eye(3)
    # cutoff = np.linalg.norm(np.array([0.5, 0.5, 0.5])) + 0.001
    # train_envs = []
    # species = ['B', 'A']
    # atom = 0
    # train_forces = []

    # for i in range(n_train):

    #     # generate random positions and build chem env
    #     positions = [np.array([np.random.rand() * 0.5,
    #                            np.random.rand() * 0.5,
    #                            np.random.rand() * 0.5]),
    #                  np.array([np.random.rand() * 0.5,
    #                            np.random.rand() * 0.5,
    #                            np.random.rand() * 0.5])]

    #     test_structure = Structure(cell, species, positions, cutoff)
    #     train_envs.append(ChemicalEnvironment(test_structure, atom))

    #     # generate random force vectors
    #     train_forces.append(np.array([np.random.rand(),
    #                         np.random.rand(),
    #                         np.random.rand()]))

    # # build gp and train
    # gaussian = GaussianProcess(kernel='two_body_py')
    # gaussian.init_db(init_type='data', envs=train_envs, forces=train_forces)
    # # gaussian.train()
<commit_msg>add gp tests<commit_after>#!/usr/bin/env python3

"""" Gaussian Process Regression model

Implementation is based on Algorithm 2.1 (pg. 19) of
"Gaussian Processes for Machine Learning" by Rasmussen and Williams

Simon Batzner
"""
import os
import math
import time
import numpy as np
from scipy.linalg import solve_triangular
from scipy.optimize import minimize

# from dev.two_body import two_body
# from dev.kern_help import get_envs
from env import ChemicalEnvironment, two_body, two_body_py
from otf import parse_qe_input, parse_qe_forces, Structure

def minus_like_hyp(hyp, gp):
    """Get minus likelihood as a function of hyperparameters

    """
    like = gp.like_hyp(hyp)
    minus_like = -like

    return minus_like

class GaussianProcess:
    """ Gaussian Process Regression Model """

    def __init__(self, kernel):
        """Initialize GP parameters and training data.

        :param kernel: covariance/ kernel function used
        :type kernel: str
        """

        # gp kernel and hyperparameters
        self.kernel_type = kernel
        self.length_scale = 1
        self.sigma_n = 1
        self.sigma_f = 0.01

        # quantities used in GPR algorithm
        self.k_mat = None
        self.l_mat = None
        self.alpha = None

        # training set
        self.training_data = []
        self.training_labels = []
        self.training_labels_np = np.empty(0,)

    def kernel(self, env1, env2, d1, d2, sig, ls):
        """Evaluate specified kernel."""

        if self.kernel_type == 'two_body':
            return two_body(env1, env2, d1, d2, sig, ls)

        elif self.kernel_type == 'two_body_py':
            return two_body_py(env1, env2, d1, d2, sig, ls)

        else:
            raise ValueError('{} is not a valid kernel'.format(self.kernel))

    def update_db(self, struc, forces):
        """Given structure and forces, add to training set.

        :param struc: [description]
        :type struc: [type]
        :param forces: [description]
        :type forces: [type]
        """

        noa = len(struc.positions)

        for atom in range(noa):
            env_curr = ChemicalEnvironment(struc, atom)
            forces_curr = forces[atom]

            self.training_data.append(env_curr)
            self.training_labels.append(forces_curr)

        # create numpy array of training labels
        self.training_labels_np = self.force_list_to_np(self.training_labels)

    @staticmethod
    def force_list_to_np(forces):
        forces_np = []
        for m in range(len(forces)):
            for n in range(3):
                forces_np.append(forces[m][n])
        forces_np = np.array(forces_np)
        return forces_np

    def train(self):
        """ Train Gaussian Process model on training data """

        # optimize hyperparameters
        self.opt_hyper()

        # following: Algorithm 2.1 (pg. 19) of
        # "Gaussian Processes for Machine Learning" by Rasmussen and Williams
        self.set_kernel(sigma_f=self.sigma_f,
                        length_scale=self.length_scale,
                        sigma_n=self.sigma_n)

        # get alpha and likelihood
        self.set_alpha()

    def opt_hyper(self):
        """
        Optimize hyperparameters of GP by minimizing minus log likelihood
        """
        # initial guess
        x_0 = np.array([self.sigma_f, self.length_scale, self.sigma_n])

        # nelder-mead optimization
        args = (self)
        res = minimize(minus_like_hyp, x_0, args,
                       method='nelder-mead',
                       options={'xtol': 1e-8, 'disp': False})

        self.sigma_f = res.x[0]
        self.length_scale = res.x[1]
        self.sigma_n = res.x[2]

    def predict(self, x_t, d):
        """Make GP prediction with SE kernel

        :param x_t: data point to predict on
        :type x_t:
        :param d: kernel parameter
        :type d:
        """

        # get kernel vector
        k_v = self.get_kernel_vector(x_t, d)

        # get predictive mean
        pred_mean = np.matmul(k_v, self.alpha)

        # get predictive variance
        v_vec = solve_triangular(self.l_mat, k_v, lower=True)
        self_kern = self.kernel(x_t, x_t, d, d,
                                self.sigma_f, self.length_scale)
        pred_var = self_kern - np.matmul(v_vec, v_vec)

        return pred_mean, pred_var

    def like_hyp(self, hyp):
        """ Get likelihood as a function of hyperparameters

        :param hyp: hyperparameters to optimize
        :type hyp: list<float>
        :return: likelihood
        :rtype:
        """

        # unpack hyperparameters
        sigma_f = hyp[0]
        length_scale = hyp[1]
        sigma_n = hyp[2]

        # calculate likelihood
        self.set_kernel(sigma_f=sigma_f,
                        length_scale=length_scale,
                        sigma_n=sigma_n)

        self.set_alpha()
        like = self.get_likelihood()

        return like

    def get_likelihood(self):
        """
        Get log marginal likelihood

        :return: likelihood
        :rtype:
        """
        like = -(1 / 2) * \
            np.matmul(self.training_labels_np.transpose(), self.alpha) - \
            np.sum(np.log(np.diagonal(self.l_mat))) - np.log(2 * np.pi) * \
            self.k_mat.shape[1] / 2

        return like

    def set_kernel(self, sigma_f, length_scale, sigma_n):
        """ Compute 3Nx3N noiseless kernel matrix

        :param sigma_f: signal variance
        :type sigma_f: float
        :param length_scale: length scale of the GP
        :type length_scale: float
        :param sigma_n: noise variance
        :type sigma_n: float

        """

        # initialize matrix
        size = len(self.training_data)*3
        k_mat = np.zeros([size, size])

        ds = [1, 2, 3]

        # calculate elements
        for m_index in range(size):
            x_1 = self.training_data[int(math.floor(m_index / 3))]
            d_1 = ds[m_index % 3]

            for n_index in range(m_index, size):
                x_2 = self.training_data[int(math.floor(n_index / 3))]
                d_2 = ds[n_index % 3]

                # calculate kernel
                cov = self.kernel(x_1, x_2, d_1, d_2, sigma_f, length_scale)
                k_mat[m_index, n_index] = cov
                k_mat[n_index, m_index] = cov

        # perform cholesky decomposition and store
        self.l_mat = np.linalg.cholesky(k_mat + sigma_n ** 2 * np.eye(size))
        self.k_mat = k_mat

    def get_kernel_vector(self, x, d_1):
        """ Compute kernel vector

        :param x: data point to compare against kernel matrix
        :type x:
        :param d_1:
        :type d_1:
        :return: kernel vector
        :rtype:
        """
        ds = [1, 2, 3]
        size = len(self.training_data)*3
        k_v = np.zeros(size,)

        for m_index in range(size):
            x_2 = self.training_data[int(math.floor(m_index/3))]
            d_2 = ds[m_index % 3]
            k_v[m_index] = self.kernel(x, x_2, d_1, d_2,
                                       self.sigma_f, self.length_scale)

        return k_v

    def set_alpha(self):
        """ Set weight vector alpha """
        ts1 = solve_triangular(self.l_mat, self.training_labels_np, lower=True)
        self.alpha = solve_triangular(self.l_mat.transpose(), ts1)

# # append data from directory
# # likely won't be used in this paper, so not necessary now

# def get_outfiles(root_dir, out=True):
#     """Find all files matching *.out or *.in.

#     :param root_dir: dir to walk from
#     :type root_dir: str
#     :param out: whether to look for .out or .in files
#     :type out: bool
#     :return: files in root_dir ending with .out
#     :rtype: list<str>
#     """
#     matching = []

#     for root, _, filenames in os.walk(root_dir):
#         for filename in filenames:

#             if out:
#                 if filename.endswith('.out'):
#                     matching.append(os.path.join(root, filename))

#             else:
#                 if filename.endswith('.in'):
#                     matching.append(os.path.join(root, filename))

#     return matching

# def update_db_from_directory(self):
#     pos, _, _, frcs = [], [], [], []

#     for file in get_outfiles(root_dir=kwargs['root_dir'], out=False):
#         pos = parse_qe_input(file)[0]

#     for file in get_outfiles(root_dir=kwargs['root_dir'], out=True):
#         frcs = parse_qe_forces(file)

#     self.training_data = np.asarray(get_envs(
#                                         pos=pos,
#                                         brav_mat=kwargs['brav_mat'],
#                                         brav_inv=kwargs['brav_inv'],
#                                         vec1=kwargs['vec1'],
#                                         vec2=kwargs['vec2'],
#                                         vec3=kwargs['vec3'],
#                                         cutoff=kwargs['cutoff']))
#     self.training_labels = np.asarray(frcs)

if __name__ == "__main__":

    # create a random test structure
    def get_random_structure(cell, unique_species, cutoff, noa):
        positions = []
        forces = []
        species = []
        for n in range(noa):
            positions.append(np.random.uniform(-1, 1, 3))
            forces.append(np.random.uniform(-1, 1, 3))
            species.append(unique_species[np.random.randint(0, 2)])

        test_structure = Structure(cell, species, positions, cutoff)

        return test_structure, forces

    cell = np.eye(3)
    unique_species = ['B', 'A']
    cutoff = 0.8
    noa = 10
    test_structure, forces = \
        get_random_structure(cell, unique_species, cutoff, noa)

    # create test point
    test_structure_2, _ = get_random_structure(cell, unique_species, cutoff,
                                               noa)
    test_pt = ChemicalEnvironment(test_structure_2, 0)

    # test update_db
    gaussian = GaussianProcess(kernel='two_body')
    gaussian.update_db(test_structure, forces)
    assert(len(gaussian.training_data) == noa)
    assert(len(gaussian.training_data) == len(gaussian.training_data))
    assert(len(gaussian.training_labels_np) == len(gaussian.training_data * 3))

    # test get_kernel
    gaussian.set_kernel(sigma_f=1, length_scale=1, sigma_n=0.1)
    db_pts = 3 * len(gaussian.training_data)
    assert(gaussian.k_mat.shape == (db_pts, db_pts))

    # test get_alpha
    gaussian.set_alpha()
    assert(gaussian.alpha.shape == (db_pts,))

    # test get_kernel_vector
    assert(gaussian.get_kernel_vector(test_pt, 1).shape ==
           (db_pts,))

    # test get_likelihood and like_hyp
    like = gaussian.get_likelihood()

    def like_performance(its, kernel_type, hyp, gp):
        # set kernel type
        gp.kernel_type = kernel_type

        # warm up jit
        like_hyp = gp.like_hyp(hyp)

        # test performance
        time0 = time.time()
        for n in range(its):
            like_hyp = gp.like_hyp(hyp)
        time1 = time.time()
        like_time = (time1 - time0) / its

        return like_time, like_hyp

    its = 10
    jit_kern = 'two_body'
    py_kern = 'two_body_py'
    hyp = np.array([1, 1, 0.1])
    jit_time, jit_like = like_performance(its, jit_kern, hyp, gaussian)
    py_time, py_like = like_performance(its, py_kern, hyp, gaussian)
    assert(py_time / jit_time > 1)
    assert(round(py_like, 8) == round(jit_like, 8))
    assert(round(like, 8) == round(jit_like, 8))

    # test train
    gaussian.kernel_type = 'two_body'
    gaussian.train()

    # test predict
    print(gaussian.predict(test_pt, 1))
<|endoftext|>


<commit_before># GYP file to build the "gm" (golden master) executable.
#
# To build and run on Linux:
#  ./gyp_skia gm.gyp && make
#  out/Debug/gm -r ../gm/base-linux
#
# Building on other platforms not tested yet.
#
{
  'includes': [
    'apptype_console.gypi',
    'target_defaults.gypi',
  ],
  'targets': [
    {
      'target_name': 'gm',
      'type': 'executable',
      'sources': [
        '../gm/bitmapfilters.cpp',
        '../gm/blurs.cpp',
        '../gm/filltypes.cpp',
        '../gm/gradients.cpp',
        '../gm/nocolorbleed.cpp',
        '../gm/pathfill.cpp',
        '../gm/points.cpp',
        '../gm/poly2poly.cpp',
        '../gm/shadows.cpp',
        '../gm/shapes.cpp',
        '../gm/strokerects.cpp',
        '../gm/tilemodes.cpp',
        '../gm/xfermodes.cpp',
        '../gm/shadertext.cpp',
        '../gm/complexclip.cpp',
        '../gm/gmmain.cpp',
      ],
      'dependencies': [
        'core.gyp:core',
        'effects.gyp:effects',
        'gpu.gyp:gr',
        'gpu.gyp:skgr',
        'images.gyp:images',
        'utils.gyp:utils',
      ],
    },
  ],
}

# Local Variables:
# tab-width:2
# indent-tabs-mode:nil
# End:
# vim: set expandtab tabstop=2 shiftwidth=2:
<commit_msg>add strokes.cpp<commit_after># GYP file to build the "gm" (golden master) executable.
#
# To build and run on Linux:
#  ./gyp_skia gm.gyp && make
#  out/Debug/gm -r ../gm/base-linux
#
# Building on other platforms not tested yet.
#
{
  'includes': [
    'apptype_console.gypi',
    'target_defaults.gypi',
  ],
  'targets': [
    {
      'target_name': 'gm',
      'type': 'executable',
      'sources': [
        '../gm/bitmapfilters.cpp',
        '../gm/blurs.cpp',
        '../gm/filltypes.cpp',
        '../gm/gradients.cpp',
        '../gm/nocolorbleed.cpp',
        '../gm/pathfill.cpp',
        '../gm/points.cpp',
        '../gm/poly2poly.cpp',
        '../gm/shadows.cpp',
        '../gm/shapes.cpp',
        '../gm/strokerects.cpp',
        '../gm/strokes.cpp',
        '../gm/tilemodes.cpp',
        '../gm/xfermodes.cpp',
        '../gm/shadertext.cpp',
        '../gm/complexclip.cpp',
        '../gm/gmmain.cpp',
      ],
      'dependencies': [
        'core.gyp:core',
        'effects.gyp:effects',
        'gpu.gyp:gr',
        'gpu.gyp:skgr',
        'images.gyp:images',
        'utils.gyp:utils',
      ],
    },
  ],
}

# Local Variables:
# tab-width:2
# indent-tabs-mode:nil
# End:
# vim: set expandtab tabstop=2 shiftwidth=2:
<|endoftext|>


<commit_before># A simple hangman game programmed in Python 3
#
# You need to have Python 3 installed on your system to be able to run this game.
# Tested on Python 3.3.2.
#
# The game prompts the user to enter a letter to start guessing a word.
# 11 incorrect guesses are allowed, after the 11th, hangman is hanged and the game is over.
# You can enable/disable audio output (see output function)
#
# TODO
# - retrieve words from a Wikipedia page
# - build hangman (ASCII graphics)
# - transform umlauts (ü -> ue)
# - delay for say? (so as to display text earlier)
# - switch between EN and DE words (dict file) ... parameters!
# - hint system (which letter is likely to be part of the word)
# - alternatives for say for other platforms (linux, windows)
# - AI
#
# Further ideas:
# - let user guess the entire word
# - allow phrases and words with hyphens
# - multiplayer hangman

import random # module for randomisation
import subprocess # enable command line functions

def stringtogether(thislist):
	"""return elements in a list as string"""
	returnstring = ""
	for element in thislist:
		returnstring += element
	return returnstring

def output(text,voice=True,ending='\n',spell=False):
	"""print out text and if voice is true, say text"""
	print(text,end=ending)
	if voice:
		if spell:
			# say cannot say a single underscore/dash/... :( -> needs to be spelled out
			for char in list(text):
				if char == ".":
					subprocess.call(["say", "dot"])
				else:
					subprocess.call(["say", char])
		else:
			subprocess.call(["say", text])

alphabet = "abcdefghijklmnopqrstuvwxyz"
mywords = [] # empty list

try:
	myfile = open("de-en.dict")
	for line in myfile:
		myword = line.split()[0]
		for letter in myword.lower():
			if len(myword) < 5:
				break
			if not letter in alphabet:
				break
		else:
			mywords.append(myword)

	myfile.close()
except:
	mywords = ["cherry", "summer", "winter", "programming", "hydrogen", "Saturday",
			"unicorn", "magic", "artichoke", "juice", "hacker", "python", "Neverland",
			"baking", "sherlock", "troll", "batman", "japan", "pastries", "Cairo",
			"Vienna", "raindrop", "waves", "diving", "Malta", "cupcake", "ukulele"]

print(len(mywords))
sound = False

print("Starting sound check:")
# using try to test existence of say subprocess
try:
	subprocess.call(["say", "soundcheck, testing, 1, 2, 3"])
	subprocess.call(["say", "If you hear the voice, please press yes and the enter key."])
except:
	print("Please install the programm 'say' to use audio output (or contact your system administrator).")
	print("Using this program will still work by the way, only without sound.")
else:
	print("It seems you have 'say' installed which makes audio output possible.")
	print("Could you hear your computer say 'soundcheck, testing, 1, 2, 3'?")
	answer = input("Please type yes or no (and press enter): ")
	if len(answer) > 0 and answer.lower()[0] == 'y':
		sound = True
finally:
	print("Soundcheck completed, you're ready to go!")

output("Let's play hangman: guess the word!", voice=sound)

theword = random.choice(mywords)

possibletries = 11 # 11 incorrect guesses are allowed
placeholder = list("."*len(theword)) # a list consisting of placeholder characters
incorrectguesses = '' # string for incorrectly guessed letters
correctguesses = ''
tryword = 'tries'

output(stringtogether(placeholder), voice=False) # show blank word
if sound:
	subprocess.call(["say", "The word you're looking for has {} letters".format(len(theword))])

while possibletries > 0:

	output("Please pick a letter: ", ending='', voice=sound)
	pickedletter = input().upper()

	if len(pickedletter) > 0:
		pickedletter = pickedletter[0]

	if not pickedletter.isalpha() : # don't allow digits, special characters
		output("Sorry, but that was not a letter! Try again.".format(pickedletter), voice=sound)
		continue

	if pickedletter in incorrectguesses: # no multiple guesses of same letter
		output("You already guessed the letter '{}'!".format(pickedletter), voice=sound)
		continue

	if pickedletter in correctguesses:
		output("You already picked {}!".format(pickedletter.upper()), voice=sound)
		continue

	if pickedletter in theword.upper(): # guess was correct

		# replace placeholder letters with correctly guessed ones
		letterposition = 0
		for letterexists in theword.upper():
			if letterexists == pickedletter:
				placeholder[letterposition] = theword.upper()[letterposition]
			letterposition += 1

		output("You guessed correctly, well done!", voice=sound)
		correctguesses += pickedletter

	else: # guess was incorrect
		incorrectguesses += pickedletter
		possibletries -= 1

		output("Sorry, '{}' is an incorrect guess!".format(pickedletter.upper()), voice=sound)

		if (incorrectguesses != '') and (possibletries > 0):
			if possibletries == 1:
				tryword = 'try'

			output("You have {} {} left.".format(possibletries,tryword), voice=sound)
			output("Incorrectly guessed letters so far: ", ending='', voice=sound)
			output(incorrectguesses, ending='', spell=True, voice=sound)
			print(".")

	if not "." in placeholder:
		output("We have a winner! Thanks for playing. :)", voice=sound)
		break

	if placeholder == list("."*len(theword)):
		output(stringtogether(placeholder), voice=False) # show blank word
		if sound:
			subprocess.call(["say", "The word you're looking for has {} letters".format(len(theword))])
	else:
		output("The word is: ", ending='', voice=sound)
		output(stringtogether(placeholder),spell=True, voice=sound)

else:
	output("\nGame over. :( ", voice=sound)

output("The word you were looking for was: " +theword+ ".", voice=sound)
<commit_msg>check for parameters (nosound, en|de)<commit_after># A simple hangman game programmed in Python 3
#
# You need to have Python 3 installed on your system to be able to run this game.
# Tested on Python 3.3.2.
#
# The game prompts the user to enter a letter to start guessing a word.
# 11 incorrect guesses are allowed, after the 11th, hangman is hanged and the game is over.
# You can enable/disable audio output (see output function)
#
# TODO
# - retrieve words from a Wikipedia page
# - build hangman (ASCII graphics)
# - transform umlauts (ü -> ue)
# - delay for say? (so as to display text earlier)
# - switch between EN and DE words (dict file) ... parameters!
# - hint system (which letter is likely to be part of the word)
# - alternatives for say for other platforms (linux, windows)
# - AI
# - put licence into comments (check underscore blahfoo)
#
# Further ideas:
# - let user guess the entire word
# - allow phrases and words with hyphens
# - multiplayer hangman

'''Hangman - a word-guessing game.

parameters:
-nosound ... play the game without sound
-en|de ... play the game with English/German words'''

import random # module for randomisation
import subprocess # enable command line functions
import sys # module for parameters

def stringtogether(thislist):
	"""return elements in a list as string"""
	returnstring = ""
	for element in thislist:
		returnstring += element
	return returnstring

def output(text,voice=True,ending='\n',spell=False):
	"""print out text and if voice is true, say text"""
	print(text,end=ending)
	if voice:
		if spell:
			# say cannot say a single underscore/dash/... :( -> needs to be spelled out
			for char in list(text):
				if char == ".":
					subprocess.call(["say", "dot"])
				else:
					subprocess.call(["say", char])
		else:
			subprocess.call(["say", text])

# start of the program

sound = True
wordlanguage = ""
# are there any parameters?
if len(sys.argv) > 1:
	# begin loop
	argumentlist = sys.argv[1:] # list includes all arguments but not the filename
	for argument.lower() in argumentlist:
		if (argument == "-nosound") or (argument == "nosound"):
			sound = False
			continue
		if (argument == "en") or (argument == "-en") or (argument == "de") or (argument == "-de"):
			if "en" in argument:
				if wordlanguage != '':
					print("You entered more than one parameters for language - this is not possible!")
					print("The Hangman program will now default to English.")
				wordlanguage = "en"
			else:
				if wordlanguage != '':
					print("You entered more than one parameters for language - this is not possible!")
					print("The Hangman program will now default to German.")
				wordlanguage = "de"

if wordlanguage == '':
	wordlanguage = 'en'

alphabet = "abcdefghijklmnopqrstuvwxyz"
mywords = [] # empty list

try:
	myfile = open("de-en.dict")
	for line in myfile:
		myword = line.split()[0]
		for letter in myword.lower():
			if len(myword) < 5:
				break
			if not letter in alphabet:
				break
		else:
			mywords.append(myword)

	myfile.close()
except:
	mywords = ["cherry", "summer", "winter", "programming", "hydrogen", "Saturday",
			"unicorn", "magic", "artichoke", "juice", "hacker", "python", "Neverland",
			"baking", "sherlock", "troll", "batman", "japan", "pastries", "Cairo",
			"Vienna", "raindrop", "waves", "diving", "Malta", "cupcake", "ukulele"]

print(len(mywords))

if sound:
	print("Starting sound check:")
	# using try to test existence of say subprocess
	try:
		subprocess.call(["say", "soundcheck, testing, 1, 2, 3"])
		subprocess.call(["say", "If you hear the voice, please press yes and the enter key."])
	except:
		print("Please install the programm 'say' to use audio output (or contact your system administrator).")
		print("Using this program will still work by the way, only without sound.")
		sound = False
	else:
		print("It seems you have 'say' installed which makes audio output possible.")
		print("Could you hear your computer say 'soundcheck, testing, 1, 2, 3'?")
		answer = input("Please type yes or no (and press enter): ")
		if len(answer) > 0 and answer.lower()[0] == 'y':
			sound = True
		else:
			sound = False
	finally:
		print("Soundcheck completed, you're ready to go!")

output("Let's play hangman: guess the word!", voice=sound)

theword = random.choice(mywords)

possibletries = 11 # 11 incorrect guesses are allowed
placeholder = list("."*len(theword)) # a list consisting of placeholder characters
incorrectguesses = '' # string for incorrectly guessed letters
correctguesses = ''
tryword = 'tries'

output(stringtogether(placeholder), voice=False) # show blank word
if sound:
	subprocess.call(["say", "The word you're looking for has {} letters".format(len(theword))])

while possibletries > 0:

	output("Please pick a letter: ", ending='', voice=sound)
	pickedletter = input().upper()

	if len(pickedletter) > 0:
		pickedletter = pickedletter[0]

	if not pickedletter.isalpha() : # don't allow digits, special characters
		output("Sorry, but that was not a letter! Try again.".format(pickedletter), voice=sound)
		continue

	if pickedletter in incorrectguesses: # no multiple guesses of same letter
		output("You already guessed the letter '{}'!".format(pickedletter), voice=sound)
		continue

	if pickedletter in correctguesses:
		output("You already picked {}!".format(pickedletter.upper()), voice=sound)
		continue

	if pickedletter in theword.upper(): # guess was correct

		# replace placeholder letters with correctly guessed ones
		letterposition = 0
		for letterexists in theword.upper():
			if letterexists == pickedletter:
				placeholder[letterposition] = theword.upper()[letterposition]
			letterposition += 1

		output("You guessed correctly, well done!", voice=sound)
		correctguesses += pickedletter

	else: # guess was incorrect
		incorrectguesses += pickedletter
		possibletries -= 1

		output("Sorry, '{}' is an incorrect guess!".format(pickedletter.upper()), voice=sound)

		if (incorrectguesses != '') and (possibletries > 0):
			if possibletries == 1:
				tryword = 'try'

			output("You have {} {} left.".format(possibletries,tryword), voice=sound)
			output("Incorrectly guessed letters so far: ", ending='', voice=sound)
			output(incorrectguesses, ending='', spell=True, voice=sound)
			print(".")

	if not "." in placeholder:
		output("We have a winner! Thanks for playing. :)", voice=sound)
		break

	if placeholder == list("."*len(theword)):
		output(stringtogether(placeholder), voice=False) # show blank word
		if sound:
			subprocess.call(["say", "The word you're looking for has {} letters".format(len(theword))])
	else:
		output("The word is: ", ending='', voice=sound)
		output(stringtogether(placeholder),spell=True, voice=sound)

else:
	output("\nGame over. :( ", voice=sound)

output("The word you were looking for was: " +theword+ ".", voice=sound)
<|endoftext|>