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

import os.path
import user
import re

import wx
import wx.media as wxm

import kchan.lyrics as lyrics
import kchan.widgets as kcw
import kchan.timedtext as timedtext
import kchan.formats.lyrics3v2 as lyrics3v2

def handler(fn):
    """Helper function to make simple event handlers"""
    def evtHandler(evt):
        fn()
    return evtHandler

class SaveDialog(wx.Dialog):
    def __init__(self, parent):
        wx.Dialog.__init__(self, parent, title="Save changes?")
        label = wx.StaticText(self, label="You have unsaved changes. Save before closing?")

        buttonSizer = wx.StdDialogButtonSizer()
        buttonSizer.AddButton(wx.Button(self, wx.ID_SAVE))
        buttonSizer.AddButton(wx.Button(self, wx.ID_NO))
        buttonSizer.AddButton(wx.Button(self, wx.ID_CANCEL))
        buttonSizer.Realize()
        self.Bind(wx.EVT_BUTTON, self.OnButton)

        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(label, 0, wx.EXPAND | wx.ALL, 10)
        sizer.Add(buttonSizer, 0, wx.EXPAND | wx.ALL, 10)
        self.SetSizerAndFit(sizer)

    def OnButton(self, evt):
        self.EndModal(evt.GetId())

class KaraokePlayer(wx.Frame):
    def __init__(self, *args, **kwargs):
        wx.Frame.__init__(self, *args, **kwargs)

        # media widget
        self.player = wxm.MediaCtrl(self)
        self.Bind(wxm.EVT_MEDIA_STATECHANGED, self.OnPlayer, self.player)

        # lyrics viewer
        self.lyricsViewer = kcw.LyricsCtrl(self, self.player)

        # lyrics editor
        self.lyricsEditor = kcw.LyricsEditor(self, self.player)

        # menu bar
        self.menuBar = wx.MenuBar()
        self.SetMenuBar(self.menuBar)

        # file menu
        self.fileMenu = wx.Menu()
        self.menuBar.Append(self.fileMenu, "&File")

        self.fileMenu.Append(wx.ID_OPEN)
        self.fileMenu.Append(wx.ID_EDIT, "Edit Lyrics\tCTRL+E")
        self.fileMenu.Append(wx.ID_SAVE)
        self.fileMenu.Append(wx.ID_CLOSE, "Close Editor")
        self.fileMenu.Append(wx.ID_EXIT)

        self.Bind(wx.EVT_MENU, self.OnOpen, id=wx.ID_OPEN)
        self.Bind(wx.EVT_MENU, self.OnEdit, id=wx.ID_EDIT)
        self.Bind(wx.EVT_MENU, self.OnSave, id=wx.ID_SAVE)
        self.Bind(wx.EVT_MENU, self.OnCloseEditor, id=wx.ID_CLOSE)
        self.Bind(wx.EVT_MENU, handler(self.Close), id=wx.ID_EXIT)

        # timing menu
        self.timingMenu = wx.Menu()
        self.menuBar.Append(self.timingMenu, "&Timing")

        self.placeholderItem = self.timingMenu.Append(wx.ID_ANY,
                                                      "Add Timestamp Placeholder\tCTRL+SHIFT+T")
        self.timestampItem = self.timingMenu.Append(wx.ID_ANY, "Set Timestamp\tCTRL+T")

        self.Bind(wx.EVT_MENU, handler(self.lyricsEditor.AddPlaceholder),
                  self.placeholderItem)
        self.Bind(wx.EVT_MENU, handler(self.lyricsEditor.SetTimestamp),
                  self.timestampItem)

        # playback menu
        self.playbackMenu = wx.Menu()
        self.menuBar.Append(self.playbackMenu, "&Playback")

        self.playPauseItem = self.playbackMenu.Append(wx.ID_ANY, "Play/Pause\tCTRL+P")
        self.stopItem = self.playbackMenu.Append(wx.ID_ANY, "Stop\tCTRL+SHIFT+P")

        self.Bind(wx.EVT_MENU, self.OnPlayPause, self.playPauseItem)
        self.Bind(wx.EVT_MENU, self.OnStop, self.stopItem)

        # these are only enabled in Edit Mode
        self.fileMenu.Enable(wx.ID_SAVE, False)
        self.fileMenu.Enable(wx.ID_CLOSE, False)
        for item in self.timingMenu.GetMenuItems():
            item.Enable(False)

        # controls
        self.playPauseButton = wx.ToggleButton(self, label="Play/Pause")
        self.stopButton = wx.Button(self, label="Stop")
        self.muteButton = wx.ToggleButton(self, label="Mute")
        self.timestampButton = wx.Button(self, label="Set Timestamp")
        self.volumeSlider = wx.Slider(self, minValue=0, maxValue=100, size=(100, -1))
        self.volumeLabel = wx.StaticText(self, size=(50,-1), label="0%",
                                         style = wx.ALIGN_RIGHT | wx.ST_NO_AUTORESIZE)
        self.timeSlider = wx.Slider(self, minValue=0, maxValue=1)
        self.timeLabel = wx.StaticText(self, size=(100, -1), label="0:00/0:00",
                                       style = wx.ALIGN_RIGHT | wx.ST_NO_AUTORESIZE)

        self.Bind(wx.EVT_TOGGLEBUTTON, self.OnPlayPause, self.playPauseButton)
        self.Bind(wx.EVT_BUTTON, self.OnStop, self.stopButton)
        self.Bind(wx.EVT_BUTTON, handler(self.lyricsEditor.SetTimestamp),
                  self.timestampButton)
        self.Bind(wx.EVT_TOGGLEBUTTON, self.OnMute, self.muteButton)
        self.Bind(wx.EVT_SLIDER, self.OnVol, self.volumeSlider)

        self.sliding = False
        self.Bind(wx.EVT_SCROLL_THUMBTRACK, self.OnTimeSliding, self.timeSlider)
        self.Bind(wx.EVT_SCROLL_THUMBRELEASE, self.OnTimeReleased, self.timeSlider)
        self.Bind(wx.EVT_SLIDER, self.OnTimeAny, self.timeSlider)

        # sizers
        self.editorButtonSizer = wx.BoxSizer(wx.HORIZONTAL)
        self.editorButtonSizer.Add(self.timestampButton, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 10)

        self.buttonSizer = wx.BoxSizer(wx.HORIZONTAL)
        self.buttonSizer.Add(self.playPauseButton, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 10)
        self.buttonSizer.Add(self.stopButton, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 10)
        self.buttonSizer.AddStretchSpacer()
        self.buttonSizer.Add(self.editorButtonSizer, 0, wx.EXPAND)
        self.buttonSizer.Hide(self.editorButtonSizer) # only shown in Edit Mode
        self.buttonSizer.AddStretchSpacer()
        self.buttonSizer.Add(self.muteButton, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 10)
        self.buttonSizer.Add(self.volumeSlider, 0, wx.ALIGN_CENTER_VERTICAL)
        self.buttonSizer.Add(self.volumeLabel, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 10)

        self.timeSizer = wx.BoxSizer(wx.HORIZONTAL)
        self.timeSizer.Add(self.timeSlider, 1, wx.ALIGN_CENTER_VERTICAL)
        self.timeSizer.Add(self.timeLabel, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 10)

        self.controlSizer = wx.BoxSizer(wx.VERTICAL)
        self.controlSizer.Add(self.timeSizer, 0, wx.EXPAND)
        self.controlSizer.Add(self.buttonSizer, 0, wx.EXPAND)

        self.viewerSizer = wx.BoxSizer(wx.HORIZONTAL)
        self.viewerSizer.Add(self.player, 1, wx.EXPAND)
        self.viewerSizer.Add(self.lyricsViewer, 1, wx.EXPAND)
        self.viewerSizer.Add(self.lyricsEditor, 1, wx.EXPAND)
        self.viewerSizer.Hide(self.lyricsEditor)

        self.mainSizer = wx.BoxSizer(wx.VERTICAL)
        self.mainSizer.Add(self.viewerSizer, 1, wx.EXPAND)
        self.mainSizer.Add(self.controlSizer, 0, wx.EXPAND)

        self.SetSizerAndFit(self.mainSizer)

        # timer
        self.timer = wx.Timer(self)
        self.Bind(wx.EVT_TIMER, handler(self.UpdateTime), self.timer)

        self.Bind(wx.EVT_CLOSE, self.OnClose, self)

        # flag to indicate which mode we're in
        self.editMode = False

        # currently loaded file
        self.filepath = None

        self.Show()

    def UpdateTime(self, updateSliderTime=True):
        length = self.player.Length()
        time = self.player.Tell()
        if updateSliderTime:
            self.timeSlider.SetMax(length)
            self.timeSlider.SetValue(time)
        self.timeLabel.SetLabelText("{}:{:02}/{}:{:02}".format(time/60000, (time/1000)%60,
                                                               length/60000, (length/1000)%60))

    def PromptSave(self):
        saveDialog = SaveDialog(self)
        result = saveDialog.ShowModal()
        if result == wx.ID_SAVE:
            self.OnSave(None)
            return True
        elif result == wx.ID_NO:
            return True
        else:
            return False

    def OnPlayer(self, evt):
        self.UpdateTime()

        if self.player.GetState() == wxm.MEDIASTATE_PLAYING:
            self.timer.Start(milliseconds=100)
            self.playPauseButton.SetValue(True)

            if self.editMode:
                self.lyricsEditor.SetFocus()
                self.lyricsViewer.SetLyrics(self.lyricsEditor.GetLyrics())
        else:
            self.timer.Stop()
            if not self.sliding:
                self.playPauseButton.SetValue(False)

        self.lyricsViewer.OnPlayer(evt)

    def OpenFile(self, filepath):
        self.filepath = filepath
        # Show lyrics, if available
        self.lyricsViewer.ClearLyrics()
        if os.path.splitext(self.filepath)[1] == ".mp3":
            try:
                self.lyricsViewer.SetLyrics(lyrics3v2.load(lyrics3v2.read(self.filepath)))
            except ValueError:
                pass

        self.player.Load(self.filepath)

        if self.editMode:
            self.lyricsEditor.LoadLyrics(self.lyricsViewer.lyrics)

        # Hide lyrics viewer if there are no lyrics
        if self.lyricsViewer.IsEmpty() and not self.editMode:
            self.viewerSizer.Hide(self.lyricsViewer)
        else:
            self.viewerSizer.Show(self.lyricsViewer)

        # Hide player screen if there's no video
        if self.player.GetBestSize() == (0,0):
            self.viewerSizer.Hide(self.player)
        else:
            self.viewerSizer.Show(self.player)

        self.viewerSizer.Layout()

        title = os.path.basename(self.filepath)
        self.SetTitle(u'{} - Karaoke-chan'.format(title))

        self.volumeSlider.SetValue(int(self.player.GetVolume() * 100))
        self.volumeLabel.SetLabelText("{}%".format(int(self.player.GetVolume() * 100)))
        self.UpdateTime()

    def OnOpen(self, evt):
        self.OnStop(None)

        dialog = wx.FileDialog(self)

        if dialog.ShowModal() == wx.ID_OK:
            self.OpenFile(dialog.GetPath())

    def OnClose(self, evt):
        if evt.CanVeto():
            self.player.Stop()
            if self.editMode and self.lyricsEditor.IsModified() and not self.PromptSave():
                return
        self.Destroy()

    def OnPlayPause(self, evt):
        if self.player.GetState() == wxm.MEDIASTATE_PLAYING:
            self.player.Pause()
        else:
            self.player.Play()

    def OnStop(self, evt):
        self.player.Stop()

    def OnMute(self, evt):
        if self.muteButton.GetValue():
            self.player.SetVolume(0)
        else:
            self.player.SetVolume(self.volumeSlider.GetValue() / 100.0)

    def OnVol(self, evt):
        self.player.SetVolume(self.volumeSlider.GetValue() / 100.0)
        self.volumeLabel.SetLabelText("{}%".format(self.volumeSlider.GetValue()))

    def OnEdit(self, evt):
        self.editMode = True

        self.fileMenu.Enable(wx.ID_EDIT, False)
        self.fileMenu.Enable(wx.ID_SAVE, True)
        self.fileMenu.Enable(wx.ID_CLOSE, True)
        for item in self.timingMenu.GetMenuItems():
            item.Enable(True)

        self.buttonSizer.Show(self.editorButtonSizer)
        self.buttonSizer.Layout()
        self.viewerSizer.Show(self.lyricsViewer)
        self.viewerSizer.Show(self.lyricsEditor)
        self.viewerSizer.Layout()

        self.lyricsEditor.LoadLyrics(self.lyricsViewer.lyrics)

        self.lyricsEditor.SetFocus()

    def OnSave(self, evt):
        try:
            lyrics3v2.write(self.filepath, lyrics3v2.dump(self.lyricsEditor.GetLyrics()))
            self.lyricsEditor.DiscardEdits()
        except:
            print "write error"

    def OnCloseEditor(self, evt):
        self.player.Stop()

        if self.lyricsEditor.IsModified() and not self.PromptSave():
            return

        self.editMode = False

        self.fileMenu.Enable(wx.ID_EDIT, True)
        self.fileMenu.Enable(wx.ID_SAVE, False)
        self.fileMenu.Enable(wx.ID_CLOSE, False)
        for item in self.timingMenu.GetMenuItems():
            item.Enable(False)

        self.buttonSizer.Hide(self.editorButtonSizer)
        self.buttonSizer.Layout()
        self.viewerSizer.Hide(self.lyricsEditor)
        self.viewerSizer.Layout()
        self.OpenFile(self.filepath)

    def OnTimeSliding(self, evt):
        self.sliding = True
        self.player.Pause()

    def OnTimeReleased(self, evt):
        self.sliding = False
        if self.playPauseButton.GetValue():
            self.player.Play()

    def OnTimeAny(self, evt):
        self.player.Seek(self.timeSlider.GetValue())
        self.UpdateTime(updateSliderTime=False)

