<commit_before># -*- coding: utf-8 -*-
import os
import abc
import media.monitor.pure   as mmp
import media.monitor.owners as owners
from media.monitor.pure       import LazyProperty
from media.monitor.metadata   import Metadata
from media.monitor.log        import Loggable
from media.monitor.exceptions import BadSongFile

class PathChannel(object):
    """
    Simple struct to hold a 'signal' string and a related 'path'. Basically
    used as a named tuple
    """
    def __init__(self, signal, path):
        self.signal = signal
        self.path   = path

# TODO : Move this to it's file. Also possible unsingleton and use it as a
# simple module just like m.m.owners
class EventRegistry(object):
    """
    This class's main use is to keep track all events with a cookie attribute.
    This is done mainly because some events must be 'morphed' into other events
    because we later detect that they are move events instead of delete events.
    """
    registry = {}
    @staticmethod
    def register(evt): EventRegistry.registry[evt.cookie] = evt
    @staticmethod
    def unregister(evt): del EventRegistry.registry[evt.cookie]
    @staticmethod
    def registered(evt): return evt.cookie in EventRegistry.registry
    @staticmethod
    def matching(evt):
        event = EventRegistry.registry[evt.cookie]
        # Want to disallow accessing the same event twice
        EventRegistry.unregister(event)
        return event
    def __init__(self,*args,**kwargs):
        raise Exception("You can instantiate this class. Must only use class \
                         methods")

class EventProxy(object):
    """
    A container object for instances of BaseEvent (or it's subclasses) used for
    event contractor
    """
    def __init__(self, orig_evt):
        self.orig_evt   = orig_evt
        self.evt        = orig_evt
        self.reset_hook()
        if hasattr(orig_evt, 'path'): self.path = orig_evt.path

    def set_pack_hook(self, l):
        self._pack_hook = l

    def reset_hook(self):
        self._pack_hook = lambda : None

    def run_hook(self):
        self._pack_hook()

    def safe_pack(self):
        self.run_hook()
        # make sure that cleanup hook is never called twice for the same event
        self.reset_hook()
        return self.evt.safe_pack()

    def merge_proxy(self, proxy):
        self.evt = proxy.evt

    def is_event(self, real_event):
        return isinstance(self.evt, real_event)

    def same_event(self, proxy):
        return self.evt.__class__ == proxy.evt.__class__

class HasMetaData(object):
    """
    Any class that inherits from this class gains the metadata attribute that
    loads metadata from the class's 'path' attribute. This is done lazily so
    there is no performance penalty to inheriting from this and subsequent
    calls to metadata are cached
    """
    __metaclass__ = abc.ABCMeta
    @LazyProperty
    def metadata(self): return Metadata(self.path)

class BaseEvent(Loggable):
    __metaclass__ = abc.ABCMeta
    def __init__(self, raw_event):
        # TODO : clean up this idiotic hack
        # we should use keyword constructors instead of this behaviour checking
        # bs to initialize BaseEvent
        if hasattr(raw_event,"pathname"):
            self._raw_event = raw_event
            self.path = os.path.normpath(raw_event.pathname)
        else: self.path = raw_event
        self.owner = owners.get_owner(self.path)
        self._pack_hook = lambda: None # no op
        # into another event

    def reset_hook(self):
        """
        Resets the hook that is called after an event is packed. Before
        resetting the hook we execute it to make sure that whatever cleanup
        operations were queued are executed.
        """
        self._pack_hook()
        self._pack_hook = lambda: None

    def exists(self): return os.path.exists(self.path)

    @LazyProperty
    def cookie(self): return getattr( self._raw_event, 'cookie', None )

    def __str__(self):
        return "Event(%s). Path(%s)" % ( self.path, self.__class__.__name__)

    def add_safe_pack_hook(self,k):
        """
        adds a callable object (function) that will be called after the event
        has been "safe_packed"
        """
        self._pack_hook = k

    def proxify(self):
        return EventProxy(self)

    # As opposed to unsafe_pack...
    def safe_pack(self):
        """
        returns exceptions instead of throwing them to be consistent with
        events that must catch their own BadSongFile exceptions since generate
        a set of exceptions instead of a single one
        """
        try:
            self._pack_hook()
            ret = self.pack()
            # Remove owner of this file only after packing. Otherwise packing
            # will not serialize the owner correctly into the airtime request
            owners.remove_file_owner(self.path)
            return ret
        except BadSongFile as e: return [e]

    # nothing to see here, please move along
    def morph_into(self, evt):
        self.logger.info("Morphing %s into %s" % ( str(self), str(evt) ) )
        self._raw_event   = evt._raw_event
        self.path         = evt.path
        self.__class__    = evt.__class__
        # Clean up old hook and transfer the new events hook
        self.reset_hook()
        self.add_safe_pack_hook( evt._pack_hook )
        return self

    def assign_owner(self,req):
        """
        Packs self.owner to req if the owner is valid. I.e. it's not -1. This
        method is used by various events that would like to pass owner as a
        parameter. NewFile for example.
        """
        if self.owner != -1: req['MDATA_KEY_OWNER_ID'] = self.owner

class FakePyinotify(object):
    """
    sometimes we must create our own pyinotify like objects to
    instantiate objects from the classes below whenever we want to turn
    a single event into multiple events
    """
    def __init__(self, path): self.pathname = path

class OrganizeFile(BaseEvent, HasMetaData):
    """
    The only kind of event that does support the pack protocol. It's used
    internally with mediamonitor to move files in the organize directory.
    """
    def __init__(self, *args, **kwargs):
        super(OrganizeFile, self).__init__(*args, **kwargs)
    def pack(self):
        raise AttributeError("You can't send organize events to airtime!!!")

class NewFile(BaseEvent, HasMetaData):
    """
    NewFile events are the only events that contain MDATA_KEY_OWNER_ID metadata
    in them.
    """
    def __init__(self, *args, **kwargs):
        super(NewFile, self).__init__(*args, **kwargs)
    def pack(self):
        """
        packs turns an event into a media monitor request
        """
        req_dict = self.metadata.extract()
        req_dict['mode'] = u'create'
        self.assign_owner(req_dict)
        req_dict['MDATA_KEY_FILEPATH'] = unicode( self.path )
        return [req_dict]

class DeleteFile(BaseEvent):
    """
    DeleteFile event only contains the path to be deleted. No other metadata
    can be or is included.  (This is because this event is fired after the
    deletion occurs).
    """
    def __init__(self, *args, **kwargs):
        super(DeleteFile, self).__init__(*args, **kwargs)
    def pack(self):
        req_dict = {}
        req_dict['mode'] = u'delete'
        req_dict['MDATA_KEY_FILEPATH'] = unicode( self.path )
        return [req_dict]

class MoveFile(BaseEvent, HasMetaData):
    """
    Path argument should be the new path of the file that was moved
    """
    def __init__(self, *args, **kwargs):
        super(MoveFile, self).__init__(*args, **kwargs)
    def old_path(self):
        return self._raw_event.src_pathname
    def pack(self):
        req_dict                            = {}
        req_dict['mode']                    = u'moved'
        req_dict['MDATA_KEY_ORIGINAL_PATH'] = self.old_path()
        req_dict['MDATA_KEY_FILEPATH']      = unicode( self.path )
        req_dict['MDATA_KEY_MD5'] = self.metadata.extract()['MDATA_KEY_MD5']
        return [req_dict]

class ModifyFile(BaseEvent, HasMetaData):
    def __init__(self, *args, **kwargs):
        super(ModifyFile, self).__init__(*args, **kwargs)
    def pack(self):
        req_dict = self.metadata.extract()
        req_dict['mode'] = u'modify'
        # path to directory that is to be removed
        req_dict['MDATA_KEY_FILEPATH'] = unicode( self.path )
        return [req_dict]

def map_events(directory, constructor):
    """
    Walks 'directory' and creates an event using 'constructor'. Returns a list
    of the constructed events.
    """
    # -unknown-path should not appear in the path here but more testing
    # might be necessary
    for f in mmp.walk_supported(directory, clean_empties=False):
        try:
            for e in constructor( FakePyinotify(f) ).pack(): yield e
        except BadSongFile as e: yield e

class DeleteDir(BaseEvent):
    """
    A DeleteDir event unfolds itself into a list of DeleteFile events for every
    file in the directory.
    """
    def __init__(self, *args, **kwargs):
        super(DeleteDir, self).__init__(*args, **kwargs)
    def pack(self):
        return map_events( self.path, DeleteFile )

class MoveDir(BaseEvent):
    """
    A MoveDir event unfolds itself into a list of MoveFile events for every
    file in the directory.
    """
    def __init__(self, *args, **kwargs):
        super(MoveDir, self).__init__(*args, **kwargs)
    def pack(self):
        return map_events( self.path, MoveFile )

class DeleteDirWatch(BaseEvent):
    """
    Deleting a watched directory is different from deleting any other
    directory.  Hence we must have a separate event to handle this case
    """
    def __init__(self, *args, **kwargs):
        super(DeleteDirWatch, self).__init__(*args, **kwargs)
    def pack(self):
        req_dict = {}
        req_dict['mode']               = u'delete_dir'
        req_dict['MDATA_KEY_FILEPATH'] = unicode( self.path + "/" )
        return [req_dict]

<commit_msg>Added todo to remove methods later<commit_after># -*- coding: utf-8 -*-
import os
import abc
import media.monitor.pure   as mmp
import media.monitor.owners as owners
from media.monitor.pure       import LazyProperty
from media.monitor.metadata   import Metadata
from media.monitor.log        import Loggable
from media.monitor.exceptions import BadSongFile