if __name__ == "__main__":
    app = wx.App(False)
    player = KaraokePlayer(None, title="Karaoke-chan")
    player.Show()
    app.MainLoop()
<commit_msg>Prompt the user to save changes before switching to a different file<commit_after>#! /usr/bin/env python2

import os.path
import user
import re

import wx
import wx.media as wxm

import kchan.lyrics as lyrics
import kchan.widgets as kcw
import kchan.timedtext as timedtext
import kchan.formats.lyrics3v2 as lyrics3v2

def handler(fn):
    """Helper function to make simple event handlers"""
    def evtHandler(evt):
        fn()
    return evtHandler

class SaveDialog(wx.Dialog):
    def __init__(self, parent):
        wx.Dialog.__init__(self, parent, title="Save changes?")
        label = wx.StaticText(self, label="You have unsaved changes. Save before closing?")

        buttonSizer = wx.StdDialogButtonSizer()
        buttonSizer.AddButton(wx.Button(self, wx.ID_SAVE))
        buttonSizer.AddButton(wx.Button(self, wx.ID_NO))
        buttonSizer.AddButton(wx.Button(self, wx.ID_CANCEL))
        buttonSizer.Realize()
        self.Bind(wx.EVT_BUTTON, self.OnButton)

        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(label, 0, wx.EXPAND | wx.ALL, 10)
        sizer.Add(buttonSizer, 0, wx.EXPAND | wx.ALL, 10)
        self.SetSizerAndFit(sizer)

    def OnButton(self, evt):
        self.EndModal(evt.GetId())

class KaraokePlayer(wx.Frame):
    def __init__(self, *args, **kwargs):
        wx.Frame.__init__(self, *args, **kwargs)

        # media widget
        self.player = wxm.MediaCtrl(self)
        self.Bind(wxm.EVT_MEDIA_STATECHANGED, self.OnPlayer, self.player)

        # lyrics viewer
        self.lyricsViewer = kcw.LyricsCtrl(self, self.player)

        # lyrics editor
        self.lyricsEditor = kcw.LyricsEditor(self, self.player)

        # menu bar
        self.menuBar = wx.MenuBar()
        self.SetMenuBar(self.menuBar)

        # file menu
        self.fileMenu = wx.Menu()
        self.menuBar.Append(self.fileMenu, "&File")

        self.fileMenu.Append(wx.ID_OPEN)
        self.fileMenu.Append(wx.ID_EDIT, "Edit Lyrics\tCTRL+E")
        self.fileMenu.Append(wx.ID_SAVE)
        self.fileMenu.Append(wx.ID_CLOSE, "Close Editor")
        self.fileMenu.Append(wx.ID_EXIT)

        self.Bind(wx.EVT_MENU, self.OnOpen, id=wx.ID_OPEN)
        self.Bind(wx.EVT_MENU, self.OnEdit, id=wx.ID_EDIT)
        self.Bind(wx.EVT_MENU, self.OnSave, id=wx.ID_SAVE)
        self.Bind(wx.EVT_MENU, self.OnCloseEditor, id=wx.ID_CLOSE)
        self.Bind(wx.EVT_MENU, handler(self.Close), id=wx.ID_EXIT)

        # timing menu
        self.timingMenu = wx.Menu()
        self.menuBar.Append(self.timingMenu, "&Timing")

        self.placeholderItem = self.timingMenu.Append(wx.ID_ANY,
                                                      "Add Timestamp Placeholder\tCTRL+SHIFT+T")
        self.timestampItem = self.timingMenu.Append(wx.ID_ANY, "Set Timestamp\tCTRL+T")

        self.Bind(wx.EVT_MENU, handler(self.lyricsEditor.AddPlaceholder),
                  self.placeholderItem)
        self.Bind(wx.EVT_MENU, handler(self.lyricsEditor.SetTimestamp),
                  self.timestampItem)

        # playback menu
        self.playbackMenu = wx.Menu()
        self.menuBar.Append(self.playbackMenu, "&Playback")

        self.playPauseItem = self.playbackMenu.Append(wx.ID_ANY, "Play/Pause\tCTRL+P")
        self.stopItem = self.playbackMenu.Append(wx.ID_ANY, "Stop\tCTRL+SHIFT+P")

        self.Bind(wx.EVT_MENU, self.OnPlayPause, self.playPauseItem)
        self.Bind(wx.EVT_MENU, self.OnStop, self.stopItem)

        # these are only enabled in Edit Mode
        self.fileMenu.Enable(wx.ID_SAVE, False)
        self.fileMenu.Enable(wx.ID_CLOSE, False)
        for item in self.timingMenu.GetMenuItems():
            item.Enable(False)

        # controls
        self.playPauseButton = wx.ToggleButton(self, label="Play/Pause")
        self.stopButton = wx.Button(self, label="Stop")
        self.muteButton = wx.ToggleButton(self, label="Mute")
        self.timestampButton = wx.Button(self, label="Set Timestamp")
        self.volumeSlider = wx.Slider(self, minValue=0, maxValue=100, size=(100, -1))
        self.volumeLabel = wx.StaticText(self, size=(50,-1), label="0%",
                                         style = wx.ALIGN_RIGHT | wx.ST_NO_AUTORESIZE)
        self.timeSlider = wx.Slider(self, minValue=0, maxValue=1)
        self.timeLabel = wx.StaticText(self, size=(100, -1), label="0:00/0:00",
                                       style = wx.ALIGN_RIGHT | wx.ST_NO_AUTORESIZE)

        self.Bind(wx.EVT_TOGGLEBUTTON, self.OnPlayPause, self.playPauseButton)
        self.Bind(wx.EVT_BUTTON, self.OnStop, self.stopButton)
        self.Bind(wx.EVT_BUTTON, handler(self.lyricsEditor.SetTimestamp),
                  self.timestampButton)
        self.Bind(wx.EVT_TOGGLEBUTTON, self.OnMute, self.muteButton)
        self.Bind(wx.EVT_SLIDER, self.OnVol, self.volumeSlider)

        self.sliding = False
        self.Bind(wx.EVT_SCROLL_THUMBTRACK, self.OnTimeSliding, self.timeSlider)
        self.Bind(wx.EVT_SCROLL_THUMBRELEASE, self.OnTimeReleased, self.timeSlider)
        self.Bind(wx.EVT_SLIDER, self.OnTimeAny, self.timeSlider)

        # sizers
        self.editorButtonSizer = wx.BoxSizer(wx.HORIZONTAL)
        self.editorButtonSizer.Add(self.timestampButton, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 10)

        self.buttonSizer = wx.BoxSizer(wx.HORIZONTAL)
        self.buttonSizer.Add(self.playPauseButton, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 10)
        self.buttonSizer.Add(self.stopButton, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 10)
        self.buttonSizer.AddStretchSpacer()
        self.buttonSizer.Add(self.editorButtonSizer, 0, wx.EXPAND)
        self.buttonSizer.Hide(self.editorButtonSizer) # only shown in Edit Mode
        self.buttonSizer.AddStretchSpacer()
        self.buttonSizer.Add(self.muteButton, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 10)
        self.buttonSizer.Add(self.volumeSlider, 0, wx.ALIGN_CENTER_VERTICAL)
        self.buttonSizer.Add(self.volumeLabel, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 10)

        self.timeSizer = wx.BoxSizer(wx.HORIZONTAL)
        self.timeSizer.Add(self.timeSlider, 1, wx.ALIGN_CENTER_VERTICAL)
        self.timeSizer.Add(self.timeLabel, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 10)

        self.controlSizer = wx.BoxSizer(wx.VERTICAL)
        self.controlSizer.Add(self.timeSizer, 0, wx.EXPAND)
        self.controlSizer.Add(self.buttonSizer, 0, wx.EXPAND)

        self.viewerSizer = wx.BoxSizer(wx.HORIZONTAL)
        self.viewerSizer.Add(self.player, 1, wx.EXPAND)
        self.viewerSizer.Add(self.lyricsViewer, 1, wx.EXPAND)
        self.viewerSizer.Add(self.lyricsEditor, 1, wx.EXPAND)
        self.viewerSizer.Hide(self.lyricsEditor)

        self.mainSizer = wx.BoxSizer(wx.VERTICAL)
        self.mainSizer.Add(self.viewerSizer, 1, wx.EXPAND)
        self.mainSizer.Add(self.controlSizer, 0, wx.EXPAND)

        self.SetSizerAndFit(self.mainSizer)

        # timer
        self.timer = wx.Timer(self)
        self.Bind(wx.EVT_TIMER, handler(self.UpdateTime), self.timer)

        self.Bind(wx.EVT_CLOSE, self.OnClose, self)

        # flag to indicate which mode we're in
        self.editMode = False

        # currently loaded file
        self.filepath = None

        self.Show()

    def UpdateTime(self, updateSliderTime=True):
        length = self.player.Length()
        time = self.player.Tell()
        if updateSliderTime:
            self.timeSlider.SetMax(length)
            self.timeSlider.SetValue(time)
        self.timeLabel.SetLabelText("{}:{:02}/{}:{:02}".format(time/60000, (time/1000)%60,
                                                               length/60000, (length/1000)%60))

    def PromptSave(self):
        saveDialog = SaveDialog(self)
        result = saveDialog.ShowModal()
        if result == wx.ID_SAVE:
            self.OnSave(None)
            return True
        elif result == wx.ID_NO:
            return True
        else:
            return False

    def OnPlayer(self, evt):
        self.UpdateTime()

        if self.player.GetState() == wxm.MEDIASTATE_PLAYING:
            self.timer.Start(milliseconds=100)
            self.playPauseButton.SetValue(True)

            if self.editMode:
                self.lyricsEditor.SetFocus()
                self.lyricsViewer.SetLyrics(self.lyricsEditor.GetLyrics())
        else:
            self.timer.Stop()
            if not self.sliding:
                self.playPauseButton.SetValue(False)

        self.lyricsViewer.OnPlayer(evt)

    def OpenFile(self, filepath):
        self.filepath = filepath
        # Show lyrics, if available
        self.lyricsViewer.ClearLyrics()
        if os.path.splitext(self.filepath)[1] == ".mp3":
            try:
                self.lyricsViewer.SetLyrics(lyrics3v2.load(lyrics3v2.read(self.filepath)))
            except ValueError:
                pass

        self.player.Load(self.filepath)

        if self.editMode:
            self.lyricsEditor.LoadLyrics(self.lyricsViewer.lyrics)

        # Hide lyrics viewer if there are no lyrics
        if self.lyricsViewer.IsEmpty() and not self.editMode:
            self.viewerSizer.Hide(self.lyricsViewer)
        else:
            self.viewerSizer.Show(self.lyricsViewer)

        # Hide player screen if there's no video
        if self.player.GetBestSize() == (0,0):
            self.viewerSizer.Hide(self.player)
        else:
            self.viewerSizer.Show(self.player)

        self.viewerSizer.Layout()

        title = os.path.basename(self.filepath)
        self.SetTitle(u'{} - Karaoke-chan'.format(title))

        self.volumeSlider.SetValue(int(self.player.GetVolume() * 100))
        self.volumeLabel.SetLabelText("{}%".format(int(self.player.GetVolume() * 100)))
        self.UpdateTime()

    def OnOpen(self, evt):
        self.OnStop(None)

        if self.editMode and self.filepath is not None and not self.PromptSave():
            return

        dialog = wx.FileDialog(self)

        if dialog.ShowModal() == wx.ID_OK:
            self.OpenFile(dialog.GetPath())

    def OnClose(self, evt):
        if evt.CanVeto():
            self.player.Stop()
            if self.editMode and self.lyricsEditor.IsModified() and not self.PromptSave():
                return
        self.Destroy()

    def OnPlayPause(self, evt):
        if self.player.GetState() == wxm.MEDIASTATE_PLAYING:
            self.player.Pause()
        else:
            self.player.Play()

    def OnStop(self, evt):
        self.player.Stop()

    def OnMute(self, evt):
        if self.muteButton.GetValue():
            self.player.SetVolume(0)
        else:
            self.player.SetVolume(self.volumeSlider.GetValue() / 100.0)

    def OnVol(self, evt):
        self.player.SetVolume(self.volumeSlider.GetValue() / 100.0)
        self.volumeLabel.SetLabelText("{}%".format(self.volumeSlider.GetValue()))

    def OnEdit(self, evt):
        self.editMode = True

        self.fileMenu.Enable(wx.ID_EDIT, False)
        self.fileMenu.Enable(wx.ID_SAVE, True)
        self.fileMenu.Enable(wx.ID_CLOSE, True)
        for item in self.timingMenu.GetMenuItems():
            item.Enable(True)

        self.buttonSizer.Show(self.editorButtonSizer)
        self.buttonSizer.Layout()
        self.viewerSizer.Show(self.lyricsViewer)
        self.viewerSizer.Show(self.lyricsEditor)
        self.viewerSizer.Layout()

        self.lyricsEditor.LoadLyrics(self.lyricsViewer.lyrics)

        self.lyricsEditor.SetFocus()

    def OnSave(self, evt):
        try:
            lyrics3v2.write(self.filepath, lyrics3v2.dump(self.lyricsEditor.GetLyrics()))
            self.lyricsEditor.DiscardEdits()
        except:
            print "write error"

    def OnCloseEditor(self, evt):
        self.player.Stop()

        if self.lyricsEditor.IsModified() and not self.PromptSave():
            return

        self.editMode = False

        self.fileMenu.Enable(wx.ID_EDIT, True)
        self.fileMenu.Enable(wx.ID_SAVE, False)
        self.fileMenu.Enable(wx.ID_CLOSE, False)
        for item in self.timingMenu.GetMenuItems():
            item.Enable(False)

        self.buttonSizer.Hide(self.editorButtonSizer)
        self.buttonSizer.Layout()
        self.viewerSizer.Hide(self.lyricsEditor)
        self.viewerSizer.Layout()
        self.OpenFile(self.filepath)

    def OnTimeSliding(self, evt):
        self.sliding = True
        self.player.Pause()

    def OnTimeReleased(self, evt):
        self.sliding = False
        if self.playPauseButton.GetValue():
            self.player.Play()

    def OnTimeAny(self, evt):
        self.player.Seek(self.timeSlider.GetValue())
        self.UpdateTime(updateSliderTime=False)

if __name__ == "__main__":
    app = wx.App(False)
    player = KaraokePlayer(None, title="Karaoke-chan")
    player.Show()
    app.MainLoop()
<|endoftext|>


<commit_before>from __future__ import unicode_literals

from django.db import models
from django.conf import settings
from django.dispatch import receiver
from django.utils.translation import ugettext_lazy as _
from django.utils.encoding import python_2_unicode_compatible

@python_2_unicode_compatible
class FoiSite(models.Model):
    country_code = models.CharField(_('Country Code'), max_length=5)
    country_name = models.CharField(_('Country Name'), max_length=255)
    name = models.CharField(_('Name'), max_length=255)
    url = models.CharField(_('URL'), max_length=255)
    text = models.TextField(_('Text'), blank=True)
    enabled = models.BooleanField(_('Enabled'), default=True)

    class Meta:
        verbose_name = _('FOI Site')
        verbose_name_plural = _('FOI Sites')

    def __str__(self):
        return '%s (%s)' % (self.name, self.country_name)

    def save(self, *args, **kwargs):
        self.country_code = self.country_code.upper()
        super(FoiSite, self).save(*args, **kwargs)

try:
    from django.contrib.gis.geoip import GeoIP
except ImportError:
    GeoIP = None  # noqa

class SiteAdivsor(object):
    def __init__(self):
        self.geoip = GeoIP()
        self.sites = None

    def update(self):
        sites = FoiSite.objects.filter(enabled=True)
        self.sites = dict([(f.country_code, f) for f in sites])

    def refresh(self):
        self.sites = None

    def get_site(self, ip):
        if self.sites is None:
            self.update()
        result = self.geoip.country(ip)
        return self.sites.get(result['country_code'], None)

class DummyAdvisor(object):
    def refresh(self):
        pass

    def get_site(self, ip):
        pass

if GeoIP and getattr(settings, 'GEOIP_PATH', False):
    advisor = SiteAdivsor()
else:
    advisor = DummyAdvisor()

@receiver(models.signals.post_save, sender=FoiSite,
        dispatch_uid="foisite_saved")
def foisite_saved(instance=None, created=False, **kwargs):
    advisor.refresh()
<commit_msg>Upgrade to GeoIP2<commit_after>from __future__ import unicode_literals

from django.db import models
from django.conf import settings
from django.dispatch import receiver
from django.utils.translation import ugettext_lazy as _
from django.utils.encoding import python_2_unicode_compatible

@python_2_unicode_compatible
class FoiSite(models.Model):
    country_code = models.CharField(_('Country Code'), max_length=5)
    country_name = models.CharField(_('Country Name'), max_length=255)
    name = models.CharField(_('Name'), max_length=255)
    url = models.CharField(_('URL'), max_length=255)
    text = models.TextField(_('Text'), blank=True)
    enabled = models.BooleanField(_('Enabled'), default=True)

    class Meta:
        verbose_name = _('FOI Site')
        verbose_name_plural = _('FOI Sites')

    def __str__(self):
        return '%s (%s)' % (self.name, self.country_name)

    def save(self, *args, **kwargs):
        self.country_code = self.country_code.upper()
        super(FoiSite, self).save(*args, **kwargs)

try:
    from django.contrib.gis.geoip2 import GeoIP2
except ImportError:
    GeoIP2 = None  # noqa

class SiteAdivsor(object):
    def __init__(self):
        self.geoip = GeoIP2()
        self.sites = None

    def update(self):
        sites = FoiSite.objects.filter(enabled=True)
        self.sites = dict([(f.country_code, f) for f in sites])

    def refresh(self):
        self.sites = None

    def get_site(self, ip):
        if self.sites is None:
            self.update()
        result = self.geoip.country(ip)
        return self.sites.get(result['country_code'], None)

class DummyAdvisor(object):
    def refresh(self):
        pass

    def get_site(self, ip):
        pass

if GeoIP2 and getattr(settings, 'GEOIP_PATH', False):
    advisor = SiteAdivsor()
else:
    advisor = DummyAdvisor()

@receiver(models.signals.post_save, sender=FoiSite,
        dispatch_uid="foisite_saved")
def foisite_saved(instance=None, created=False, **kwargs):
    advisor.refresh()
<|endoftext|>


<commit_before>from argparse import ArgumentParser
from functools import wraps
from inspect import getargspec
from itertools import izip_longest
import sys

class NoDefault(object):
  """Use to fill the list of args and default to indicate the argument doesn't
    have a default value.
  """
  pass

class Self(object):
  """Special value to use as the type of the self parameter of a method."""
  pass

def _get_args_and_defaults(args, defaults):
  """Return a list of 2-tuples - the argument name and its default value or
     a special value that indicates there is no default value.

  Args:
    args: list of argument name
    defaults: tuple of default values
  """
  args_and_defaults = []
  for (k,v) in izip_longest(args[::-1], defaults[::-1], fillvalue=NoDefault):
    args_and_defaults.append((k,v))
  return args_and_defaults[::-1]

def _prepare_doc(func, args):
  """From the function docstring get the arg parse description and arguments
    help message. If no there is no docstring simple description and help
    message are created.

  Args:
    func: the function that needs argument parsin
    args: name of the function arguments
  """
  if not func.__doc__:
    return ("Argument parsing for %s" % func.__name__, ["Help message for %s"
                                                        % arg for arg in args])
  description = []
  args_help = []
  for line in func.__doc__.split("\n"):
    if line.strip():
      description.append(line.strip())
    else:
      break
  for argument in args:
    args_help.append(" ".join([line[line.index(":") + 1:].strip()
                               for line in func.__doc__.split("\n")
                               if line.strip().startswith("%s:" % argument)]))
  return (" ".join(description), args_help)