class PathChannel(object):
    """
    Simple struct to hold a 'signal' string and a related 'path'. Basically
    used as a named tuple
    """
    def __init__(self, signal, path):
        self.signal = signal
        self.path   = path

# TODO : Move this to it's file. Also possible unsingleton and use it as a
# simple module just like m.m.owners
class EventRegistry(object):
    """
    This class's main use is to keep track all events with a cookie attribute.
    This is done mainly because some events must be 'morphed' into other events
    because we later detect that they are move events instead of delete events.
    """
    registry = {}
    @staticmethod
    def register(evt): EventRegistry.registry[evt.cookie] = evt
    @staticmethod
    def unregister(evt): del EventRegistry.registry[evt.cookie]
    @staticmethod
    def registered(evt): return evt.cookie in EventRegistry.registry
    @staticmethod
    def matching(evt):
        event = EventRegistry.registry[evt.cookie]
        # Want to disallow accessing the same event twice
        EventRegistry.unregister(event)
        return event
    def __init__(self,*args,**kwargs):
        raise Exception("You can instantiate this class. Must only use class \
                         methods")

class EventProxy(object):
    """
    A container object for instances of BaseEvent (or it's subclasses) used for
    event contractor
    """
    def __init__(self, orig_evt):
        self.orig_evt   = orig_evt
        self.evt        = orig_evt
        self.reset_hook()
        if hasattr(orig_evt, 'path'): self.path = orig_evt.path

    def set_pack_hook(self, l):
        self._pack_hook = l

    def reset_hook(self):
        self._pack_hook = lambda : None

    def run_hook(self):
        self._pack_hook()

    def safe_pack(self):
        self.run_hook()
        # make sure that cleanup hook is never called twice for the same event
        self.reset_hook()
        return self.evt.safe_pack()

    def merge_proxy(self, proxy):
        self.evt = proxy.evt

    def is_event(self, real_event):
        return isinstance(self.evt, real_event)

    def same_event(self, proxy):
        return self.evt.__class__ == proxy.evt.__class__

class HasMetaData(object):
    """
    Any class that inherits from this class gains the metadata attribute that
    loads metadata from the class's 'path' attribute. This is done lazily so
    there is no performance penalty to inheriting from this and subsequent
    calls to metadata are cached
    """
    __metaclass__ = abc.ABCMeta
    @LazyProperty
    def metadata(self): return Metadata(self.path)

class BaseEvent(Loggable):
    __metaclass__ = abc.ABCMeta
    def __init__(self, raw_event):
        # TODO : clean up this idiotic hack
        # we should use keyword constructors instead of this behaviour checking
        # bs to initialize BaseEvent
        if hasattr(raw_event,"pathname"):
            self._raw_event = raw_event
            self.path = os.path.normpath(raw_event.pathname)
        else: self.path = raw_event
        self.owner = owners.get_owner(self.path)
        self._pack_hook = lambda: None # no op
        # into another event

    # TODO : delete this method later
    def reset_hook(self):
        """
        Resets the hook that is called after an event is packed. Before
        resetting the hook we execute it to make sure that whatever cleanup
        operations were queued are executed.
        """
        self._pack_hook()
        self._pack_hook = lambda: None

    def exists(self): return os.path.exists(self.path)

    @LazyProperty
    def cookie(self): return getattr( self._raw_event, 'cookie', None )

    def __str__(self):
        return "Event(%s). Path(%s)" % ( self.path, self.__class__.__name__)

    # TODO : delete this method later
    def add_safe_pack_hook(self,k):
        """
        adds a callable object (function) that will be called after the event
        has been "safe_packed"
        """
        self._pack_hook = k

    def proxify(self):
        return EventProxy(self)

    # As opposed to unsafe_pack...
    def safe_pack(self):
        """
        returns exceptions instead of throwing them to be consistent with
        events that must catch their own BadSongFile exceptions since generate
        a set of exceptions instead of a single one
        """
        try:
            self._pack_hook()
            ret = self.pack()
            # Remove owner of this file only after packing. Otherwise packing
            # will not serialize the owner correctly into the airtime request
            owners.remove_file_owner(self.path)
            return ret
        except BadSongFile as e: return [e]

    # nothing to see here, please move along
    def morph_into(self, evt):
        self.logger.info("Morphing %s into %s" % ( str(self), str(evt) ) )
        self._raw_event   = evt._raw_event
        self.path         = evt.path
        self.__class__    = evt.__class__
        # Clean up old hook and transfer the new events hook
        self.reset_hook()
        self.add_safe_pack_hook( evt._pack_hook )
        return self

    def assign_owner(self,req):
        """
        Packs self.owner to req if the owner is valid. I.e. it's not -1. This
        method is used by various events that would like to pass owner as a
        parameter. NewFile for example.
        """
        if self.owner != -1: req['MDATA_KEY_OWNER_ID'] = self.owner

class FakePyinotify(object):
    """
    sometimes we must create our own pyinotify like objects to
    instantiate objects from the classes below whenever we want to turn
    a single event into multiple events
    """
    def __init__(self, path): self.pathname = path

class OrganizeFile(BaseEvent, HasMetaData):
    """
    The only kind of event that does support the pack protocol. It's used
    internally with mediamonitor to move files in the organize directory.
    """
    def __init__(self, *args, **kwargs):
        super(OrganizeFile, self).__init__(*args, **kwargs)
    def pack(self):
        raise AttributeError("You can't send organize events to airtime!!!")

class NewFile(BaseEvent, HasMetaData):
    """
    NewFile events are the only events that contain MDATA_KEY_OWNER_ID metadata
    in them.
    """
    def __init__(self, *args, **kwargs):
        super(NewFile, self).__init__(*args, **kwargs)
    def pack(self):
        """
        packs turns an event into a media monitor request
        """
        req_dict = self.metadata.extract()
        req_dict['mode'] = u'create'
        self.assign_owner(req_dict)
        req_dict['MDATA_KEY_FILEPATH'] = unicode( self.path )
        return [req_dict]

class DeleteFile(BaseEvent):
    """
    DeleteFile event only contains the path to be deleted. No other metadata
    can be or is included.  (This is because this event is fired after the
    deletion occurs).
    """
    def __init__(self, *args, **kwargs):
        super(DeleteFile, self).__init__(*args, **kwargs)
    def pack(self):
        req_dict = {}
        req_dict['mode'] = u'delete'
        req_dict['MDATA_KEY_FILEPATH'] = unicode( self.path )
        return [req_dict]

class MoveFile(BaseEvent, HasMetaData):
    """
    Path argument should be the new path of the file that was moved
    """
    def __init__(self, *args, **kwargs):
        super(MoveFile, self).__init__(*args, **kwargs)
    def old_path(self):
        return self._raw_event.src_pathname
    def pack(self):
        req_dict                            = {}
        req_dict['mode']                    = u'moved'
        req_dict['MDATA_KEY_ORIGINAL_PATH'] = self.old_path()
        req_dict['MDATA_KEY_FILEPATH']      = unicode( self.path )
        req_dict['MDATA_KEY_MD5'] = self.metadata.extract()['MDATA_KEY_MD5']
        return [req_dict]

class ModifyFile(BaseEvent, HasMetaData):
    def __init__(self, *args, **kwargs):
        super(ModifyFile, self).__init__(*args, **kwargs)
    def pack(self):
        req_dict = self.metadata.extract()
        req_dict['mode'] = u'modify'
        # path to directory that is to be removed
        req_dict['MDATA_KEY_FILEPATH'] = unicode( self.path )
        return [req_dict]

def map_events(directory, constructor):
    """
    Walks 'directory' and creates an event using 'constructor'. Returns a list
    of the constructed events.
    """
    # -unknown-path should not appear in the path here but more testing
    # might be necessary
    for f in mmp.walk_supported(directory, clean_empties=False):
        try:
            for e in constructor( FakePyinotify(f) ).pack(): yield e
        except BadSongFile as e: yield e

class DeleteDir(BaseEvent):
    """
    A DeleteDir event unfolds itself into a list of DeleteFile events for every
    file in the directory.
    """
    def __init__(self, *args, **kwargs):
        super(DeleteDir, self).__init__(*args, **kwargs)
    def pack(self):
        return map_events( self.path, DeleteFile )

class MoveDir(BaseEvent):
    """
    A MoveDir event unfolds itself into a list of MoveFile events for every
    file in the directory.
    """
    def __init__(self, *args, **kwargs):
        super(MoveDir, self).__init__(*args, **kwargs)
    def pack(self):
        return map_events( self.path, MoveFile )

class DeleteDirWatch(BaseEvent):
    """
    Deleting a watched directory is different from deleting any other
    directory.  Hence we must have a separate event to handle this case
    """
    def __init__(self, *args, **kwargs):
        super(DeleteDirWatch, self).__init__(*args, **kwargs)
    def pack(self):
        req_dict = {}
        req_dict['mode']               = u'delete_dir'
        req_dict['MDATA_KEY_FILEPATH'] = unicode( self.path + "/" )
        return [req_dict]

<|endoftext|>


<commit_before>import re
import urllib
import urllib2
import json

from hamper.interfaces import ChatPlugin