def _get_arg_parser(func, types, args_and_defaults):
  """Return an ArgumentParser for the given function. Arguments are defined
    from the function arguments and their associated defaults.

  Args:
    func: function for which we want an ArgumentParser
    types: types to which the command line arguments should be converted to
    args_and_defaults: list of 2-tuples (arg_name, arg_default)
  """
  (description, arg_help) = _prepare_doc(func, [x for (x,y) in args_and_defaults])
  parser = ArgumentParser(description=description)
  identity_type = lambda x:x
  for ((arg, default), arg_type, help_msg) in izip_longest(args_and_defaults, types, arg_help):
    if default is NoDefault:
      arg_type = arg_type or identity_type
      parser.add_argument(arg, help=help_msg, type=arg_type)
    else:
      arg_type = arg_type or type(default)
      parser.add_argument("--%s" % arg, help=help_msg, default=default,
                          type=arg_type)
  return parser

def _get_args_to_parse(args, sys_argv):
  """Return the given argument if it is not None else sys.argv if it contains
    something, an empty list otherwise.

  Args:
    args: argument to be parsed
    sys_argv: arguments of the command line i.e. sys.argv[1:]
  """
  if args is None:
    command_line_arguments = sys_argv[1:]
    if command_line_arguments:
      return command_line_arguments
    return []
  return args

def _check_types(types, func_args, defaults):
  """Make sure that enough types were given to ensure conversion

  Args:
    types: a list of Python types to which the argument should be converted
    func_args: list of function arguments name
    defaults: tuple of default values for the function argument
  """
  if len(types) > len(func_args):
    raise AssertionError("To many types provided for conversion.")
  if len(types) < len(func_args) - len(defaults):
    raise AssertionError("Not enough types provided for conversion")

def _call(func, func_args, arguments):
  """Actually calls the function with the arguments parsed from the command line.

  Args:
    func: the function to called
    func_args: name of the function arguments
    arguments: the namespace object parse from the command line
  """
  args = []
  for argument in func_args:
    args.append(getattr(arguments, argument))
  return func(*args)

def parse_this(func, types, args=None):
  """Create an ArgumentParser for the given function converting the command line
    arguments according to the list of types.

  Args:
    func: the function for which the command line arguments to be parsed
    types: a list of types - as accepted by argparse - that will be used to
      convert the command line arguments
    args: a list of arguments to be parsed if None sys.argv is used
  """
  (func_args, varargs, keywords, defaults) = getargspec(func)
  _check_types(types, func_args, defaults)
  # Remove self from the the arguments to avoid parsing it instead of an
  # other argument
  if types[0] == Self:
    func_args = func_args[1:]
    types = types[1:]
  args_and_defaults = _get_args_and_defaults(func_args, defaults)
  parser = _get_arg_parser(func, types, args_and_defaults)
  arguments = parser.parse_args(_get_args_to_parse(args, sys.argv))
  return _call(func, func_args, arguments)

class create_parser(object):
  """Decorator that creates an argument parser for the decorated function."""

  def __init__(self, *types):
    """
    Args:
      type: vargs list of types to which the command line arguments should be
        converted to
    """
    self.types = types

  def __call__(self, func):
    """Add an argument parser attribute `parser` to the decorated function.

    Args:
      func: the function for which we want to create an argument parser
    """
    if not hasattr(func, "parser"):
      (func_args, _, _, defaults) = getargspec(func)
      _check_types(self.types, func_args, defaults)
      if self.types[0] == Self:
        func_args = func_args[1:]
        self.types = self.types[1:]
      args_and_defaults = _get_args_and_defaults(func_args, defaults)
      func.parser = _get_arg_parser(func, self.types, args_and_defaults)
    @wraps(func)
    def decorated(*args, **kwargs):
      return func(*args, **kwargs)
    return decorated

<commit_msg>Fix a few typos<commit_after>from argparse import ArgumentParser
from functools import wraps
from inspect import getargspec
from itertools import izip_longest
import sys

class NoDefault(object):
  """Use to fill the list of args and default to indicate the argument doesn't
    have a default value.
  """
  pass

class Self(object):
  """Special value to use as the type of the self parameter of a method."""
  pass

def _get_args_and_defaults(args, defaults):
  """Return a list of 2-tuples - the argument name and its default value or
     a special value that indicates there is no default value.

  Args:
    args: list of argument name
    defaults: tuple of default values
  """
  args_and_defaults = []
  for (k,v) in izip_longest(args[::-1], defaults[::-1], fillvalue=NoDefault):
    args_and_defaults.append((k,v))
  return args_and_defaults[::-1]

def _prepare_doc(func, args):
  """From the function docstring get the arg parse description and arguments
    help message. If there is no docstring simple description and help
    message are created.

  Args:
    func: the function that needs argument parsin
    args: name of the function arguments
  """
  if not func.__doc__:
    return ("Argument parsing for %s" % func.__name__, ["Help message for %s"
                                                        % arg for arg in args])
  description = []
  args_help = []
  for line in func.__doc__.split("\n"):
    if line.strip():
      description.append(line.strip())
    else:
      break
  for argument in args:
    args_help.append(" ".join([line[line.index(":") + 1:].strip()
                               for line in func.__doc__.split("\n")
                               if line.strip().startswith("%s:" % argument)]))
  return (" ".join(description), args_help)

def _get_arg_parser(func, types, args_and_defaults):
  """Return an ArgumentParser for the given function. Arguments are defined
    from the function arguments and their associated defaults.

  Args:
    func: function for which we want an ArgumentParser
    types: types to which the command line arguments should be converted to
    args_and_defaults: list of 2-tuples (arg_name, arg_default)
  """
  (description, arg_help) = _prepare_doc(func, [x for (x,y) in args_and_defaults])
  parser = ArgumentParser(description=description)
  identity_type = lambda x:x
  for ((arg, default), arg_type, help_msg) in izip_longest(args_and_defaults, types, arg_help):
    if default is NoDefault:
      arg_type = arg_type or identity_type
      parser.add_argument(arg, help=help_msg, type=arg_type)
    else:
      arg_type = arg_type or type(default)
      parser.add_argument("--%s" % arg, help=help_msg, default=default,
                          type=arg_type)
  return parser

def _get_args_to_parse(args, sys_argv):
  """Return the given arguments if it is not None else sys.argv if it contains
    something, an empty list otherwise.

  Args:
    args: argument to be parsed
    sys_argv: arguments of the command line i.e. sys.argv[1:]
  """
  if args is None:
    command_line_arguments = sys_argv[1:]
    if command_line_arguments:
      return command_line_arguments
    return []
  return args

def _check_types(types, func_args, defaults):
  """Make sure that enough types were given to ensure conversion

  Args:
    types: a list of Python types to which the argument should be converted to
    func_args: list of function arguments name
    defaults: tuple of default values for the function argument
  """
  if len(types) > len(func_args):
    raise AssertionError("To many types provided for conversion.")
  if len(types) < len(func_args) - len(defaults):
    raise AssertionError("Not enough types provided for conversion")

def _call(func, func_args, arguments):
  """Actually calls the function with the arguments parsed from the command line.

  Args:
    func: the function to called
    func_args: name of the function arguments
    arguments: the namespace object parse from the command line
  """
  args = []
  for argument in func_args:
    args.append(getattr(arguments, argument))
  return func(*args)

def parse_this(func, types, args=None):
  """Create an ArgumentParser for the given function converting the command line
    arguments according to the list of types.

  Args:
    func: the function for which the command line arguments to be parsed
    types: a list of types - as accepted by argparse - that will be used to
      convert the command line arguments
    args: a list of arguments to be parsed if None sys.argv is used
  """
  (func_args, varargs, keywords, defaults) = getargspec(func)
  _check_types(types, func_args, defaults)
  # Remove self from the arguments
  if types[0] == Self:
    func_args = func_args[1:]
    types = types[1:]
  args_and_defaults = _get_args_and_defaults(func_args, defaults)
  parser = _get_arg_parser(func, types, args_and_defaults)
  arguments = parser.parse_args(_get_args_to_parse(args, sys.argv))
  return _call(func, func_args, arguments)

class create_parser(object):
  """Creates an argument parser for the decorated function."""

  def __init__(self, *types):
    """
    Args:
      type: vargs list of types to which the command line arguments should be
        converted to
    """
    self.types = types

  def __call__(self, func):
    """Add an argument parser attribute `parser` to the decorated function.

    Args:
      func: the function for which we want to create an argument parser
    """
    if not hasattr(func, "parser"):
      (func_args, _, _, defaults) = getargspec(func)
      _check_types(self.types, func_args, defaults)
      if self.types[0] == Self:
        func_args = func_args[1:]
        self.types = self.types[1:]
      args_and_defaults = _get_args_and_defaults(func_args, defaults)
      func.parser = _get_arg_parser(func, self.types, args_and_defaults)
    @wraps(func)
    def decorated(*args, **kwargs):
      return func(*args, **kwargs)
    return decorated

<|endoftext|>


<commit_before>from adk.bloom import *

import random
import os.path

cur = os.path.realpath(__file__)
wordFile = os.path.normpath(os.path.join(cur, os.pardir, os.pardir, os.pardir, 'Blogs', 'artifacts', 'searching', 'words.english.txt'))

def hf1(s, size):
    return hash(s) % size

def jenkins_one_at_a_time_hash(s, size):
    """http://en.wikipedia.org/wiki/Jenkins_hash_function."""
    h = 0
    for c in s:
        h += ord(c)
        h += (h << 10)
        h ^= (h >> 6)
    h += (h << 3);
    h ^= (h >> 11);
    h += (h << 15);
    return h % size;

def hf3(s, size):
    """Hashcode implementation from Java String class."""
    h = 0
    for c in s:
        h = 31*h + ord(c)
    return h % size

def evalBloom(size, strings, missing):
    """
    Returns the total number of false positives when searching for the elements of missing after
    creating a Bloom Filter of the given input strings.
    """
    bloom = bloomFilter(size, [hf1, jenkins_one_at_a_time_hash, hf3])

    for s in missing:
        if s in strings:
            print ("input contains a missing word:", s, ". Try again with different input.")
            return None

    # Create Bloom Filter
    for s in strings:
        bloom.add(s)

    # Now that bloom filter is built, try the missing words
    numFalse = 0
    for s in missing:
        if bloom.contains(s):
            numFalse += 1

    return numFalse

# grab every word and add to bloom filter. Must strip '\n' from each. Use ascending
# and descending orders
wf = open(wordFile)
raw = wf.readlines()
words = [w[:-1] for w in raw]

wf.close()

letters = 'abcdefghijklmnopqrstuvwxyz'

base = 2135