class Bitly(ChatPlugin):
    name = 'bitly'
    priority = 2

    # Regex is taken from:
    # http://daringfireball.net/2010/07/improved_regex_for_matching_urls
    regex = ur"""
    (                       # Capture 1: entire matched URL
      (?:
        (?P<prot>https?://)     # http or https protocol
        |                       #   or
        www\d{0,3}[.]           # "www.", "www1.", "www2." ... "www999."
        |                           #   or
        [a-z0-9.\-]+[.][a-z]{2,4}/  # looks like domain name
                                    # followed by a slash
      )
      (?:                                  # One or more:
        [^\s()<>]+                         # Run of non-space, non-()<>
        |                                  # or
        \(([^\s()<>]+|(\([^\s()<>]+\)))*\) # balanced parens, up to 2 levels
      )+
      (?:                                  # End with:
        \(([^\s()<>]+|(\([^\s()<>]+\)))*\) # balanced parens, up to 2 levels
        |                                  # or
        [^\s`!()\[\]{};:'".,<>?]           # not a space or one of
                                           # these punct chars
      )
    )
    """

    def setup(self, loader):
        self.regex = re.compile(self.regex, re.VERBOSE | re.IGNORECASE | re.U)
        self.api_url = 'https://api-ssl.bitly.com/v3/shorten'
        # If an exclude value is found in the url
        # it will not be shortened
        self.excludes = ['imgur.com', 'gist.github.com', 'pastebin.com']
        # Make sure they've configured the bitly config values.
        try:
            self.username = loader.config['bitly']['login']
            self.api_key = loader.config['bitly']['api_key']
        except KeyError:
            print ('\nTo use the bitly plugin you need to set your bitly login'
                   '\nand api_key in your config file.\n'
                   'Example:\n'
                   'bitly:\n'
                   "    login: '123456789000'\n"
                   "    api_key: '1234678901234567890123467890123456'\n")
            quit()

    def message(self, bot, comm):
        match = self.regex.search(comm['message'])
        # Found a url
        if match:
            # base url isn't % encoded, python 2.7 doesn't do this well, and I
            # couldn't figure it out.
            long_url = match.group(0)

            # Only shorten urls which are longer than a bitly url (12 chars)
            if len(long_url) <= 12:
                return False

            # Don't shorten url's which are in the exclude list
            for item in self.excludes:
                if item in long_url.lower():
                    return False

            # Bitly requires a valid URI
            if not match.group('prot'):
                long_url = 'http://' + long_url

            # Bitly requires valid % encoded urls
            params = urllib.urlencode({'login': self.username,
                                       'apiKey': self.api_key,
                                       'longUrl': long_url})
            req = urllib2.Request(self.api_url, data=params)
            response = urllib2.urlopen(req)
            data = json.load(response)

            if data['status_txt'] == 'OK':
                bot.reply(comm, "{0[user]}'s shortened url is {1[url]}"
                          .format(comm, data['data']))

        # Always let the other plugins run
        return False
<commit_msg>bitly: Include page title<commit_after>import json
import StringIO as SIO
import re
import urllib
import urllib2

from hamper.interfaces import ChatPlugin

class Bitly(ChatPlugin):
    name = 'bitly'
    priority = 2

    # Regex is taken from:
    # http://daringfireball.net/2010/07/improved_regex_for_matching_urls
    regex = ur"""
    (                       # Capture 1: entire matched URL
      (?:
        (?P<prot>https?://)     # http or https protocol
        |                       #   or
        www\d{0,3}[.]           # "www.", "www1.", "www2." ... "www999."
        |                           #   or
        [a-z0-9.\-]+[.][a-z]{2,4}/  # looks like domain name
                                    # followed by a slash
      )
      (?:                                  # One or more:
        [^\s()<>]+                         # Run of non-space, non-()<>
        |                                  # or
        \(([^\s()<>]+|(\([^\s()<>]+\)))*\) # balanced parens, up to 2 levels
      )+
      (?:                                  # End with:
        \(([^\s()<>]+|(\([^\s()<>]+\)))*\) # balanced parens, up to 2 levels
        |                                  # or
        [^\s`!()\[\]{};:'".,<>?]           # not a space or one of
                                           # these punct chars
      )
    )
    """
    title_regex = ur"<title>(.*?)</title>"

    def setup(self, loader):
        self.regex = re.compile(self.regex, re.VERBOSE | re.IGNORECASE | re.U)
        self.title_regex = re.compile(self.title_regex, re.I | re.U)
        self.api_url = 'https://api-ssl.bitly.com/v3/shorten'
        # If an exclude value is found in the url
        # it will not be shortened
        self.excludes = ['imgur.com', 'gist.github.com', 'pastebin.com']
        # Make sure they've configured the bitly config values.
        try:
            self.username = loader.config['bitly']['login']
            self.api_key = loader.config['bitly']['api_key']
        except KeyError:
            print ('\nTo use the bitly plugin you need to set your bitly login'
                   '\nand api_key in your config file.\n'
                   'Example:\n'
                   'bitly:\n'
                   "    login: '123456789000'\n"
                   "    api_key: '1234678901234567890123467890123456'\n")
            quit()

    def message(self, bot, comm):
        match = self.regex.search(comm['message'])
        # Found a url
        if match:
            # base url isn't % encoded, python 2.7 doesn't do this well, and I
            # couldn't figure it out.
            long_url = match.group(0)

            # Only shorten urls which are longer than a bitly url (12 chars)
            if len(long_url) <= 12:
                return False

            # Don't shorten url's which are in the exclude list
            for item in self.excludes:
                if item in long_url.lower():
                    return False

            # Bitly requires a valid URI
            if not match.group('prot'):
                long_url = 'http://' + long_url

            # Bitly requires valid % encoded urls
            params = urllib.urlencode({'login': self.username,
                                       'apiKey': self.api_key,
                                       'longUrl': long_url})
            req = urllib2.Request(self.api_url, data=params)
            response = urllib2.urlopen(req)
            data = json.load(response)

            if data['status_txt'] == 'OK':
                # Grab the title of said link
                page_req = urllib2.Request(long_url)
                page_resp = urllib2.urlopen(page_req)
                contents = SIO.StringIO()
                contents.write(page_resp.read())
                title = self.title_regex.search(contents.getvalue()).group(1)

                bot.reply(comm, "{0[user]}'s shortened url is {1[url]}"
                          .format(comm, data['data']))
                bot.reply(comm, "Title: {0}".format(title))

        # Always let the other plugins run
        return False
<|endoftext|>


<commit_before>import json
from datetime import datetime as dt
import sys
import os

DATE_FORMAT = '%Y-%m-%d %H:%M:%S'

def main(json_path, out_dir_path):
    """ Main method, converts Google Hangouts log given in JSON format
        and stores them in location given as second argument.
        Logs are stored in CSV format, one file per conversaton. """

    if not os.path.isdir(out_dir_path):
        os.mkdir(out_dir_path)
        print("Created {} directory".format(out_dir_path))

    with open(json_path, 'r') as json_file:
        data = json.load(json_file)
        participants_id_map = get_participants(data)
        chats = {}

        for event in find_nodes(data, 'event'):
            for conversation in event:
                conversation_id = find_node(conversation, 'id')
                timestamp = int(find_node(conversation, 'timestamp')) / 10**6
                timestamp = dt.fromtimestamp(timestamp).strftime(DATE_FORMAT)

                sender_id = find_node(conversation['sender_id'], 'gaia_id')
                sender = participants_id_map[sender_id]

                msgs = [msg.encode('utf-8') for msg in find_nodes(conversation, 'text') if msg.strip()]
                for msg in msgs:
                    chats.setdefault(conversation_id, []).append('{}\t{}\t{}'.format(timestamp, sender, msg))

        for chat_id in chats:
            file_path = '{}/{}.csv'.format(out_dir_path, chat_id)
            with open(file_path, 'w') as out:
                print("Created {} log file".format(file_path))
                for msg in sorted(chats[chat_id]):
                    out.write(msg + '\n')

def verbose_usage_and_exit():
    """ Prints usage and exits. """

    sys.stderr.write('Usage:\n')
    sys.stderr.write('\tpython <script_name> <file_json> <out_dir>\n'.format(sys.argv[0]))
    exit(0)

def find_nodes(root, query):
    """ Interprets json as tree and finds all nodes with given string. """

    if not isinstance(root, dict):
        return []

    rv = []
    for key, value in root.items():
        if key == query:
            rv.append(value)
            continue

        if not isinstance(value, list):
            # single json entry, make json array
            value = [value]

        for node in value:
            rv.extend(find_nodes(node, query))

    return rv

def find_node(root, query):
    """ Interprets JSON as tree and finds first node with givens string. """

    if not isinstance(root, dict):  # skip leaf
        return None

    for key, value in root.items():
        if key == query:
            return value

        if not isinstance(value, list):
            value = [value]

        for node in value:
            rv = find_node(node, query)
            if rv:
                return rv
    return None

def get_participants(json_data):
    """ Finds all participants in Google Hangouts JSON log and returns dictionary
        { user_id : username }. """

    all_participant_data = find_nodes(json_data, 'participant_data')

    participants_id_map = {}
    for conv_participants in all_participant_data:
        for participant in conv_participants:
            username = participant['fallback_name'].encode('utf-8')
            user_id = find_node(participant['id'], 'gaia_id')
            participants_id_map[user_id] = username

    return participants_id_map

if __name__ == '__main__':
    if len(sys.argv) != 3:
        verbose_usage_and_exit()
    main(sys.argv[1], sys.argv[2])
<commit_msg>Added documentation to code<commit_after>import json
from datetime import datetime as dt
import sys
import os

DATE_FORMAT = '%Y-%m-%d %H:%M:%S'

def main(json_path, out_dir_path):
    """ Converts Google Hangouts log given in JSON format
        and stores them in location given as second argument.
        Logs are stored in CSV format, one file per conversaton. """

    if not os.path.isdir(out_dir_path):
        os.mkdir(out_dir_path)
        print("Created {} directory".format(out_dir_path))

    with open(json_path, 'r') as json_file:
        data = json.load(json_file)
        participants_id_map = get_participants(data)
        chats = {}

        for event in find_nodes(data, 'event'):
            for conversation in event:
                conversation_id = find_node(conversation, 'id')
                timestamp = int(find_node(conversation, 'timestamp')) / 10**6
                timestamp = dt.fromtimestamp(timestamp).strftime(DATE_FORMAT)

                sender_id = find_node(conversation['sender_id'], 'gaia_id')
                sender = participants_id_map[sender_id]

                msgs = [msg.encode('utf-8') for msg in find_nodes(conversation, 'text') if msg.strip()]
                for msg in msgs:
                    chats.setdefault(conversation_id, []).append('{}\t{}\t{}'.format(timestamp, sender, msg))

        for chat_id in chats:
            file_path = '{}/{}.csv'.format(out_dir_path, chat_id)
            with open(file_path, 'w') as out:
                print("Created {} log file".format(file_path))
                for msg in sorted(chats[chat_id]):
                    out.write(msg + '\n')

def verbose_usage_and_exit():
    """ Prints usage and exits. """

    sys.stderr.write('Usage:\n')
    sys.stderr.write('\tpython <script_name> <file_json> <out_dir>\n'.format(sys.argv[0]))
    exit(0)

def find_nodes(root, query):
    """ Interprets json as tree and finds all nodes with given string. """

    if not isinstance(root, dict):
        return []

    rv = []
    for key, value in root.items():
        if key == query:
            rv.append(value)
            continue

        if not isinstance(value, list):
            # single json entry, make json array
            value = [value]

        for node in value:
            rv.extend(find_nodes(node, query))

    return rv

def find_node(root, query):
    """ Interprets JSON as tree and finds first node with givens string. """

    if not isinstance(root, dict):  # skip leaf
        return None

    for key, value in root.items():
        if key == query:
            return value

        if not isinstance(value, list):
            value = [value]

        for node in value:
            rv = find_node(node, query)
            if rv:
                return rv
    return None

def get_participants(json_data):
    """ Finds all participants in Google Hangouts JSON log and returns dictionary
        { user_id : username }. """

    all_participant_data = find_nodes(json_data, 'participant_data')

    participants_id_map = {}
    for conv_participants in all_participant_data:
        for participant in conv_participants:
            username = participant['fallback_name'].encode('utf-8')
            user_id = find_node(participant['id'], 'gaia_id')
            participants_id_map[user_id] = username

    return participants_id_map

if __name__ == '__main__':
    if len(sys.argv) != 3:
        verbose_usage_and_exit()
    main(sys.argv[1], sys.argv[2])
<|endoftext|>


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

import roller
import argparse
import os.path
import sys
import version

def get_args(raw_args):
    parser = argparse.ArgumentParser(
        description='Build the kernel from the config'
    )
    parser.add_argument(
        'config_file',
        help='Config file to work from',
    )
    parser.add_argument(
        '-b', '--build-dir',
        dest='build_dir',
        default='/tmp',
        help='directory for downloading, extracting, and building the kernel'
    )
    parser.add_argument(
        '-r', '--revision',
        dest='revision',
        default='dev',
        help='Set revision for new kernel'
    )
    return parser.parse_args(raw_args)

def easy_build(raw_args):
    args = get_args(raw_args)

    config_file = os.path.abspath(args.config_file)

    kernel = roller.Kernel(
        build_dir=args.build_dir,
        verbose=True
    )

    kernel.version = version.get_version(config_file)
    kernel.revision = args.revision
    kernel.config = config_file
    kernel.output = 'none'

    kernel.download()
    kernel.extract()
    kernel.configure()
    kernel.make()
    kernel.where()

if __name__ == '__main__':
    easy_build(sys.argv[1:])
<commit_msg>fix version handling<commit_after>#!/usr/bin/env python

import roller
import argparse
import os.path
import sys
import version

def get_args(raw_args):
    parser = argparse.ArgumentParser(
        description='Build the kernel from the config'
    )
    parser.add_argument(
        'config_file',
        help='Config file to work from',
    )
    parser.add_argument(
        '-b', '--build-dir',
        dest='build_dir',
        default='/tmp',
        help='directory for downloading, extracting, and building the kernel'
    )
    parser.add_argument(
        '-r', '--revision',
        dest='revision',
        default='dev',
        help='Set revision for new kernel'
    )
    return parser.parse_args(raw_args)

def easy_build(raw_args):
    args = get_args(raw_args)

    config_file = os.path.abspath(args.config_file)

    kernel = roller.Kernel(
        build_dir=args.build_dir,
        verbose=True
    )

    version = version.get_version(config_file)
    if version.endswith('.0')
      version = version[0:-2]
    kernel.version = version
    if kernel.version
    kernel.revision = args.revision
    kernel.config = config_file
    kernel.output = 'none'

    kernel.download()
    kernel.extract()
    kernel.configure()
    kernel.make()
    kernel.where()

if __name__ == '__main__':
    easy_build(sys.argv[1:])
<|endoftext|>


<commit_before>import sys

from bot.action.core.action import Action, IntermediateAction
from bot.api.domain import Message

EXIT_STATUS_TO_HALT_BOT = 55

class RestartAction(Action):
    def process(self, event):
        response_text = "Restarting bot...\nCommands might not work while restarting."
        self.api.send_message(Message.create_reply(event.message, response_text))
        raise KeyboardInterrupt()

class EvalAction(Action):
    def process(self, event):
        code = event.command_args
        result = eval(code)
        response_message = "Evaluated. Result: %s" % result
        self.api.send_message(Message.create_reply(event.message, response_message))

class HaltAction(Action):
    def process(self, event):
        response_text = "Bot stopped.\nYou need to launch it manually for it to work again."
        self.api.send_message(Message.create_reply(event.message, response_text))
        sys.exit(EXIT_STATUS_TO_HALT_BOT)

class AdminAction(IntermediateAction):
    def process(self, event):
        from_ = event.message.from_
        if from_ is not None and str(from_.id) == self.config.admin_user_id:
            self._continue(event)

class AdminActionWithErrorMessage(IntermediateAction):
    def process(self, event):
        from_ = event.message.from_
        if from_ is not None and str(from_.id) == self.config.admin_user_id:
            self._continue(event)
        else:
            error_response = "You are not allowed to perform this action (admins only)."
            self.api.send_message(Message.create_reply(event.message, error_response))

class GroupAdminAction(IntermediateAction):
    def process(self, event):
        chat = event.message.chat
        if chat.type == "private":
            # lets consider private chat members are admins :)
            self._continue(event)
        else:
            user = event.message.from_
            if user is not None:
                chat_member = self.api.getChatMember(chat_id=chat.id, user_id=user.id)
                if chat_member.status in ("creator", "administrator"):
                    self._continue(event)
                else:
                    error_response = "Sorry, this command is only available to group admins."
                    self.api.send_message(Message.create_reply(event.message, error_response))
<commit_msg>Improve EvalAction<commit_after>import sys

from bot.action.core.action import Action, IntermediateAction
from bot.action.util.textformat import FormattedText
from bot.api.domain import Message

EXIT_STATUS_TO_HALT_BOT = 55

class RestartAction(Action):
    def process(self, event):
        response_text = "Restarting bot...\nCommands might not work while restarting."
        self.api.send_message(Message.create_reply(event.message, response_text))
        raise KeyboardInterrupt()

class EvalAction(Action):
    def process(self, event):
        code = event.command_args
        response = FormattedText()
        try:
            result = eval(code)
        except Exception as e:
            response.normal("Error: {error}").start_format().bold(error=str(e)).end_format()
        else:
            response.normal("Result: {result}").start_format().bold(result=result).end_format()
        self.api.send_message(response.build_message().to_chat_replying(event.message))

class HaltAction(Action):
    def process(self, event):
        response_text = "Bot stopped.\nYou need to launch it manually for it to work again."
        self.api.send_message(Message.create_reply(event.message, response_text))
        sys.exit(EXIT_STATUS_TO_HALT_BOT)

class AdminAction(IntermediateAction):
    def process(self, event):
        from_ = event.message.from_
        if from_ is not None and str(from_.id) == self.config.admin_user_id:
            self._continue(event)

class AdminActionWithErrorMessage(IntermediateAction):
    def process(self, event):
        from_ = event.message.from_
        if from_ is not None and str(from_.id) == self.config.admin_user_id:
            self._continue(event)
        else:
            error_response = "You are not allowed to perform this action (admins only)."
            self.api.send_message(Message.create_reply(event.message, error_response))

class GroupAdminAction(IntermediateAction):
    def process(self, event):
        chat = event.message.chat
        if chat.type == "private":
            # lets consider private chat members are admins :)
            self._continue(event)
        else:
            user = event.message.from_
            if user is not None:
                chat_member = self.api.getChatMember(chat_id=chat.id, user_id=user.id)
                if chat_member.status in ("creator", "administrator"):
                    self._continue(event)
                else:
                    error_response = "Sorry, this command is only available to group admins."
                    self.api.send_message(Message.create_reply(event.message, error_response))
<|endoftext|>


<commit_before>__author__ = 'Tom Schaul, tom@idsia.ch'

from scipy import randn, zeros
from random import choice, random, gauss
from evolution import Evolution