for size in range(100000,2000000,10000):
    copy = words[:]
    random.shuffle(copy)
    missing = copy[:base]
    copy = copy[base:]

    wordFalsePositive = evalBloom(size, copy, missing)

    # generate random strings of between 2 and 10 characters in length
    randomStrings = []
    while len(randomStrings) < base:
        rlen = random.randint(2,10)
        w = ''
        for i in range(rlen):
            w = w + letters[random.randint(0,25)]
        if w not in words:
            randomStrings.append(w)

    randomFalsePositive = evalBloom(size, copy, randomStrings)

    n = len(copy)
    k = 3

    computed = (1 - (1-1.0/size) ** (k*n)) ** k
    fp  = '%.4f' % (1.0*wordFalsePositive/base)
    rfp = '%.4f' % (1.0*randomFalsePositive/base)
    cfp = '%.4f' % computed

    print (str(size) + "\t" + fp + "\t" + rfp + "\t" + cfp)
<commit_msg>changed way 'contains' was written in bloomfilter<commit_after>from adk.bloom import *

import random
import os.path

cur = os.path.realpath(__file__)
wordFile = os.path.normpath(os.path.join(cur, os.pardir, os.pardir, os.pardir, 'Blogs', 'artifacts', 'searching', 'words.english.txt'))

def hf1(s, size):
    return hash(s) % size

def jenkins_one_at_a_time_hash(s, size):
    """http://en.wikipedia.org/wiki/Jenkins_hash_function."""
    h = 0
    for c in s:
        h += ord(c)
        h += (h << 10)
        h ^= (h >> 6)
    h += (h << 3);
    h ^= (h >> 11);
    h += (h << 15);
    return h % size;

def hf3(s, size):
    """Hashcode implementation from Java String class."""
    h = 0
    for c in s:
        h = 31*h + ord(c)
    return h % size

def evalBloom(size, strings, missing):
    """
    Returns the total number of false positives when searching for the elements of missing after
    creating a Bloom Filter of the given input strings.
    """
    bloom = bloomFilter(size, [hf1, jenkins_one_at_a_time_hash, hf3])

    for s in missing:
        if s in strings:
            print ("input contains a missing word:", s, ". Try again with different input.")
            return None

    # Create Bloom Filter
    for s in strings:
        bloom.add(s)

    # Now that bloom filter is built, try the missing words
    numFalse = 0
    for s in missing:
        if s in bloom:
            numFalse += 1

    return numFalse

# grab every word and add to bloom filter. Must strip '\n' from each. Use ascending
# and descending orders
wf = open(wordFile)
raw = wf.readlines()
words = [w[:-1] for w in raw]

wf.close()

letters = 'abcdefghijklmnopqrstuvwxyz'

base = 2135

for size in range(100000,2000000,10000):
    copy = words[:]
    random.shuffle(copy)
    missing = copy[:base]
    copy = copy[base:]

    wordFalsePositive = evalBloom(size, copy, missing)

    # generate random strings of between 2 and 10 characters in length
    randomStrings = []
    while len(randomStrings) < base:
        rlen = random.randint(2,10)
        w = ''
        for i in range(rlen):
            w = w + letters[random.randint(0,25)]
        if w not in words:
            randomStrings.append(w)

    randomFalsePositive = evalBloom(size, copy, randomStrings)

    n = len(copy)
    k = 3

    computed = (1 - (1-1.0/size) ** (k*n)) ** k
    fp  = '%.4f' % (1.0*wordFalsePositive/base)
    rfp = '%.4f' % (1.0*randomFalsePositive/base)
    cfp = '%.4f' % computed

    print (str(size) + "\t" + fp + "\t" + rfp + "\t" + cfp)
<|endoftext|>


<commit_before># Shared functions for postcode and area importing.

import re
import sys
from xml.sax.handler import ContentHandler
import shapely.ops
import shapely.wkt
from django.contrib.gis.geos import GEOSGeometry, MultiPolygon, Polygon

class KML(ContentHandler):
    def __init__(self, *args, **kwargs):
        self.content = ''
        self.data = {}

    def characters(self, content):
        self.content += content

    @staticmethod
    def normalize_whitespace(s):
        return re.sub('(?us)\s+', ' ', s).strip()

    def endElement(self, name):
        if name == 'name':
            self.current = {}
            self.data[self.normalize_whitespace(self.content)] = self.current
        elif name == 'value':
            self.current[self.name] = self.content.strip()
            self.name = None
        self.content = ''

    def startElement(self, name, attr):
        if name == 'Data':
            self.name = self.normalize_whitespace(attr['name'])

def save_polygons(lookup):
    for shape in lookup.values():
        m, poly = shape
        if not poly:
            continue
        sys.stdout.write(".")
        sys.stdout.flush()
        #g = OGRGeometry(OGRGeomType('MultiPolygon'))
        m.polygons.all().delete()
        for p in poly:
            if p.geom_name == 'POLYGON':
                shapes = [ p ]
            else:
                shapes = p
            for g in shapes:
                # Ignore any shape with fewer than four points, to
                # avoid introducing invalid polygons into the
                # database.
                if g.point_count < 4:
                    continue
                # XXX Using g.wkt directly when importing Norway KML works fine
                # with Django 1.1, Postgres 8.3, PostGIS 1.3.3 but fails with
                # Django 1.2, Postgres 8.4, PostGIS 1.5.1, saying that the
                # dimensions constraint fails - because it is trying to import
                # a shape as 3D as the WKT contains " 0" at the end of every
                # co-ordinate. Removing the altitudes from the KML, and/or
                # using altitudeMode makes no difference to the WKT here, so
                # the only easy solution appears to be removing the altitude
                # directly from the WKT before using it.
                must_be_two_d = g.wkt.replace(' 0,', ',')
                m.polygons.create(polygon=must_be_two_d)
        #m.polygon = g.wkt
        #m.save()
        poly[:] = [] # Clear the polygon's list, so that if it has both an ons_code and unit_id, it's not processed twice
    print ""

def fix_with_buffer(geos_polygon):
    return geos_polygon.buffer(0)

def fix_with_exterior_union_polygonize(geos_polygon):
    exterior_ring = geos_polygon.exterior_ring
    unioned = exterior_ring.union(exterior_ring)
    # We want to use GEOSPolygonize which isn't exposed via
    # django.contrib.gis.geos, but is available via shapely:
    shapely_unioned = shapely.wkt.loads(unioned.wkt)
    reconstructed_geos_polygons = [
        GEOSGeometry(sp.wkt, geos_polygon.srid) for sp in
        shapely.ops.polygonize(shapely_unioned)]
    return MultiPolygon(reconstructed_geos_polygons)

def fix_invalid_geos_polygon(geos_polygon, methods=('buffer', 'exterior')):
    """Try to make a valid version of an invalid GEOS polygon

    The test cases and techniques used here are from the helpful
    presentation here: http://s3.opengeo.org/postgis-power.pdf

      3  ------>------
         |           |
         |           |
      2  |     x     |
         |    / \    |
         ^   /   \   |
      1  |  x     x  |
         |   \   /   |
         |    \ /    |
      0  --<--| |---<--

         0  1  2  3  4

    This is the "banana polygon" example, if you imagine the points at
    (2, 0) drawn together to be the same point.

    >>> from django.contrib.gis.geos import Polygon
    >>> coords = [(0, 0), (0, 3), (4, 3), (4, 0),
    ...           (2, 0), (3, 1), (2, 2), (1, 1),
    ...           (2, 0), (0, 0)]
    >>> poly = Polygon(coords)
    >>> poly.valid
    False

    That one should be fixable just with the ST_Buffer(_, 0) technique:

    >>> fixed = fix_invalid_geos_polygon(poly, 'buffer')
    >>> fixed.valid
    True
    >>> len(fixed)
    2
    >>> import math
    >>> expected_length = 3 + 3 + 4 + 4 + 4 * math.sqrt(2)
    >>> abs(fixed.length - expected_length) < 0.000001
    True

    Others need the more complex technique mentioned in that PDF, such
    as this figure-of-eight polygon:

      2         -->--
               |     |
               |     |
      1   --<-----<--
         |     |
         |     |
      0   -->--

         0     1     2

    ... with coordinates as follows:

    >>> coords = [(0, 0), (1, 0), (1, 2), (2, 2),
    ...           (2, 1), (0, 1), (0, 0)]
    >>> poly = Polygon(coords)
    >>> poly.valid
    False

    The function should return a valid version with the right
    perimeter length:

    >>> fixed = fix_invalid_geos_polygon(poly, ('buffer', 'exterior'))
    >>> fixed.valid
    True
    >>> fixed.length
    8.0

    Also, check that this can fix a invalid polygon that's equivalent
    to four separate polygons:

      2  ---x---
         | / \ |
         |/   \|
      1  x     x
         |\   /|
         | \ / |
      0  ---x---

         0  1  2

    ... where the points start at the bottom 'x', go right around the
    outside square clockwise and then go around the inside square
    anti-clockwise, creating 4 filled triangles with a square hole in
    the middle:

    >>> coords = [(1, 0), (0, 0), (0, 2), (2, 2), (2, 0), (1, 0),
    ...           (2, 1), (1, 2), (0, 1), (1, 0)]
    >>> poly = Polygon(coords)
    >>> poly.valid
    False

    Try to fix it:

    >>> fixed = fix_invalid_geos_polygon(poly)
    >>> fixed.valid
    True
    >>> len(fixed)
    4
    >>> expected_length = 2 + 2 + 2 + 2 + 4 * math.sqrt(2)
    >>> abs(fixed.length - expected_length) < 0.000001
    True
    """

    cutoff = 0.01
    original_length = geos_polygon.length

    for method, fix_function in (
        ('buffer', fix_with_buffer),
        ('exterior', fix_with_exterior_union_polygonize)
        ):
        if method in methods:
            fixed = fix_function(geos_polygon)
            if not fixed:
                continue
            difference = abs(original_length - fixed.length)
            if (difference / float(original_length)) < cutoff:
                return fixed
    return None