class GA(Evolution):
    """ Genetic algorithm """

    # selection schemes
    tournament = False
    tournamentsize = 2

    topproportion = 0.2

    elitism = False
    eliteproportion = 0.5
    elitesize = None # override with an exact number

    mutationprob = 0.1
    mutationStdDev = 0.5

    def initPopulation(self):
        self.currentpop = [self.x0]
        for dummy in range(self.popsize-1):
            self.currentpop.append(self.x0+randn(self.xdim))

    def crossOver(self, parents, nbChildren):
        """ generate a number of children by doing 1-point cross-over """
        children = []
        for dummy in range(nbChildren):
            p1 = choice(parents)
            if self.xdim < 2:
                children.append(p1)
            else:
                p2 = choice(parents)
                point = choice(range(self.xdim-1))
                res = zeros(self.xdim)
                res[:point] = p1[:point]
                res[point:] = p2[point:]
                children.append(res)
        return children

    def mutated(self, indiv):
        """ mutate some genes of the given individual """
        res = indiv.copy()
        for i in range(self.xdim):
            if random() < self.mutationprob:
                res[i] = indiv[i] + gauss(0, self.mutationStdDev)
        return res

    def selectionSize(self):
        """ the number of parents selected from the current population """
        return int(self.popsize * self.topproportion)

    def eliteSize(self):
        if self.elitism:
            if self.elitesize != None:
                return self.elitesize
            else:
                return int(self.popsize * self.eliteproportion)
        else:
            return 0

    def select(self):
        """ select some of the individuals of the population, taking into account their fitnesses
        @return: list of selected parents """
        if not self.tournament:
            tmp = zip(self.fitnesses, self.currentpop)
            tmp.sort(key = lambda x: x[0])
            tmp2 = list(reversed(tmp))[:self.selectionSize()]
            return map(lambda x: x[1], tmp2)
        else:
            # TODO: tournament selection
            raise NotImplementedError()

    def produceOffspring(self):
        """ produce offspring by selection, mutation and crossover. """
        parents = self.select()
        es = min(self.eliteSize(), self.selectionSize())
        self.currentpop = parents[:es]
        for child in self.crossOver(parents, self.popsize-es):
            self.currentpop.append(self.mutated(child))

        <commit_msg>minor: additional parameter<commit_after>__author__ = 'Tom Schaul, tom@idsia.ch'

from scipy import randn, zeros
from random import choice, random, gauss
from evolution import Evolution

class GA(Evolution):
    """ Genetic algorithm """

    # selection schemes
    tournament = False
    tournamentsize = 2

    topproportion = 0.2

    elitism = False
    eliteproportion = 0.5
    elitesize = None # override with an exact number

    mutationprob = 0.1
    mutationStdDev = 0.5
    initRangeScaling = 10.

    def initPopulation(self):
        self.currentpop = [self.x0]
        for dummy in range(self.popsize-1):
            self.currentpop.append(self.x0+randn(self.xdim)*self.mutationStdDev*self.initRangeScaling)

    def crossOver(self, parents, nbChildren):
        """ generate a number of children by doing 1-point cross-over """
        children = []
        for dummy in range(nbChildren):
            p1 = choice(parents)
            if self.xdim < 2:
                children.append(p1)
            else:
                p2 = choice(parents)
                point = choice(range(self.xdim-1))
                res = zeros(self.xdim)
                res[:point] = p1[:point]
                res[point:] = p2[point:]
                children.append(res)
        return children

    def mutated(self, indiv):
        """ mutate some genes of the given individual """
        res = indiv.copy()
        for i in range(self.xdim):
            if random() < self.mutationprob:
                res[i] = indiv[i] + gauss(0, self.mutationStdDev)
        return res

    def selectionSize(self):
        """ the number of parents selected from the current population """
        return int(self.popsize * self.topproportion)

    def eliteSize(self):
        if self.elitism:
            if self.elitesize != None:
                return self.elitesize
            else:
                return int(self.popsize * self.eliteproportion)
        else:
            return 0

    def select(self):
        """ select some of the individuals of the population, taking into account their fitnesses
        @return: list of selected parents """
        if not self.tournament:
            tmp = zip(self.fitnesses, self.currentpop)
            tmp.sort(key = lambda x: x[0])
            tmp2 = list(reversed(tmp))[:self.selectionSize()]
            return map(lambda x: x[1], tmp2)
        else:
            # TODO: tournament selection
            raise NotImplementedError()

    def produceOffspring(self):
        """ produce offspring by selection, mutation and crossover. """
        parents = self.select()
        es = min(self.eliteSize(), self.selectionSize())
        self.currentpop = parents[:es]
        for child in self.crossOver(parents, self.popsize-es):
            self.currentpop.append(self.mutated(child))

        <|endoftext|>


<commit_before># -*- coding:utf-8 -*-
# Author: hankcs
# Date: 2019-12-28 21:12
from hanlp_common.constant import HANLP_URL

SIGHAN2005_PKU_CONVSEG = HANLP_URL + 'tok/sighan2005-pku-convseg_20200110_153722.zip'
'Conv model (:cite:`wang-xu-2017-convolutional`) trained on sighan2005 pku dataset.'
SIGHAN2005_MSR_CONVSEG = HANLP_URL + 'tok/convseg-msr-nocrf-noembed_20200110_153524.zip'
'Conv model (:cite:`wang-xu-2017-convolutional`) trained on sighan2005 msr dataset.'
CTB6_CONVSEG = HANLP_URL + 'tok/ctb6_convseg_nowe_nocrf_20200110_004046.zip'
'Conv model (:cite:`wang-xu-2017-convolutional`) trained on CTB6 dataset.'
PKU_NAME_MERGED_SIX_MONTHS_CONVSEG = HANLP_URL + 'tok/pku98_6m_conv_ngram_20200110_134736.zip'
'Conv model (:cite:`wang-xu-2017-convolutional`) trained on pku98 six months dataset with familiy name and given name merged into one unit.'
LARGE_ALBERT_BASE = HANLP_URL + 'tok/large_corpus_cws_albert_base_20211228_160926.zip'
'ALBERT model (:cite:`Lan2020ALBERT:`) trained on the largest CWS dataset in the world.'
SIGHAN2005_PKU_BERT_BASE_ZH = HANLP_URL + 'tok/sighan2005_pku_bert_base_zh_20201231_141130.zip'
'BERT model (:cite:`devlin-etal-2019-bert`) trained on sighan2005 pku dataset.'
COARSE_ELECTRA_SMALL_ZH = HANLP_URL + 'tok/coarse_electra_small_20220220_013548.zip'
'Electra (:cite:`clark2020electra`) small model trained on coarse-grained CWS corpora. Its performance is P=96.97% R=96.87% F1=96.92% which is ' \
'much higher than that of MTL model '
FINE_ELECTRA_SMALL_ZH = HANLP_URL + 'tok/fine_electra_small_20220217_190117.zip'
'Electra (:cite:`clark2020electra`) small model trained on fine-grained CWS corpora. Its performance is P=97.44% R=97.40% F1=97.42% which is ' \
'much higher than that of MTL model '
CTB9_TOK_ELECTRA_SMALL = HANLP_URL + 'tok/ctb9_electra_small_20220215_205427.zip'
'Electra (:cite:`clark2020electra`) small model trained on CTB9. Its performance is P=97.15% R=97.36% F1=97.26% which is ' \
'much higher than that of MTL model '
CTB9_TOK_ELECTRA_BASE = 'http://download.hanlp.com/tok/extra/ctb9_tok_electra_base_20220426_111949.zip'
'Electra (:cite:`clark2020electra`) base model trained on CTB9. Its performance is ``P: 97.62% R: 97.67% F1: 97.65%`` ' \
'which is much higher than that of MTL model '
CTB9_TOK_ELECTRA_BASE_CRF = 'http://download.hanlp.com/tok/extra/ctb9_tok_electra_base_crf_20220426_161255.zip'
'Electra (:cite:`clark2020electra`) base model trained on CTB9. Its performance is ``P: 97.68% R: 97.71% F1: 97.69%`` ' \
'which is much higher than that of MTL model '

# Will be filled up during runtime
ALL = {}
<commit_msg>Release `MSR_TOK_ELECTRA_BASE_CRF` model<commit_after># -*- coding:utf-8 -*-
# Author: hankcs
# Date: 2019-12-28 21:12
from hanlp_common.constant import HANLP_URL

SIGHAN2005_PKU_CONVSEG = HANLP_URL + 'tok/sighan2005-pku-convseg_20200110_153722.zip'
'Conv model (:cite:`wang-xu-2017-convolutional`) trained on sighan2005 pku dataset.'
SIGHAN2005_MSR_CONVSEG = HANLP_URL + 'tok/convseg-msr-nocrf-noembed_20200110_153524.zip'
'Conv model (:cite:`wang-xu-2017-convolutional`) trained on sighan2005 msr dataset.'
CTB6_CONVSEG = HANLP_URL + 'tok/ctb6_convseg_nowe_nocrf_20200110_004046.zip'
'Conv model (:cite:`wang-xu-2017-convolutional`) trained on CTB6 dataset.'
PKU_NAME_MERGED_SIX_MONTHS_CONVSEG = HANLP_URL + 'tok/pku98_6m_conv_ngram_20200110_134736.zip'
'Conv model (:cite:`wang-xu-2017-convolutional`) trained on pku98 six months dataset with familiy name and given name merged into one unit.'
LARGE_ALBERT_BASE = HANLP_URL + 'tok/large_corpus_cws_albert_base_20211228_160926.zip'
'ALBERT model (:cite:`Lan2020ALBERT:`) trained on the largest CWS dataset in the world.'
SIGHAN2005_PKU_BERT_BASE_ZH = HANLP_URL + 'tok/sighan2005_pku_bert_base_zh_20201231_141130.zip'
'BERT model (:cite:`devlin-etal-2019-bert`) trained on sighan2005 pku dataset.'
COARSE_ELECTRA_SMALL_ZH = HANLP_URL + 'tok/coarse_electra_small_20220220_013548.zip'
'Electra (:cite:`clark2020electra`) small model trained on coarse-grained CWS corpora. Its performance is P=96.97% R=96.87% F1=96.92% which is ' \
'much higher than that of MTL model '
FINE_ELECTRA_SMALL_ZH = HANLP_URL + 'tok/fine_electra_small_20220217_190117.zip'
'Electra (:cite:`clark2020electra`) small model trained on fine-grained CWS corpora. Its performance is P=97.44% R=97.40% F1=97.42% which is ' \
'much higher than that of MTL model '
CTB9_TOK_ELECTRA_SMALL = HANLP_URL + 'tok/ctb9_electra_small_20220215_205427.zip'
'Electra (:cite:`clark2020electra`) small model trained on CTB9. Its performance is P=97.15% R=97.36% F1=97.26% which is ' \
'much higher than that of MTL model '
CTB9_TOK_ELECTRA_BASE = 'http://download.hanlp.com/tok/extra/ctb9_tok_electra_base_20220426_111949.zip'
'Electra (:cite:`clark2020electra`) base model trained on CTB9. Its performance is ``P: 97.62% R: 97.67% F1: 97.65%`` ' \
'which is much higher than that of MTL model '
CTB9_TOK_ELECTRA_BASE_CRF = 'http://download.hanlp.com/tok/extra/ctb9_tok_electra_base_crf_20220426_161255.zip'
'Electra (:cite:`clark2020electra`) base model trained on CTB9. Its performance is ``P: 97.68% R: 97.71% F1: 97.69%`` ' \
'which is much higher than that of MTL model '
MSR_TOK_ELECTRA_BASE_CRF = 'http://download.hanlp.com/tok/extra/msra_crf_electra_base_20220507_113936.zip'
'Electra (:cite:`clark2020electra`) base model trained on MSR CWS dataset. Its performance is ``P: 98.71% R: 98.64% F1: 98.68%`` ' \
'which is much higher than that of MTL model '

# Will be filled up during runtime
ALL = {}
<|endoftext|>


<commit_before>import argparse
from argparse import ArgumentDefaultsHelpFormatter
import errno
import logging
import jenkins
import os
import sys
import textwrap
import jenkins_job_wrecker.job_handlers as job_handlers
import xml.etree.ElementTree as ET
from yaml import dump

logging.basicConfig(level=logging.INFO)
log = logging.getLogger('jjwrecker')

# set the format of the standard StreamHandler to have a space in it
# (on the root logger)
for handler in logging.getLogger().handlers:
    if isinstance(handler, logging.StreamHandler):
        handler.setFormatter(logging.Formatter(fmt='%(name)s %(levelname)s: %(message)s'))

# Given a file with XML, or a string of XML, parse it with
# xml.etree.ElementTree and return the XML tree root.
def get_xml_root(filename=False, string=False):
    if filename == False and string == False:
        raise TypeError('specify a filename or string argument')
    if filename:
        tree = ET.parse(filename)
        return tree.getroot()
    if string:
        return ET.fromstring(string)

# Walk an XML ElementTree ("root"), and return a YAML string
def root_to_yaml(root, name):
    # Top-level "job" data
    job = {}
    build = [{'job': job}]

    job['name'] = name

    if root.tag != 'matrix-project':
       raise NotImplementedError, 'We only support matrix-type jobs'

    job['project-type'] = 'matrix'

    # Handle each top-level XML element with custom "handle_*" functions in
    # job_handlers.py.
    for child in root:
        handler_name = 'handle_%s' % child.tag.lower()
        try:
            handler = getattr(job_handlers, handler_name)
        except AttributeError:
            # Show our YAML translation so far:
            print dump(build, default_flow_style=False)
            # ... and report what still needs to be done:
            raise NotImplementedError("write a function for %s" % handler_name)
        try:
            settings = handler(child)
            if settings is not None:
                for setting in settings:
                    key, value = setting
                    job[key] = value
        except Exception, e:
            print 'last called %s' % handler_name
            raise

    return dump(build, default_flow_style=False)

# argparse foo
def parse_args(args):
    parser = argparse.ArgumentParser(
        description='Input XML, output YAML.',
        epilog=textwrap.dedent('''
        Examples:
        jjwrecker -f ice-tools.xml
        '''),
        formatter_class=ArgumentDefaultsHelpFormatter)
    parser.add_argument(
        '-f', '--filename',
        help='XML file to translate'
    )
    parser.add_argument(
        '-s', '--jenkins-server',
        help='Jenkins server to query'
    )
    parser.add_argument(
        '-n', '--name',
        help='Name of a job'
    )
    return parser.parse_args(args)

def main():
    args = parse_args(sys.argv[1:])

    # Options:
    # -f and -n
    # -s and -n
    # TODO: -s (without -n means "all jobs on the server")

    # Choose either -f or -j ...
    if not args.jenkins_server and not args.filename:
        log.critical('Choose an XML file (-f) or Jenkins URL (-j).')
        exit(1)

    # ... but not both -f and -j.
    if args.jenkins_server and args.filename:
        log.critical('Choose either an XML file (-f) or Jenkins URL (-j).')
        exit(1)

    # -f requires -n
    if args.filename and not args.name:
        log.critical('Choose a job name (-n) for the job in this file.')
        exit(1)

    # Args are ok. Proceed with writing output
    try:
        os.mkdir('output')
    # We don't care if "output" dir already exists.
    except OSError as exception:
        if exception.errno != errno.EEXIST:
            raise

    if args.filename:
        # Convert to YAML
        root = get_xml_root(filename=args.filename)
        yaml = root_to_yaml(root, args.name)
        # write yaml string to file (job-name.yml)
        yaml_filename = os.path.join('output', args.name + '.yml')
        output_file = open(yaml_filename, 'w')
        output_file.write(yaml)
        output_file.close()

    # -s requires -n
    if args.jenkins_server:
        # 'http://jenkins-calamari.front.sepia.ceph.com:8080'
        server = jenkins.Jenkins(args.jenkins_server)
        if args.name:
            job_names = [args.name]
        else:
            job_names = []
            for job in server.get_jobs():
                job_names.append(job['name'])

        # write YAML
        for name in job_names:
            log.info('looking up job "%s"' % name)
            # Convert to YAML
            root = get_xml_root(string=server.get_job_config(name))
            log.info('converting job "%s" to YAML' % name)
            yaml = root_to_yaml(root, name)
            # write yaml string to file (job-name.yml)
            yaml_filename = os.path.join('output', name + '.yml')
            output_file = open(yaml_filename, 'w')
            output_file.write(yaml)
            output_file.close()
<commit_msg>cli: refactor dynamic job lookups<commit_after>import argparse
from argparse import ArgumentDefaultsHelpFormatter
import errno
import logging
import jenkins
import os
import sys
import textwrap
import jenkins_job_wrecker.job_handlers as job_handlers
import xml.etree.ElementTree as ET
from yaml import dump

logging.basicConfig(level=logging.INFO)
log = logging.getLogger('jjwrecker')

# set the format of the standard StreamHandler to have a space in it
# (on the root logger)
for handler in logging.getLogger().handlers:
    if isinstance(handler, logging.StreamHandler):
        handler.setFormatter(logging.Formatter(fmt='%(name)s %(levelname)s: %(message)s'))

# Given a file with XML, or a string of XML, parse it with
# xml.etree.ElementTree and return the XML tree root.
def get_xml_root(filename=False, string=False):
    if filename == False and string == False:
        raise TypeError('specify a filename or string argument')
    if filename:
        tree = ET.parse(filename)
        return tree.getroot()
    if string:
        return ET.fromstring(string)

# Walk an XML ElementTree ("root"), and return a YAML string
def root_to_yaml(root, name):
    # Top-level "job" data
    job = {}
    build = [{'job': job}]

    job['name'] = name

    if root.tag != 'matrix-project':
       raise NotImplementedError, 'We only support matrix-type jobs'

    job['project-type'] = 'matrix'

    # Handle each top-level XML element with custom "handle_*" functions in
    # job_handlers.py.
    for child in root:
        handler_name = 'handle_%s' % child.tag.lower()
        try:
            handler = getattr(job_handlers, handler_name)
        except AttributeError:
            # Show our YAML translation so far:
            print dump(build, default_flow_style=False)
            # ... and report what still needs to be done:
            raise NotImplementedError("write a function for %s" % handler_name)
        try:
            settings = handler(child)
            if settings is not None:
                for setting in settings:
                    key, value = setting
                    job[key] = value
        except Exception, e:
            print 'last called %s' % handler_name
            raise

    return dump(build, default_flow_style=False)

# argparse foo
def parse_args(args):
    parser = argparse.ArgumentParser(
        description='Input XML, output YAML.',
        epilog=textwrap.dedent('''
        Examples:
        jjwrecker -f ice-tools.xml
        '''),
        formatter_class=ArgumentDefaultsHelpFormatter)
    parser.add_argument(
        '-f', '--filename',
        help='XML file to translate'
    )
    parser.add_argument(
        '-s', '--jenkins-server',
        help='Jenkins server to query'
    )
    parser.add_argument(
        '-n', '--name',
        help='Name of a job'
    )
    return parser.parse_args(args)