def fix_invalid_geos_multipolygon(geos_multipolygon):
    """Try to fix an invalid GEOS MultiPolygon

    Two overlapping valid polyons should be unioned to one shape:

    3  ---------                3  ---------
       | A     |                   | C     |
    2  |   ----|----            2  |       -----
       |   |   |   |   --->        |           |
    1  ----|----   |            1  -----       |
           |     B |                   |       |
    0      ---------            0      ---------

       0   1   2   3               0   1   2   3

    >>> coords_a = [(0, 1), (0, 3), (2, 3), (2, 1), (0, 1)]
    >>> coords_b = [(1, 0), (1, 2), (3, 2), (3, 0), (1, 0)]
    >>> coords_c = [(0, 1), (0, 3), (2, 3), (2, 2), (3, 2),
    ...             (3, 0), (1, 0), (1, 1), (0, 1)]

    >>> mp = MultiPolygon(Polygon(coords_a), Polygon(coords_b))
    >>> mp.valid
    False

    >>> fixed_mp = fix_invalid_geos_multipolygon(mp)
    >>> fixed_mp.valid
    True
    >>> expected_polygon = Polygon(coords_c)
    >>> fixed_mp.equals(expected_polygon)
    True

    If there's one valid and one fixable invalid polygon in the
    multipolygon, it should return a multipolygon with the valid one
    and the fixed version:

      2         -->--
               | D   |
               |     |
      1   --<-----<--       -----
         |     |           | E   |
         |     |           |     |
      0   -->--             -----

         0     1     2     3     4

    >>> coords_d = [(0, 0), (1, 0), (1, 2), (2, 2),
    ...             (2, 1), (0, 1), (0, 0)]
    >>> coords_e = [(3, 0), (3, 1), (4, 1), (4, 0), (3, 0)]
    >>> mp = MultiPolygon(Polygon(coords_d), Polygon(coords_e))
    >>> mp.valid
    False

    >>> fixed_mp = fix_invalid_geos_multipolygon(mp)
    >>> fixed_mp.valid
    True

    The eventual result should be three squares:

    >>> fixed_mp.num_geom
    3
    >>> fixed_mp.area
    3.0
    >>> expected_polygon = MultiPolygon(Polygon([(0, 0), (0, 1), (1, 1), (1, 0), (0, 0)]),
    ...                                 Polygon([(1, 1), (1, 2), (2, 2), (2, 1), (1, 1)]),
    ...                                 Polygon([(3, 0), (3, 1), (4, 1), (4, 0), (3, 0)]))
    >>> fixed_mp.equals(expected_polygon)
    True

    If all the polygons are invalid and unfixable, an empty
    MultiPolygon will be returned.  This example, where the loop
    around the inner diamond is traversed clockwise as well, seems to
    be unfixable, so it's a good example for this:

      2  ---x---
         | / \ |
         |/   \|
      1  x     x
         |\   /|
         | \ / |
      0  ---x---

         0  1  2

    Here the points start at the bottom 'x', go right around the
    outside square clockwise and then go around the inside square
    clockwise as well.

    >>> coords = [(1, 0), (0, 0), (0, 2), (2, 2), (2, 0), (1, 0),
    ...           (0, 1), (1, 2), (2, 1), (1, 0)]
    >>> poly = Polygon(coords)
    >>> poly.valid
    False
    >>> mp = MultiPolygon(poly)
    >>> fixed = fix_invalid_geos_multipolygon(mp)
    >>> len(fixed)
    0

    """

    polygons = list(geos_multipolygon)
    # If all of the polygons in the KML are individually
    # valid, then we just need to union them:
    individually_all_valid = all(p.valid for p in polygons)
    if individually_all_valid:
        for_union = geos_multipolygon
    # Otherwise, try to fix the individually broken
    # polygons, discard any unfixable ones, and union
    # the result:
    else:
        valid_polygons = []
        for p in polygons:
            if p.valid:
                valid_polygons.append(p)
            else:
                fixed = fix_invalid_geos_polygon(p)
                if fixed is not None:
                    if fixed.geom_type == 'MultiPolygon':
                        valid_polygons += list(fixed)
                    elif fixed.geom_type == 'Polygon':
                        valid_polygons.append(fixed)
                    else:
                        raise "Unknown fixed geometry type:", fixed.geom_type
        for_union = MultiPolygon(valid_polygons)
    if len(for_union) > 0:
        result = for_union.cascaded_union
        # If it have been unioned into a single Polygon, still return
        # a MultiPolygon, for consistency of return types:
        if result.geom_type == 'Polygon':
            result = MultiPolygon(result)
    else:
        result = for_union
    return result

if __name__ == "__main__":
    import doctest
    doctest.testmod()
<commit_msg>Ignore an exception thrown by earlier versions of Shapely<commit_after># Shared functions for postcode and area importing.

import re
import sys
from xml.sax.handler import ContentHandler
import shapely.ops
import shapely.wkt
from django.contrib.gis.geos import GEOSGeometry, MultiPolygon, Polygon

class KML(ContentHandler):
    def __init__(self, *args, **kwargs):
        self.content = ''
        self.data = {}

    def characters(self, content):
        self.content += content

    @staticmethod
    def normalize_whitespace(s):
        return re.sub('(?us)\s+', ' ', s).strip()

    def endElement(self, name):
        if name == 'name':
            self.current = {}
            self.data[self.normalize_whitespace(self.content)] = self.current
        elif name == 'value':
            self.current[self.name] = self.content.strip()
            self.name = None
        self.content = ''

    def startElement(self, name, attr):
        if name == 'Data':
            self.name = self.normalize_whitespace(attr['name'])

def save_polygons(lookup):
    for shape in lookup.values():
        m, poly = shape
        if not poly:
            continue
        sys.stdout.write(".")
        sys.stdout.flush()
        #g = OGRGeometry(OGRGeomType('MultiPolygon'))
        m.polygons.all().delete()
        for p in poly:
            if p.geom_name == 'POLYGON':
                shapes = [ p ]
            else:
                shapes = p
            for g in shapes:
                # Ignore any shape with fewer than four points, to
                # avoid introducing invalid polygons into the
                # database.
                if g.point_count < 4:
                    continue
                # XXX Using g.wkt directly when importing Norway KML works fine
                # with Django 1.1, Postgres 8.3, PostGIS 1.3.3 but fails with
                # Django 1.2, Postgres 8.4, PostGIS 1.5.1, saying that the
                # dimensions constraint fails - because it is trying to import
                # a shape as 3D as the WKT contains " 0" at the end of every
                # co-ordinate. Removing the altitudes from the KML, and/or
                # using altitudeMode makes no difference to the WKT here, so
                # the only easy solution appears to be removing the altitude
                # directly from the WKT before using it.
                must_be_two_d = g.wkt.replace(' 0,', ',')
                m.polygons.create(polygon=must_be_two_d)
        #m.polygon = g.wkt
        #m.save()
        poly[:] = [] # Clear the polygon's list, so that if it has both an ons_code and unit_id, it's not processed twice
    print ""

def fix_with_buffer(geos_polygon):
    return geos_polygon.buffer(0)

def fix_with_exterior_union_polygonize(geos_polygon):
    exterior_ring = geos_polygon.exterior_ring
    unioned = exterior_ring.union(exterior_ring)
    # We want to use GEOSPolygonize which isn't exposed via
    # django.contrib.gis.geos, but is available via shapely:
    shapely_unioned = shapely.wkt.loads(unioned.wkt)
    try:
        reconstructed_geos_polygons = [
            GEOSGeometry(sp.wkt, geos_polygon.srid) for sp in
            shapely.ops.polygonize(shapely_unioned)]
    except ValueError:
        reconstructed_geos_polygons = []
    return MultiPolygon(reconstructed_geos_polygons)

def fix_invalid_geos_polygon(geos_polygon, methods=('buffer', 'exterior')):
    """Try to make a valid version of an invalid GEOS polygon

    The test cases and techniques used here are from the helpful
    presentation here: http://s3.opengeo.org/postgis-power.pdf

      3  ------>------
         |           |
         |           |
      2  |     x     |
         |    / \    |
         ^   /   \   |
      1  |  x     x  |
         |   \   /   |
         |    \ /    |
      0  --<--| |---<--

         0  1  2  3  4

    This is the "banana polygon" example, if you imagine the points at
    (2, 0) drawn together to be the same point.

    >>> from django.contrib.gis.geos import Polygon
    >>> coords = [(0, 0), (0, 3), (4, 3), (4, 0),
    ...           (2, 0), (3, 1), (2, 2), (1, 1),
    ...           (2, 0), (0, 0)]
    >>> poly = Polygon(coords)
    >>> poly.valid
    False

    That one should be fixable just with the ST_Buffer(_, 0) technique:

    >>> fixed = fix_invalid_geos_polygon(poly, 'buffer')
    >>> fixed.valid
    True
    >>> len(fixed)
    2
    >>> import math
    >>> expected_length = 3 + 3 + 4 + 4 + 4 * math.sqrt(2)
    >>> abs(fixed.length - expected_length) < 0.000001
    True

    Others need the more complex technique mentioned in that PDF, such
    as this figure-of-eight polygon:

      2         -->--
               |     |
               |     |
      1   --<-----<--
         |     |
         |     |
      0   -->--

         0     1     2

    ... with coordinates as follows:

    >>> coords = [(0, 0), (1, 0), (1, 2), (2, 2),
    ...           (2, 1), (0, 1), (0, 0)]
    >>> poly = Polygon(coords)
    >>> poly.valid
    False

    The function should return a valid version with the right
    perimeter length:

    >>> fixed = fix_invalid_geos_polygon(poly, ('buffer', 'exterior'))
    >>> fixed.valid
    True
    >>> fixed.length
    8.0

    Also, check that this can fix a invalid polygon that's equivalent
    to four separate polygons:

      2  ---x---
         | / \ |
         |/   \|
      1  x     x
         |\   /|
         | \ / |
      0  ---x---

         0  1  2

    ... where the points start at the bottom 'x', go right around the
    outside square clockwise and then go around the inside square
    anti-clockwise, creating 4 filled triangles with a square hole in
    the middle:

    >>> coords = [(1, 0), (0, 0), (0, 2), (2, 2), (2, 0), (1, 0),
    ...           (2, 1), (1, 2), (0, 1), (1, 0)]
    >>> poly = Polygon(coords)
    >>> poly.valid
    False

    Try to fix it:

    >>> fixed = fix_invalid_geos_polygon(poly)
    >>> fixed.valid
    True
    >>> len(fixed)
    4
    >>> expected_length = 2 + 2 + 2 + 2 + 4 * math.sqrt(2)
    >>> abs(fixed.length - expected_length) < 0.000001
    True
    """

    cutoff = 0.01
    original_length = geos_polygon.length

    for method, fix_function in (
        ('buffer', fix_with_buffer),
        ('exterior', fix_with_exterior_union_polygonize)
        ):
        if method in methods:
            fixed = fix_function(geos_polygon)
            if not fixed:
                continue
            difference = abs(original_length - fixed.length)
            if (difference / float(original_length)) < cutoff:
                return fixed
    return None