def main():
    args = parse_args(sys.argv[1:])

    # Options:
    # -f and -n
    # -s and -n
    # TODO: -s (without -n means "all jobs on the server")

    # Choose either -f or -j ...
    if not args.jenkins_server and not args.filename:
        log.critical('Choose an XML file (-f) or Jenkins URL (-j).')
        exit(1)

    # ... but not both -f and -j.
    if args.jenkins_server and args.filename:
        log.critical('Choose either an XML file (-f) or Jenkins URL (-j).')
        exit(1)

    # -f requires -n
    if args.filename and not args.name:
        log.critical('Choose a job name (-n) for the job in this file.')
        exit(1)

    # Args are ok. Proceed with writing output
    try:
        os.mkdir('output')
    # We don't care if "output" dir already exists.
    except OSError as exception:
        if exception.errno != errno.EEXIST:
            raise

    if args.filename:
        # Convert to YAML
        root = get_xml_root(filename=args.filename)
        yaml = root_to_yaml(root, args.name)
        # write yaml string to file (job-name.yml)
        yaml_filename = os.path.join('output', args.name + '.yml')
        output_file = open(yaml_filename, 'w')
        output_file.write(yaml)
        output_file.close()

    # -s requires -n
    if args.jenkins_server:
        # 'http://jenkins-calamari.front.sepia.ceph.com:8080'
        server = jenkins.Jenkins(args.jenkins_server)
        if args.name:
            job_names = [args.name]
        else:
            job_names = []
            for job in server.get_jobs():
                job_names.append(job['name'])

        # write YAML
        for name in job_names:
            log.info('looking up job "%s"' % name)
            # Get a job's XML
            xml = server.get_job_config(name)
            # Convert XML to YAML
            root = get_xml_root(string=xml)
            log.info('converting job "%s" to YAML' % name)
            yaml = root_to_yaml(root, name)
            # write yaml string to file (job-name.yml)
            yaml_filename = os.path.join('output', name + '.yml')
            output_file = open(yaml_filename, 'w')
            output_file.write(yaml)
            output_file.close()
<|endoftext|>


<commit_before>#!/usr/bin/env python
"""
NAME
    dev_setup.py

DESCRIPTION
    Facilitates simple install, uninstall, and reinstall of the developer
    version of the PmagPy library and GUIs for both Unix and Windows systems.
    This will edit global environment variables and shell configuration files
    if you have no idea what those are, unless you need immediate quick
    updates to PmagPy, it may be better to use the pip or binary installs of
    this software instructions here:
    (https://earthref.org/PmagPy/cookbook/#pip_install).
    Also note for Windows users the windows_install function of this file
    requires administrative access so you will need to run in a command prompt
    with elevated privileges. See:
    (http://www.thewindowsclub.com/how-to-run-command-prompt-as-an-administrator)
    Last, this script MUST BE RUN FROM THE PMAGPY DIRECTORY.

SYNTAX
    python dev_setup.py [install,uninstall,reinstall] [OPTIONS]

OPTIONS
    -h: open help message
    -p: path_to_python -> necessary if you don't have a fully set up python
        environment on windows. This information will allow the script to set
        this python.exe to be the default python interpreter and will have it
        run when file name with extension .py is entered in command prompt.
        Note: this should be an ABSOLUTE path. If you don't know where your
        python.exe is type this:
            cd \\
            dir python.exe /s /p
        into command prompt and it will tell you the location of all
        python.exe it can find. This process can take a while, so be
        patient.
"""
import sys,os,subprocess

def unix_install():
    """
    Edits or creates .bashrc, .bash_profile, and .profile files in the users
    HOME directory in order to add your current directory (hopefully your
    PmagPy directory) and assorted lower directories in the PmagPy/programs
    directory to your PATH environment variable. It also adds the PmagPy and
    the PmagPy/programs directories to PYTHONPATH.
    """
    PmagPyDir=os.path.abspath(".")
    COMMAND="""\n
PMAGPATHS="%s"
PMAGPATHS="$PMAGPATHS:%s/programs/"
for d in %s/programs/*/; do
  PMAGPATHS="$PMAGPATHS:$d"
done
export PYTHONPATH="$PYTHONPATH:%s:%s/programs/"
export PATH="$PATH:$PMAGPATHS" """%(PmagPyDir,PmagPyDir,PmagPyDir,PmagPyDir,PmagPyDir)
    frc_path=os.path.join(os.environ["HOME"],".bashrc") #not recommended, but hey it freaking works
    fbprof_path=os.path.join(os.environ["HOME"],".bash_profile")
    fprof_path=os.path.join(os.environ["HOME"],".profile")

    for f_path in [frc_path,fbprof_path,fprof_path]:
        open_type='a'
        if not os.path.isfile(f_path):
            open_type='w+'
            fout=open(f_path,open_type)
            fout.write(COMMAND)
            fout.close()
        else:
            fin=open(f_path,'r')
            current_f=fin.read()
            fin.close()
            if COMMAND not in current_f:
                fout=open(f_path,open_type)
                fout.write(COMMAND)
                fout.close()

    print("Install complete. Please restart the shell to complete install.\nIf you are seeing strange or non-existent paths in your PATH or PYTHONPATH variable please manually check your .bashrc, .bash_profile, and .profile or attempt to reinstall.")

def unix_uninstall():
    frc_path=os.path.join(os.environ["HOME"],".bashrc") #not recommended, but hey it freaking works
    fbprof_path=os.path.join(os.environ["HOME"],".bash_profile")
    fprof_path=os.path.join(os.environ["HOME"],".profile")

    for f_path in [frc_path,fbprof_path,fprof_path]:
        fin=open(f_path,'r')
        fout_string,skip="",False
        for line in fin.readlines():
            if "PMAGPATHS=" in line: skip=True
            elif 'export PATH="$PATH:$PMAGPATHS"' in line: skip=False; continue
            if skip: continue
            else: fout_string+=line
        fout_string=fout_string.strip('\n')
        fout=open(f_path,'w')
        fout.write(fout_string); fin.close(); fout.close()

    print("Uninstall complete. Please restart your shell to complete uninstall.")

def windows_install(path_to_python=""):
    """
    Sets the .py extension to be associated with the ftype Python which is
    then set to the python.exe you provide in the path_to_python variable or
    after the -p flag if run as a script. Once the python environment is set
    up the function proceeds to set PATH and PYTHONPATH using setx.

    Parameters
    ----------
    path_to_python : the path the python.exe you want windows to execute when
    running .py files
    """
    if not path_to_python:
        print("Please enter the path to your python.exe you wish Windows to use to run python files. If you do not, this script will not be able to set up a full python environment in Windows. If you already have a python environment set up in Windows such that you can run python scripts from command prompt with just a file name then ignore this message. For more information, rerun script with -h flag.")
        print("Would you like to continue? [y/N] ")
        ans=raw_input()
        if ans=='y': pass
        else: return
    #make windows associate .py with python
    subprocess.check_call('assoc .py=Python',shell=True)
    subprocess.check_call('ftype Python=%s '%path_to_python + '"%1" %*', shell=True)

    PmagPyDir=os.path.abspath(".")
    ProgramsDir=os.path.join(PmagPyDir,'programs')
    dirs_to_add = [PmagPyDir,ProgramsDir]
    for d in next(os.walk(ProgramsDir))[1]:
        dirs_to_add.append(os.path.join(ProgramsDir,d))
    path = subprocess.check_output('echo %PATH%', shell=True).strip('\n')
    if "PATH" in path: path=''
    pypath = subprocess.check_output('echo %PYTHONPATH%', shell=True).strip('\n')
    if "PYTHONPATH" in pypath: pypath=''
    for d_add in dirs_to_add:
        path+=';'+d_add
    pypath+=PmagPyDir+';'+ProgramsDir
    unique_path_list=[]
    for p in path.split(';'):
        p=p.replace('"','')
        if p not in unique_path_list:
            unique_path_list.append(p)
    unique_pypath_list=[]
    for p in pypath.split(';'):
        p=p.replace('"','')
        if p not in unique_pypath_list:
            unique_pypath_list.append(p)
    path=reduce(lambda x,y: x+';'+y, unique_path_list)
    pypath=reduce(lambda x,y: x+';'+y, unique_pypath_list)
    print('setx PATH "%s"'%path)
    subprocess.call('setx PATH "%s"'%path, shell=True)
    print('setx PYTHONPATH "%s"'%pypath)
    subprocess.call('setx PYTHONPATH "%s"'%(pypath), shell=True)

    print("Install complete. Please restart the command prompt to complete install")

def windows_uninstall():
    path = subprocess.check_output('echo %PATH%', shell=True).strip('\n')
    if "PATH" in path: path=''; print("PmagPy dev version not installed, aborting"); return
    pypath = subprocess.check_output('echo %PYTHONPATH%', shell=True).strip('\n')
    if "PYTHONPATH" in pypath: pypath=''; print("PmagPy dev version not installed, aborting"); return

    paths=path.split(';')
    pypaths=pypath.split(';')

    new_paths = [p for p in paths if 'pmagpy' not in p.lower()]
    new_pypaths = [p for p in pypaths if 'pmagpy' not in p.lower()]

    if new_paths: new_path = reduce(lambda x,y: x+';'+y, new_paths)
    else: new_path=''
    if new_pypaths: new_pypath = reduce(lambda x,y: x+';'+y, new_pypaths)
    else: new_pypath=''

    print('setx PATH "%s"'%new_path)
    subprocess.call('setx PATH "%s"'%new_path, shell=True)
    print('setx PYTHONPATH "%s"'%new_pypath)
    subprocess.call('setx PYTHONPATH "%s"'%(new_pypath), shell=True)

    print("Uninstall complete. Please restart the command prompt to complete uninstall")

if __name__=="__main__":
    if '-h' in sys.argv: help(__name__)
    elif sys.platform.startswith("linux") or sys.platform.startswith("darwin"):
        if "install" in sys.argv:
            unix_install()
        elif "uninstall" in sys.argv:
            unix_uninstall()
        elif "reinstall" in sys.argv:
            unix_uninstall()
            unix_install()
        else: unix_install()
    else:
        kwargs={}
        if '-p' in sys.argv:
            ip=sys.argv.index('-p')
            kwargs['path_to_python']=sys.argv[ip+1]

        if "install" in sys.argv:
            windows_install(**kwargs)
        elif "uninstall" in sys.argv:
            windows_uninstall()
        elif "reinstall" in sys.argv:
            windows_uninstall()
            windows_install(**kwargs)
        else: windows_install(**kwargs)
<commit_msg>made unix install work so that it doesn't duplicate items already in the path dispite entries in multipule configuration files as per #172<commit_after>#!/usr/bin/env python
"""
NAME
    dev_setup.py

DESCRIPTION
    Facilitates simple install, uninstall, and reinstall of the developer
    version of the PmagPy library and GUIs for both Unix and Windows systems.
    This will edit global environment variables and shell configuration files
    if you have no idea what those are, unless you need immediate quick
    updates to PmagPy, it may be better to use the pip or binary installs of
    this software instructions here:
    (https://earthref.org/PmagPy/cookbook/#pip_install).
    Also note for Windows users the windows_install function of this file
    requires administrative access so you will need to run in a command prompt
    with elevated privileges. See:
    (http://www.thewindowsclub.com/how-to-run-command-prompt-as-an-administrator)
    Last, this script MUST BE RUN FROM THE PMAGPY DIRECTORY.

SYNTAX
    python dev_setup.py [install,uninstall,reinstall] [OPTIONS]

OPTIONS
    -h: open help message
    -p: path_to_python -> necessary if you don't have a fully set up python
        environment on windows. This information will allow the script to set
        this python.exe to be the default python interpreter and will have it
        run when file name with extension .py is entered in command prompt.
        Note: this should be an ABSOLUTE path. If you don't know where your
        python.exe is type this:
            cd \\
            dir python.exe /s /p
        into command prompt and it will tell you the location of all
        python.exe it can find. This process can take a while, so be
        patient.
"""
import sys,os,subprocess

def unix_install():
    """
    Edits or creates .bashrc, .bash_profile, and .profile files in the users
    HOME directory in order to add your current directory (hopefully your
    PmagPy directory) and assorted lower directories in the PmagPy/programs
    directory to your PATH environment variable. It also adds the PmagPy and
    the PmagPy/programs directories to PYTHONPATH.
    """
    PmagPyDir=os.path.abspath(".")
    COMMAND="""\n
for d in %s/programs/*/ "%s" "%s/programs/"; do
  case ":$PATH:" in
    *":$d:"*) :;; # already there
    *) PMAGPATHS="$PMAGPATHS:$d";; # or PATH="$PATH:$new_entry"
  esac
done
export PYTHONPATH="$PYTHONPATH:%s:%s/programs/"
export PATH="$PATH:$PMAGPATHS" """%(PmagPyDir,PmagPyDir,PmagPyDir,PmagPyDir,PmagPyDir)
    frc_path=os.path.join(os.environ["HOME"],".bashrc") #not recommended, but hey it freaking works
    fbprof_path=os.path.join(os.environ["HOME"],".bash_profile")
    fprof_path=os.path.join(os.environ["HOME"],".profile")
    all_paths=[frc_path,fbprof_path,fprof_path]

    for f_path in all_paths:
        open_type='a'
        if not os.path.isfile(f_path):
            open_type='w+'
            fout=open(f_path,open_type)
            fout.write(COMMAND)
            fout.close()
        else:
            fin=open(f_path,'r')
            current_f=fin.read()
            fin.close()
            if COMMAND not in current_f:
                fout=open(f_path,open_type)
                fout.write(COMMAND)
                fout.close()

    print("Install complete. Please restart the shell to complete install.\nIf you are seeing strange or non-existent paths in your PATH or PYTHONPATH variable please manually check your .bashrc, .bash_profile, and .profile or attempt to reinstall.")

def unix_uninstall():
    frc_path=os.path.join(os.environ["HOME"],".bashrc") #not recommended, but hey it freaking works
    fbprof_path=os.path.join(os.environ["HOME"],".bash_profile")
    fprof_path=os.path.join(os.environ["HOME"],".profile")

    for f_path in [frc_path,fbprof_path,fprof_path]:
        fin=open(f_path,'r')
        fout_string,skip="",False
        for line in fin.readlines():
            if ("for d in " in line and "/programs/*/" in line) or "PMAGPATHS=" in line: skip=True
            elif 'export PATH="$PATH:$PMAGPATHS"' in line: skip=False; continue
            if skip: continue
            else: fout_string+=line
        fout_string=fout_string.strip('\n')
        fout=open(f_path,'w')
        fout.write(fout_string); fin.close(); fout.close()

    print("Uninstall complete. Please restart your shell to complete uninstall.")

def windows_install(path_to_python=""):
    """
    Sets the .py extension to be associated with the ftype Python which is
    then set to the python.exe you provide in the path_to_python variable or
    after the -p flag if run as a script. Once the python environment is set
    up the function proceeds to set PATH and PYTHONPATH using setx.

    Parameters
    ----------
    path_to_python : the path the python.exe you want windows to execute when
    running .py files
    """
    if not path_to_python:
        print("Please enter the path to your python.exe you wish Windows to use to run python files. If you do not, this script will not be able to set up a full python environment in Windows. If you already have a python environment set up in Windows such that you can run python scripts from command prompt with just a file name then ignore this message. For more information, rerun script with -h flag.")
        print("Would you like to continue? [y/N] ")
        ans=raw_input()
        if ans=='y': pass
        else: return
    #make windows associate .py with python
    subprocess.check_call('assoc .py=Python',shell=True)
    subprocess.check_call('ftype Python=%s '%path_to_python + '"%1" %*', shell=True)

    PmagPyDir=os.path.abspath(".")
    ProgramsDir=os.path.join(PmagPyDir,'programs')
    dirs_to_add = [PmagPyDir,ProgramsDir]
    for d in next(os.walk(ProgramsDir))[1]:
        dirs_to_add.append(os.path.join(ProgramsDir,d))
    path = subprocess.check_output('echo %PATH%', shell=True).strip('\n')
    if "PATH" in path: path=''
    pypath = subprocess.check_output('echo %PYTHONPATH%', shell=True).strip('\n')
    if "PYTHONPATH" in pypath: pypath=''
    for d_add in dirs_to_add:
        path+=';'+d_add
    pypath+=PmagPyDir+';'+ProgramsDir
    unique_path_list=[]
    for p in path.split(';'):
        p=p.replace('"','')
        if p not in unique_path_list:
            unique_path_list.append(p)
    unique_pypath_list=[]
    for p in pypath.split(';'):
        p=p.replace('"','')
        if p not in unique_pypath_list:
            unique_pypath_list.append(p)
    path=reduce(lambda x,y: x+';'+y, unique_path_list)
    pypath=reduce(lambda x,y: x+';'+y, unique_pypath_list)
    print('setx PATH "%s"'%path)
    subprocess.call('setx PATH "%s"'%path, shell=True)
    print('setx PYTHONPATH "%s"'%pypath)
    subprocess.call('setx PYTHONPATH "%s"'%(pypath), shell=True)

    print("Install complete. Please restart the command prompt to complete install")

def windows_uninstall():
    path = subprocess.check_output('echo %PATH%', shell=True).strip('\n')
    if "PATH" in path: path=''; print("PmagPy dev version not installed, aborting"); return
    pypath = subprocess.check_output('echo %PYTHONPATH%', shell=True).strip('\n')
    if "PYTHONPATH" in pypath: pypath=''; print("PmagPy dev version not installed, aborting"); return

    paths=path.split(';')
    pypaths=pypath.split(';')

    new_paths = [p for p in paths if 'pmagpy' not in p.lower()]
    new_pypaths = [p for p in pypaths if 'pmagpy' not in p.lower()]

    if new_paths: new_path = reduce(lambda x,y: x+';'+y, new_paths)
    else: new_path=''
    if new_pypaths: new_pypath = reduce(lambda x,y: x+';'+y, new_pypaths)
    else: new_pypath=''

    print('setx PATH "%s"'%new_path)
    subprocess.call('setx PATH "%s"'%new_path, shell=True)
    print('setx PYTHONPATH "%s"'%new_pypath)
    subprocess.call('setx PYTHONPATH "%s"'%(new_pypath), shell=True)

    print("Uninstall complete. Please restart the command prompt to complete uninstall")

if __name__=="__main__":
    if '-h' in sys.argv: help(__name__)
    elif sys.platform.startswith("linux") or sys.platform.startswith("darwin"):
        if "install" in sys.argv:
            unix_install()
        elif "uninstall" in sys.argv:
            unix_uninstall()
        elif "reinstall" in sys.argv:
            unix_uninstall()
            unix_install()
        else: unix_install()
    else:
        kwargs={}
        if '-p' in sys.argv:
            ip=sys.argv.index('-p')
            kwargs['path_to_python']=sys.argv[ip+1]

        if "install" in sys.argv:
            windows_install(**kwargs)
        elif "uninstall" in sys.argv:
            windows_uninstall()
        elif "reinstall" in sys.argv:
            windows_uninstall()
            windows_install(**kwargs)
        else: windows_install(**kwargs)
<|endoftext|>