def fix_invalid_geos_multipolygon(geos_multipolygon):
    """Try to fix an invalid GEOS MultiPolygon

    Two overlapping valid polyons should be unioned to one shape:

    3  ---------                3  ---------
       | A     |                   | C     |
    2  |   ----|----            2  |       -----
       |   |   |   |   --->        |           |
    1  ----|----   |            1  -----       |
           |     B |                   |       |
    0      ---------            0      ---------

       0   1   2   3               0   1   2   3

    >>> coords_a = [(0, 1), (0, 3), (2, 3), (2, 1), (0, 1)]
    >>> coords_b = [(1, 0), (1, 2), (3, 2), (3, 0), (1, 0)]
    >>> coords_c = [(0, 1), (0, 3), (2, 3), (2, 2), (3, 2),
    ...             (3, 0), (1, 0), (1, 1), (0, 1)]

    >>> mp = MultiPolygon(Polygon(coords_a), Polygon(coords_b))
    >>> mp.valid
    False

    >>> fixed_mp = fix_invalid_geos_multipolygon(mp)
    >>> fixed_mp.valid
    True
    >>> expected_polygon = Polygon(coords_c)
    >>> fixed_mp.equals(expected_polygon)
    True

    If there's one valid and one fixable invalid polygon in the
    multipolygon, it should return a multipolygon with the valid one
    and the fixed version:

      2         -->--
               | D   |
               |     |
      1   --<-----<--       -----
         |     |           | E   |
         |     |           |     |
      0   -->--             -----

         0     1     2     3     4

    >>> coords_d = [(0, 0), (1, 0), (1, 2), (2, 2),
    ...             (2, 1), (0, 1), (0, 0)]
    >>> coords_e = [(3, 0), (3, 1), (4, 1), (4, 0), (3, 0)]
    >>> mp = MultiPolygon(Polygon(coords_d), Polygon(coords_e))
    >>> mp.valid
    False

    >>> fixed_mp = fix_invalid_geos_multipolygon(mp)
    >>> fixed_mp.valid
    True

    The eventual result should be three squares:

    >>> fixed_mp.num_geom
    3
    >>> fixed_mp.area
    3.0
    >>> expected_polygon = MultiPolygon(Polygon([(0, 0), (0, 1), (1, 1), (1, 0), (0, 0)]),
    ...                                 Polygon([(1, 1), (1, 2), (2, 2), (2, 1), (1, 1)]),
    ...                                 Polygon([(3, 0), (3, 1), (4, 1), (4, 0), (3, 0)]))
    >>> fixed_mp.equals(expected_polygon)
    True

    If all the polygons are invalid and unfixable, an empty
    MultiPolygon will be returned.  This example, where the loop
    around the inner diamond is traversed clockwise as well, seems to
    be unfixable, so it's a good example for this:

      2  ---x---
         | / \ |
         |/   \|
      1  x     x
         |\   /|
         | \ / |
      0  ---x---

         0  1  2

    Here the points start at the bottom 'x', go right around the
    outside square clockwise and then go around the inside square
    clockwise as well.

    >>> coords = [(1, 0), (0, 0), (0, 2), (2, 2), (2, 0), (1, 0),
    ...           (0, 1), (1, 2), (2, 1), (1, 0)]
    >>> poly = Polygon(coords)
    >>> poly.valid
    False
    >>> mp = MultiPolygon(poly)
    >>> fixed = fix_invalid_geos_multipolygon(mp)
    >>> len(fixed)
    0

    """

    polygons = list(geos_multipolygon)
    # If all of the polygons in the KML are individually
    # valid, then we just need to union them:
    individually_all_valid = all(p.valid for p in polygons)
    if individually_all_valid:
        for_union = geos_multipolygon
    # Otherwise, try to fix the individually broken
    # polygons, discard any unfixable ones, and union
    # the result:
    else:
        valid_polygons = []
        for p in polygons:
            if p.valid:
                valid_polygons.append(p)
            else:
                fixed = fix_invalid_geos_polygon(p)
                if fixed is not None:
                    if fixed.geom_type == 'MultiPolygon':
                        valid_polygons += list(fixed)
                    elif fixed.geom_type == 'Polygon':
                        valid_polygons.append(fixed)
                    else:
                        raise "Unknown fixed geometry type:", fixed.geom_type
        for_union = MultiPolygon(valid_polygons)
    if len(for_union) > 0:
        result = for_union.cascaded_union
        # If it have been unioned into a single Polygon, still return
        # a MultiPolygon, for consistency of return types:
        if result.geom_type == 'Polygon':
            result = MultiPolygon(result)
    else:
        result = for_union
    return result

if __name__ == "__main__":
    import doctest
    doctest.testmod()
<|endoftext|>


<commit_before>#!/usr/bin/env python
from __future__ import print_function, division

from submit import submit
from error import LSFError
from utility import color

import sys
import os
import argparse
import re
import subprocess

def esub(args, bsubargs, jobscript):
    if args.show:
        try:
            os.unlink(".esubrecord")
        except:
            pass
        return
    data = {"Command": ""}
    if args.aices:
        data["-P"] = "aices"
    if args.aices2:
        data["-P"] = "aices2"
    last = False
    cmd = False
    for arg in bsubargs:
        if cmd:
            data["Command"] += " " + arg
        if arg[0] == "-":
            if last:
                data[last] = True
            last = arg
        else:
            if last:
                data[last] = arg
                last = False
            else:
                cmd = True
                data["Command"] += " " + arg
    if last:
        data[last] = True
    for line in jobscript.splitlines(True):
        if line.startswith("#BSUB"):
            line = "#" + line.split("#")[1]
            match = re.match("#BSUB (-\w+)$", line)
            if match:
                data[match.groups()[0]] = True
            match = re.match("#BSUB (-\w+) \"?(.*?)\"?$", line)
            if match:
                key, value = match.groups()
                if key in data:
                    if not isinstance(data[key], list):
                        data[key] = [data[key]]
                    data[key].append(value)
                else:
                    data[key] = value
        else:
            data["Command"] += line
    try:
        job = submit(data)
        if args.record:
            if not os.path.isfile(".esubrecord"):
                with open(".esubrecord", "w") as fout:
                    fout.write("esub temporary file; can safely be deleted")
                subprocess.call(["ejobs", str(job["Job"])])
            else:
                subprocess.Popen(["ejobs", "--noheader", str(job["Job"])])
        else:
            subprocess.call(["ejobs", str(job["Job"])])
    except LSFError as e:
        print(color(e.strerror, "r"))
        sys.exit(-1)

def main():
    parser = argparse.ArgumentParser(
        description="Wrapper for bsub."
    )
    parser.add_argument(
        "--record",
        help="record submitted job ids",
        action="store_true",
    )
    parser.add_argument(
        "--show",
        help="list recorded jobs",
        action="store_true",
    )
    parser.add_argument(
        "-aices",
        help="short for -P aices",
        action="store_true",
    )
    parser.add_argument(
        "-aices2",
        help="short for -P aices2",
        action="store_true",
    )
    parser.add_argument_group("further arguments",
                              description="are passed to bsub")

    args, bsubargs = parser.parse_known_args()

    jobscript = None
    if not args.show:
        jobscript = sys.stdin.read()

    esub(args, bsubargs, jobscript)

if __name__ == "__main__":
    main()
<commit_msg>another try on waiting<commit_after>#!/usr/bin/env python
from __future__ import print_function, division

from submit import submit
from error import LSFError
from utility import color

import sys
import os
import argparse
import re
import subprocess

def esub(args, bsubargs, jobscript):
    if args.show:
        if os.path.isfile(".esubrecord"):
            with open(".esubrecord") as fin:
                cmd = ["wait"] + map(str.strip, fin)
                subprocess.call(cmd, shell=True)
            os.unlink(".esubrecord")
        return
    data = {"Command": ""}
    if args.aices:
        data["-P"] = "aices"
    if args.aices2:
        data["-P"] = "aices2"
    last = False
    cmd = False
    for arg in bsubargs:
        if cmd:
            data["Command"] += " " + arg
        if arg[0] == "-":
            if last:
                data[last] = True
            last = arg
        else:
            if last:
                data[last] = arg
                last = False
            else:
                cmd = True
                data["Command"] += " " + arg
    if last:
        data[last] = True
    for line in jobscript.splitlines(True):
        if line.startswith("#BSUB"):
            line = "#" + line.split("#")[1]
            match = re.match("#BSUB (-\w+)$", line)
            if match:
                data[match.groups()[0]] = True
            match = re.match("#BSUB (-\w+) \"?(.*?)\"?$", line)
            if match:
                key, value = match.groups()
                if key in data:
                    if not isinstance(data[key], list):
                        data[key] = [data[key]]
                    data[key].append(value)
                else:
                    data[key] = value
        else:
            data["Command"] += line
    try:
        job = submit(data)
        if args.record:
            if not os.path.isfile(".esubrecord"):
                with open(".esubrecord", "w") as fout:
                    pass
                subprocess.call(["ejobs", str(job["Job"])])
            else:
                p = subprocess.Popen(["ejobs", "--noheader", str(job["Job"])])
                with open(".esubrecord", "a") as fout:
                    print(p.pid, file=fout)
        else:
            subprocess.call(["ejobs", str(job["Job"])])
    except LSFError as e:
        print(color(e.strerror, "r"))
        sys.exit(-1)

def main():
    parser = argparse.ArgumentParser(
        description="Wrapper for bsub."
    )
    parser.add_argument(
        "--record",
        help="record submitted job ids",
        action="store_true",
    )
    parser.add_argument(
        "--show",
        help="list recorded jobs",
        action="store_true",
    )
    parser.add_argument(
        "-aices",
        help="short for -P aices",
        action="store_true",
    )
    parser.add_argument(
        "-aices2",
        help="short for -P aices2",
        action="store_true",
    )
    parser.add_argument_group("further arguments",
                              description="are passed to bsub")

    args, bsubargs = parser.parse_known_args()

    jobscript = None
    if not args.show:
        jobscript = sys.stdin.read()

    esub(args, bsubargs, jobscript)

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


<commit_before># -*- encoding: utf-8 -*-
##############################################################################
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU Affero General Public License as published
#    by the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program 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 this program.  If not, see http://www.gnu.org/licenses/.
#
##############################################################################

from openerp import models, fields, api, exceptions, _

class MrpProduction(models.Model):
    _inherit = 'mrp.production'

    @api.multi
    def _action_compute_lines(self, properties=None):
        res = super(MrpProduction,
                    self)._action_compute_lines(properties=properties)
        self._get_workorder_in_product_lines(self.workcenter_lines,
                                             self.product_lines)
        return res

    def _get_workorder_in_product_lines(self, workcenter_lines, product_lines):
        for p_line in product_lines:
            for bom_line in self.bom_id.bom_line_ids:
                if bom_line.product_id.id == p_line.product_id.id:
                    for wc_line in workcenter_lines:
                        if wc_line.routing_wc_line.id == bom_line.operation.id:
                            p_line.work_order = wc_line.id
                            break

    def _get_workorder_in_move_lines(self, product_lines, move_lines):
        for move_line in move_lines:
            for product_line in product_lines:
                if product_line.product_id.id == move_line.product_id.id:
                    move_line.work_order = product_line.work_order.id

    @api.multi
    def action_confirm(self):
        produce = False
        for workcenter_line in self.workcenter_lines:
            if workcenter_line.do_production:
                produce = True
                break
        if not produce:
            raise exceptions.Warning(
                _('Produce Operation'), _('At least one operation '
                                          'must have checked '
                                          '"Move produced quantity to stock"'
                                          'field'))
        res = super(MrpProduction, self).action_confirm()
        self._get_workorder_in_move_lines(self.product_lines, self.move_lines)
        return res

    @api.multi
    def action_compute(self, properties=None):
        res = super(MrpProduction, self).action_compute(properties=properties)
        self._get_workorder_in_move_lines(self.product_lines, self.move_lines)
        return res

class MrpProductionProductLine(models.Model):
    _inherit = 'mrp.production.product.line'

    work_order = fields.Many2one('mrp.production.workcenter.line',
                                 'Work Order')

class MrpProductionWorkcenterLine(models.Model):
    _inherit = 'mrp.production.workcenter.line'

    product_line = fields.One2many('mrp.production.product.line',
                                   'work_order', string='Product Lines')
    routing_wc_line = fields.Many2one('mrp.routing.workcenter',
                                      string='Routing WC Line')
    do_production = fields.Boolean(
        string='Move Final Product to Stock')

    @api.model
    def create(self, data):
        workcenter_obj = self.env['mrp.routing.workcenter']
        if 'routing_wc_line' in data:
            routing_wc_line_id = data.get('routing_wc_line')
            work = workcenter_obj.browse(routing_wc_line_id)
            data.update({'do_production':
                         work.operation.do_production})
        return super(MrpProductionWorkcenterLine, self).create(data)
<commit_msg>[FIX] mrp_operations_extension: When no routing_id is defined in the manufacturing order move Final Product to Stock check in operation is not controlled.<commit_after># -*- encoding: utf-8 -*-
##############################################################################
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU Affero General Public License as published
#    by the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program 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 this program.  If not, see http://www.gnu.org/licenses/.
#
##############################################################################

from openerp import models, fields, api, exceptions, _

class MrpProduction(models.Model):
    _inherit = 'mrp.production'

    @api.multi
    def _action_compute_lines(self, properties=None):
        res = super(MrpProduction,
                    self)._action_compute_lines(properties=properties)
        self._get_workorder_in_product_lines(self.workcenter_lines,
                                             self.product_lines)
        return res

    def _get_workorder_in_product_lines(self, workcenter_lines, product_lines):
        for p_line in product_lines:
            for bom_line in self.bom_id.bom_line_ids:
                if bom_line.product_id.id == p_line.product_id.id:
                    for wc_line in workcenter_lines:
                        if wc_line.routing_wc_line.id == bom_line.operation.id:
                            p_line.work_order = wc_line.id
                            break

    def _get_workorder_in_move_lines(self, product_lines, move_lines):
        for move_line in move_lines:
            for product_line in product_lines:
                if product_line.product_id.id == move_line.product_id.id:
                    move_line.work_order = product_line.work_order.id

    @api.multi
    def action_confirm(self):
        produce = False
        if not self.routing_id:
            produce = True
        else:
            for workcenter_line in self.workcenter_lines:
                if workcenter_line.do_production:
                    produce = True
                    break
        if not produce:
            raise exceptions.Warning(
                _('Produce Operation'), _('At least one operation '
                                          'must have checked '
                                          '"Move produced quantity to stock"'
                                          'field'))
        res = super(MrpProduction, self).action_confirm()
        self._get_workorder_in_move_lines(self.product_lines, self.move_lines)
        return res

    @api.multi
    def action_compute(self, properties=None):
        res = super(MrpProduction, self).action_compute(properties=properties)
        self._get_workorder_in_move_lines(self.product_lines, self.move_lines)
        return res

class MrpProductionProductLine(models.Model):
    _inherit = 'mrp.production.product.line'

    work_order = fields.Many2one('mrp.production.workcenter.line',
                                 'Work Order')

class MrpProductionWorkcenterLine(models.Model):
    _inherit = 'mrp.production.workcenter.line'

    product_line = fields.One2many('mrp.production.product.line',
                                   'work_order', string='Product Lines')
    routing_wc_line = fields.Many2one('mrp.routing.workcenter',
                                      string='Routing WC Line')
    do_production = fields.Boolean(
        string='Move Final Product to Stock')

    @api.model
    def create(self, data):
        workcenter_obj = self.env['mrp.routing.workcenter']
        if 'routing_wc_line' in data:
            routing_wc_line_id = data.get('routing_wc_line')
            work = workcenter_obj.browse(routing_wc_line_id)
            data.update({'do_production':
                         work.operation.do_production})
        return super(MrpProductionWorkcenterLine, self).create(data)
<|endoftext|>


<commit_before>#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright (C) 2016 Red Hat, Inc
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.

from ansible.executor import playbook_executor
from ansible.executor import task_queue_manager
from ansible import inventory
from ansible.parsing import dataloader
from ansible.plugins import callback
from ansible import vars
from dci_agent.ansible.dci_callback_plugin import CallbackModule as DciCallback

import os

class Options(object):
    def __init__(self, verbosity=None, inventory=None, listhosts=None,
                 subset=None, module_path=None, extra_vars=[], forks=5,
                 ask_vault_pass=None, vault_password_files=None,
                 new_vault_password_file=None, output_file=None, tags='all',
                 skip_tags=None, one_line=None, tree=None, ask_sudo_pass=None,
                 ask_su_pass=None, sudo=None, sudo_user=None, become=None,
                 become_method=None, become_user=None, become_ask_pass=None,
                 ask_pass=None, private_key_file=None, remote_user=None,
                 connection=None, timeout=None, ssh_common_args=None,
                 sftp_extra_args=None, scp_extra_args=None,
                 ssh_extra_args=None, poll_interval=None, seconds=None,
                 check=None, syntax=None, diff=None, force_handlers=None,
                 flush_cache=None, listtasks=None, listtags=None):
        self.verbosity = verbosity
        self.inventory = inventory
        self.listhosts = listhosts
        self.subset = subset
        self.module_path = module_path
        self.extra_vars = extra_vars
        self.forks = forks
        self.ask_vault_pass = ask_vault_pass
        self.vault_password_files = vault_password_files
        self.new_vault_password_file = new_vault_password_file
        self.output_file = output_file
        self.tags = tags
        self.skip_tags = skip_tags
        self.one_line = one_line
        self.tree = tree
        self.ask_sudo_pass = ask_sudo_pass
        self.ask_su_pass = ask_su_pass
        self.sudo = sudo
        self.sudo_user = sudo_user
        self.become = become
        self.become_method = become_method
        self.become_user = become_user
        self.become_ask_pass = become_ask_pass
        self.ask_pass = ask_pass
        self.private_key_file = private_key_file
        self.remote_user = remote_user
        self.connection = connection
        self.timeout = timeout
        self.ssh_common_args = ssh_common_args
        self.sftp_extra_args = sftp_extra_args
        self.scp_extra_args = scp_extra_args
        self.ssh_extra_args = ssh_extra_args
        self.poll_interval = poll_interval
        self.seconds = seconds
        self.check = check
        self.syntax = syntax
        self.diff = diff
        self.force_handlers = force_handlers
        self.flush_cache = flush_cache
        self.listtasks = listtasks
        self.listtags = listtags

class Runner(object):

    def __init__(self, playbook, dci_context, options=None, verbosity=3):

        if options is None:
            self._options = Options()
            self._options.verbosity = verbosity
            self._options.connection = 'ssh'
            self._options.become_method = 'sudo'
            self._options.module_path = '%s/library/' % os.path.dirname(
                __file__
            )

        self._loader = dataloader.DataLoader()
        self._variable_manager = vars.VariableManager()

        task_queue_manager.display.verbosity = verbosity
        callback.global_display.verbosity = verbosity

        self._inventory = inventory.Inventory(
            loader=self._loader,
            variable_manager=self._variable_manager,
            host_list='/etc/ansible/hosts'
        )
        self._variable_manager.set_inventory(self._inventory)

        # Instantiate our Callback plugin
        self._results_callback = DciCallback(dci_context=dci_context)

        self._pbex = playbook_executor.PlaybookExecutor(
            playbooks=[playbook],
            inventory=self._inventory,
            variable_manager=self._variable_manager,
            loader=self._loader,
            options=self._options,
            passwords={})

    def run(self, job_id, **kwargs):
        """Run the playbook and returns the playbook's stats."""

        extra_vars = {'job_id': job_id}
        extra_vars.update(kwargs)

        self._variable_manager.extra_vars = extra_vars
        # set the custom callback plugin
        self._pbex._tqm._stdout_callback = self._results_callback
        self._pbex.run()

        return self._pbex._tqm._stats
<commit_msg>Add ansible default options values.<commit_after>#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright (C) 2016 Red Hat, Inc
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.

from ansible.executor import playbook_executor
from ansible.executor import task_queue_manager
from ansible import inventory
from ansible.parsing import dataloader
from ansible.plugins import callback
from ansible import vars
from dci_agent.ansible.dci_callback_plugin import CallbackModule as DciCallback

import os

class Options(object):
    def __init__(self, verbosity=None, inventory='/etc/ansible/hosts',
                 subset=None, module_path=None, extra_vars=[], forks=5,
                 ask_vault_pass=False, vault_password_files=None,
                 new_vault_password_file=None, output_file=None, tags='all',
                 skip_tags=None, one_line=None, tree=None, ask_sudo_pass=None,
                 ask_su_pass=None, sudo=None, sudo_user=None, become=True,
                 become_method='sudo', become_user='root',
                 become_ask_pass=False, ask_pass=False, private_key_file=None,
                 remote_user=None, connection='smart', timeout=10,
                 ssh_common_args='', sftp_extra_args='', scp_extra_args='',
                 ssh_extra_args='', poll_interval=15, seconds=0, check=False,
                 syntax=False, diff=False, force_handlers=False,
                 flush_cache=False, listhosts=False, listtasks=False,
                 listtags=False):
        self.verbosity = verbosity
        self.inventory = inventory
        self.listhosts = listhosts
        self.subset = subset
        self.module_path = module_path
        self.extra_vars = extra_vars
        self.forks = forks
        self.ask_vault_pass = ask_vault_pass
        self.vault_password_files = vault_password_files
        self.new_vault_password_file = new_vault_password_file
        self.output_file = output_file
        self.tags = tags
        self.skip_tags = skip_tags
        self.one_line = one_line
        self.tree = tree
        self.ask_sudo_pass = ask_sudo_pass
        self.ask_su_pass = ask_su_pass
        self.sudo = sudo
        self.sudo_user = sudo_user
        self.become = become
        self.become_method = become_method
        self.become_user = become_user
        self.become_ask_pass = become_ask_pass
        self.ask_pass = ask_pass
        self.private_key_file = private_key_file
        self.remote_user = remote_user
        self.connection = connection
        self.timeout = timeout
        self.ssh_common_args = ssh_common_args
        self.sftp_extra_args = sftp_extra_args
        self.scp_extra_args = scp_extra_args
        self.ssh_extra_args = ssh_extra_args
        self.poll_interval = poll_interval
        self.seconds = seconds
        self.check = check
        self.syntax = syntax
        self.diff = diff
        self.force_handlers = force_handlers
        self.flush_cache = flush_cache
        self.listtasks = listtasks
        self.listtags = listtags

class Runner(object):

    def __init__(self, playbook, dci_context, options=None, verbosity=3):

        if options is None:
            self._options = Options()
            self._options.verbosity = verbosity
            self._options.module_path = '%s/library/' % os.path.dirname(
                __file__
            )

        self._loader = dataloader.DataLoader()
        self._variable_manager = vars.VariableManager()

        task_queue_manager.display.verbosity = verbosity
        callback.global_display.verbosity = verbosity

        self._inventory = inventory.Inventory(
            loader=self._loader,
            variable_manager=self._variable_manager,
            host_list='/etc/ansible/hosts'
        )
        self._variable_manager.set_inventory(self._inventory)

        # Instantiate our Callback plugin
        self._results_callback = DciCallback(dci_context=dci_context)

        self._pbex = playbook_executor.PlaybookExecutor(
            playbooks=[playbook],
            inventory=self._inventory,
            variable_manager=self._variable_manager,
            loader=self._loader,
            options=self._options,
            passwords={})

    def run(self, job_id, **kwargs):
        """Run the playbook and returns the playbook's stats."""

        extra_vars = {'job_id': job_id}
        extra_vars.update(kwargs)

        self._variable_manager.extra_vars = extra_vars
        # set the custom callback plugin
        self._pbex._tqm._stdout_callback = self._results_callback
        self._pbex.run()

        return self._pbex._tqm._stats
<|endoftext|>