diff --git a/CHANGELOG b/CHANGELOG index 0a20ca18a947fc489de34eb7f7f8d751760c8efa..b39db00973e70dee8db0c1f9c4ac0897f9e2797f 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -12,4 +12,8 @@ Version 1.0.2a Version 1.0.3 -Fixed #14: Added support for PMT idle mode - -Reduced CPU usage when running OCP by reducing polling frequency \ No newline at end of file + -Reduced CPU usage when running OCP by reducing polling frequency + +Version 1.0.4 + -Adds support for synchronous electromechanical shutter detection (added in dstat/dstat-firmware@29d4c86) + -New version string system \ No newline at end of file diff --git a/dstat_interface/dstat_comm.py b/dstat_interface/dstat_comm.py index 56e0baa7125b6dc490798c942a462ddf4de82f73..f18fbe0531cf94a5b6618f74da1654e08bb4796c 100644 --- a/dstat_interface/dstat_comm.py +++ b/dstat_interface/dstat_comm.py @@ -344,6 +344,7 @@ class Experiment(object): self.parameters = parameters self.databytes = 8 self.scan = 0 + self.time = 0 self.data_extra = [] # must be defined even when not needed @@ -608,24 +609,35 @@ class PDExp(Chronoamp): self.xmin = 0 self.xmax = self.parameters['time'] - self.commands += "E" - self.commands[2] += "R" - self.commands[2] += "1" - self.commands[2] += " " + if self.parameters['shutter']: + if self.parameters['sync']: + self.commands.append("EZ") + self.commands[-1] += str(self.parameters['sync_freq']) + self.commands[-1] += " " + else: + self.commands.append("E2") + + self.commands.append("ER1 ") if self.parameters['voltage'] == 0: # Special case where V=0 - self.commands[2] += "65535" + self.commands[-1] += "65535" else: - self.commands[2] += str(int( + self.commands[-1] += str(int( 65535-(self.parameters['voltage']*(65536./3000)))) - self.commands[2] += " " - self.commands[2] += str(self.parameters['time']) - self.commands[2] += " " + self.commands[-1] += " " + self.commands[-1] += str(self.parameters['time']) + self.commands[-1] += " " if self.parameters['interlock']: - self.commands[2] += "1" + self.commands[-1] += "1" else: - self.commands[2] += "0" - self.commands[2] += " " + self.commands[-1] += "0" + self.commands[-1] += " " + + if self.parameters['shutter']: + if self.parameters['sync']: + self.commands.append("Ez") + else: + self.commands.append("E1") class PotExp(Experiment): """Potentiometry experiment""" diff --git a/dstat_interface/interface/dstatinterface.glade b/dstat_interface/interface/dstatinterface.glade index 8f2f2726ef8b78133edb4bbbd3629fc2f649add5..62df9c0df9852d8742b1f5dbb3432243d016c357 100644 --- a/dstat_interface/interface/dstatinterface.glade +++ b/dstat_interface/interface/dstatinterface.glade @@ -777,7 +777,7 @@ Thanks to Christian Fobel for help with Dropbot Plugin - Save Plot… + Save Plots… True False image3 @@ -1008,6 +1008,7 @@ Thanks to Christian Fobel for help with Dropbot Plugin True True + True True @@ -1074,11 +1075,81 @@ Thanks to Christian Fobel for help with Dropbot Plugin - + True - False + True + bottom - + + True + False + + + + + + + + True + False + 0.40999999642372131 + Main + + + False + + + + + True + False + + + + + + + + + 1 + + + + + True + False + FT + + + 1 + False + + + + + True + False + + + + + + + + + 2 + + + + + True + False + Periodogram + + + 2 + False + diff --git a/dstat_interface/interface/exp_int.py b/dstat_interface/interface/exp_int.py index 86e4e94a0b74b10408d30a414a7e6b3892930c96..b8a3c70234e8a22ae0724a96e43bea1109716e2c 100644 --- a/dstat_interface/interface/exp_int.py +++ b/dstat_interface/interface/exp_int.py @@ -209,8 +209,15 @@ class PD(ExpInterface): self.entry['voltage'] = self.builder.get_object('voltage_adjustment') self.entry['time'] = self.builder.get_object('time_entry') self.entry['interlock'] = self.builder.get_object('interlock_button') + self.entry['shutter'] = self.builder.get_object('shutter_button') + self.entry['sync'] = self.builder.get_object('sync_button') + self.entry['sync_freq'] = self.builder.get_object('sync_freq') - self.buttons = map(self.builder.get_object, ['light_button', 'threshold_button']) + self.buttons = map(self.builder.get_object, + ['light_button', 'threshold_button']) + + self.shutter_buttons = map(self.builder.get_object, + ['sync_button', 'sync_freq']) def on_light_button_clicked(self, data=None): __main__.MAIN.on_pot_stop_clicked() @@ -236,6 +243,7 @@ class PD(ExpInterface): def on_threshold_button_clicked(self, data=None): __main__.MAIN.on_pot_stop_clicked() __main__.MAIN.stop_ocp() + for i in self.buttons: i.set_sensitive(False) @@ -250,6 +258,14 @@ class PD(ExpInterface): finally: gobject.timeout_add(700, restore_buttons, self.buttons) + + def on_shutter_button_toggled(self, widget): + if self.entry['shutter'].get_active(): + for i in self.shutter_buttons: + i.set_sensitive(True) + else: + for i in self.shutter_buttons: + i.set_sensitive(False) def get_params(self): """Returns a dict of parameters for experiment.""" @@ -257,6 +273,9 @@ class PD(ExpInterface): parameters['voltage'] = int(self.entry['voltage'].get_value()) parameters['time'] = int(self.entry['time'].get_text()) parameters['interlock'] = self.entry['interlock'].get_active() + parameters['shutter'] = self.entry['shutter'].get_active() + parameters['sync'] = self.entry['sync'].get_active() + parameters['sync_freq'] = float(self.entry['sync_freq'].get_text()) return parameters diff --git a/dstat_interface/interface/pd.glade b/dstat_interface/interface/pd.glade index 05c104ee40a272331da1d1d23df88aa81414b4d1..aaa16ead58a81e2470bc40c3e2734c24a76ebe02 100644 --- a/dstat_interface/interface/pd.glade +++ b/dstat_interface/interface/pd.glade @@ -28,7 +28,7 @@ True False - 6 + 9 2 True @@ -123,6 +123,7 @@ 2 3 4 + GTK_EXPAND @@ -208,6 +209,85 @@ 6 + + + True + False + + + 2 + 6 + 7 + + + + + True + False + Electromechanical Shutter + + + 7 + 8 + + + + + Synchronous Detection (Hz) + True + True + False + 0.54000002145767212 + True + + + 8 + 9 + + + + + + True + True + + 8 + 0 + 1 + True + False + False + True + True + + + 1 + 2 + 8 + 9 + + GTK_FILL + 3 + + + + + Enable Shutter + True + True + False + True + True + + + + 1 + 2 + 7 + 8 + GTK_EXPAND + + False diff --git a/dstat_interface/interface/save.py b/dstat_interface/interface/save.py index 760d07e62dd555563c83c4de12ff4a2f46922ff5..a8e5a007aa253ba95fb5d119991db9199a99a8dd 100644 --- a/dstat_interface/interface/save.py +++ b/dstat_interface/interface/save.py @@ -20,10 +20,11 @@ import gtk, io, os import numpy as np -from datetime import datetime + +from errors import InputError, VarError, ErrorLogger +_logger = ErrorLogger(sender="dstat-interface-save") def manSave(current_exp): - exp = current_exp fcd = gtk.FileChooserDialog("Save...", None, gtk.FILE_CHOOSER_ACTION_SAVE, (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_SAVE, gtk.RESPONSE_OK)) @@ -43,19 +44,27 @@ def manSave(current_exp): if response == gtk.RESPONSE_OK: path = fcd.get_filename() - print "Selected filepath: %s" % path + logger.error(" ".join(("Selected filepath:", path)),'INFO') filter_selection = fcd.get_filter().get_name() if filter_selection.endswith("(.npy)"): - npy(exp, path) + if (current_exp.parameters['shutter'] and current_exp.parameters['sync']): + npy(current_exp, current_exp.data, "-".join((path,'data'))) + npy(current_exp, current_exp.ftdata, "-".join((path,'ft'))) + else: + npy(current_exp, current_exp.data, path, auto=True) elif filter_selection.endswith("(.txt)"): - text(exp, path) + if (current_exp.parameters['shutter'] and current_exp.parameters['sync']): + text(current_exp, current_exp.data, "-".join((path,'data'))) + text(current_exp, current_exp.ftdata, "-".join((path,'ft'))) + else: + text(current_exp, current_exp.data, path, auto=True) fcd.destroy() elif response == gtk.RESPONSE_CANCEL: fcd.destroy() -def plotSave(plot): +def plotSave(plots): fcd = gtk.FileChooserDialog("Save Plot…", None, gtk.FILE_CHOOSER_ACTION_SAVE, (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, @@ -76,18 +85,22 @@ def plotSave(plot): if response == gtk.RESPONSE_OK: path = fcd.get_filename() - print "Selected filepath: %s" % path + logger.error(" ".join(("Selected filepath:", path)),'INFO') filter_selection = fcd.get_filter().get_name() - if filter_selection.endswith("(.pdf)"): - if not path.endswith(".pdf"): - path += ".pdf" + for i in plots: + path += '-' + path += i - elif filter_selection.endswith("(.png)"): - if not path.endswith(".png"): - path += ".png" - - plot.figure.savefig(path) # determines format from file extension + if filter_selection.endswith("(.pdf)"): + if not path.endswith(".pdf"): + path += ".pdf" + + elif filter_selection.endswith("(.png)"): + if not path.endswith(".png"): + path += ".png" + + plots[i].figure.savefig(path) # determines format from file extension fcd.destroy() elif response == gtk.RESPONSE_CANCEL: @@ -99,39 +112,45 @@ def autoSave(current_exp, dir_button, name, expnumber): path = dir_button.get_filename() path += '/' path += name + path += '-' path += str(expnumber) - - text(current_exp, path, auto=True) - -def autoPlot(plot, dir_button, name, expnumber): - if name == "": - name = "file" - path = dir_button.get_filename() - path += '/' - path += name - path += str(expnumber) + if (current_exp.parameters['shutter'] and current_exp.parameters['sync']): + text(current_exp, current_exp.data, "-".join((path,'data')), auto=True) + text(current_exp, current_exp.ftdata, "-".join((path,'ft')), auto=True) + else: + text(current_exp, current_exp.data, path, auto=True) + +def autoPlot(plots, dir_button, name, expnumber): + for i in plots: + if name == "": + name = "file" + + path = dir_button.get_filename() + path += '/' + path += name + path += '-' + path += str(expnumber) + path += '-' + path += i + + if path.endswith(".pdf"): + path = path.rstrip(".pdf") - if path.endswith(".pdf"): - path = path.rstrip(".pdf") - - j = 1 - while os.path.exists("".join([path, ".pdf"])): - if j > 1: - path = path[:-len(str(j))] - path += str(j) - j += 1 - - path += ".pdf" - plot.figure.savefig(path) - + j = 1 + while os.path.exists("".join([path, ".pdf"])): + if j > 1: + path = path[:-len(str(j))] + path += str(j) + j += 1 + + path += ".pdf" + plots[i].figure.savefig(path) -def npy(exp, path, auto=False): +def npy(exp, data, path, auto=False): if path.endswith(".npy"): path = path.rstrip(".npy") - data = np.array(exp.data) - if auto == True: j = 1 while os.path.exists("".join([path, ".npy"])): @@ -142,7 +161,7 @@ def npy(exp, path, auto=False): np.save(path, data) -def text(exp, path, auto=False): +def text(exp, data, path, auto=False): if path.endswith(".txt"): path = path.rstrip(".txt") @@ -158,19 +177,16 @@ def text(exp, path, auto=False): path += ".txt" file = open(path, 'w') - time = datetime.now() + time = exp.time - data = np.array(exp.data) header = "".join(['#', time.isoformat(), "\n#"]) for i in exp.commands: header += i file.write("".join([header, '\n'])) - for col in zip(*exp.data): + for col in zip(*data): for row in col: file.write(str(row)+ " ") file.write('\n') file.close() - - diff --git a/dstat_interface/main.py b/dstat_interface/main.py index 8822a4db8259731e15aaeaf8308a4cdc7046f16e..c2a53c73d1f88482c1d0860dd2be508907eb7e6c 100755 --- a/dstat_interface/main.py +++ b/dstat_interface/main.py @@ -20,9 +20,11 @@ """ GUI Interface for Wheeler Lab DStat """ -import sys,os -from errors import InputError, VarError, ErrorLogger -_logger = ErrorLogger(sender="dstat-interface-main") +import sys +import os +import multiprocessing +import time +from datetime import datetime try: import pygtk @@ -40,19 +42,21 @@ try: except ImportError: _logger.error('gobject not available', 'ERR') sys.exit(1) +from serial import SerialException os.chdir(os.path.dirname(os.path.abspath(sys.argv[0]))) +from version import getVersion import interface.save as save import dstat_comm as comm import interface.exp_window as exp_window import interface.adc_pot as adc_pot import plot import microdrop +from errors import InputError, VarError, ErrorLogger +_logger = ErrorLogger(sender="dstat-interface-main") -from serial import SerialException -import multiprocessing -import time +from datetime import datetime class Main(object): """Main program """ @@ -62,7 +66,7 @@ class Main(object): self.builder.connect_signals(self) self.cell = gtk.CellRendererText() - #create instance of interface components + # Create instance of interface components self.statusbar = self.builder.get_object('statusbar') self.ocp_disp = self.builder.get_object('ocp_disp') self.window = self.builder.get_object('window1') @@ -77,15 +81,21 @@ class Main(object): self.message_context_id = self.statusbar.get_context_id("message") self.plotwindow = self.builder.get_object('plotbox') + self.ft_window = self.builder.get_object('ft_box') + self.period_window = self.builder.get_object('period_box') self.exp_window = exp_window.Experiments(self.builder) - #setup autosave + # Setup Autosave self.autosave_checkbox = self.builder.get_object('autosave_checkbutton') self.autosavedir_button = self.builder.get_object('autosavedir_button') self.autosavename = self.builder.get_object('autosavename') + # Setup Plots + self.plot_notebook = self.builder.get_object('plot_notebook') + self.plot = plot.plotbox(self.plotwindow) + self.ft_plot = plot.ft_box(self.ft_window) #fill adc_pot_box self.adc_pot_box = self.builder.get_object('gain_adc_box') @@ -118,7 +128,12 @@ class Main(object): self.spinner = self.builder.get_object('spinner') self.mainwindow = self.builder.get_object('window1') - self.mainwindow.set_title("DStat Interface 1.0.3") + + # Set Version Strings + ver = getVersion() + self.mainwindow.set_title(" ".join(("DStat Interface", ver))) + self.aboutdialog.set_version(ver) + self.mainwindow.show_all() self.on_expcombobox_changed() @@ -134,6 +149,11 @@ class Main(object): 'menu_dropbot_disconnect') self.dropbot_enabled = False self.dropbot_triggered = False + + self.plot_notebook.get_nth_page( + self.plot_notebook.page_num(self.ft_window)).hide() + self.plot_notebook.get_nth_page( + self.plot_notebook.page_num(self.period_window)).hide() def on_window1_destroy(self, object, data=None): """ Quit when main window closed.""" @@ -363,6 +383,20 @@ class Main(object): """ Starts experiment """ self.plot.clearall() self.plot.changetype(self.current_exp) + + nb = self.plot_notebook + + if (parameters['sync'] and parameters['shutter']): + nb.get_nth_page( + nb.page_num(self.ft_window)).show() + # nb.get_nth_page( + # nb.page_num(self.period_window)).show() + self.ft_plot.clearall() + self.ft_plot.changetype(self.current_exp) + else: + nb.get_nth_page(nb.page_num(self.ft_window)).hide() + # nb.get_nth_page(nb.page_num(self.period_window)).hide() + comm.serial_instance.proc_pipe_p.send(self.current_exp) @@ -384,10 +418,15 @@ class Main(object): while comm.serial_instance.data_pipe_p.poll(): # Clear data pipe comm.serial_instance.data_pipe_p.recv() + selection = self.expcombobox.get_active() parameters = {} parameters['version'] = self.version + # Make sure these are defined + parameters['sync'] = False + parameters['shutter'] = False + if self.adc_pot.buffer_toggle.get_active(): # True if box checked parameters['adc_buffer'] = "2" else: @@ -404,6 +443,16 @@ class Main(object): parameters['adc_rate'] = srate_model.get_value( self.adc_pot.srate_combobox.get_active_iter(), 2) # third column + + srate = srate_model.get_value( + self.adc_pot.srate_combobox.get_active_iter(), 1) + + if srate.endswith("kHz"): + sample_rate = float(srate.rstrip(" kHz"))*1000 + else: + sample_rate = float(srate.rstrip(" Hz")) + + parameters['adc_rate_hz'] = sample_rate parameters['adc_pga'] = pga_model.get_value( self.adc_pot.pga_combobox.get_active_iter(), 2) @@ -638,6 +687,11 @@ class Main(object): if (parameters['time'] > 65535): raise InputError(parameters['clean_s'], "Time must fit in 16-bit counter.") + if (parameters['sync'] and parameters['shutter']): + if (parameters['sync_freq'] > 30 or + parameters['sync_freq'] <= 0): + raise InputError(parameters['sync_freq'], + "Frequency must be between 0 and 30 Hz.") self.current_exp = comm.PDExp(parameters) @@ -713,10 +767,10 @@ class Main(object): try: if comm.serial_instance.data_pipe_p.poll(): incoming = comm.serial_instance.data_pipe_p.recv() - if isinstance(incoming, basestring): # Test if incoming is str - self.experiment_done() - self.on_serial_disconnect_clicked() - return False + # if isinstance(incoming, basestring): # Test if incoming is str + # self.experiment_done() + # self.on_serial_disconnect_clicked() + # return False self.line, data = incoming if self.line > self.lastdataline: @@ -729,6 +783,8 @@ class Main(object): if len(data) > 2: self.current_exp.data_extra[2*self.line+i].append( data[i+2]) + if comm.serial_instance.data_pipe_p.poll(): + self.experiment_running_data() return True return True @@ -796,9 +852,15 @@ class Main(object): """Clean up after data acquisition is complete. Update plot and copy data to raw data tab. Saves data if autosave enabled. """ + self.current_exp.time = datetime.now() gobject.source_remove(self.experiment_proc[0]) gobject.source_remove(self.plot_proc) # stop automatic plot update self.experiment_running_plot() # make sure all data updated on plot + + if (self.current_exp.parameters['shutter'] and + self.current_exp.parameters['sync']): + self.ft_plot.updateline(self.current_exp, 0) + self.ft_plot.redraw() self.databuffer.set_text("") self.databuffer.place_cursor(self.databuffer.get_start_iter()) @@ -825,7 +887,13 @@ class Main(object): if self.autosave_checkbox.get_active(): save.autoSave(self.current_exp, self.autosavedir_button, self.autosavename.get_text(), self.expnumber) - save.autoPlot(self.plot, self.autosavedir_button, + plots = {'data':self.plot} + + if (self.current_exp.parameters['shutter'] and + self.current_exp.parameters['sync']): + plots['ft'] = self.ft_plot + + save.autoPlot(plots, self.autosavedir_button, self.autosavename.get_text(), self.expnumber) self.expnumber += 1 @@ -858,7 +926,13 @@ class Main(object): def on_file_save_plot_activate(self, menuitem, data=None): """Activate dialogue to save current plot.""" - save.plotSave(self.plot) + plots = {'data':self.plot} + + if (self.current_exp.parameters['shutter'] and + self.current_exp.parameters['sync']): + plots['ft'] = self.ft_plot + + save.plotSave(plots) def on_menu_dropbot_connect_activate(self, menuitem, data=None): """Listen for remote control connection from µDrop.""" diff --git a/dstat_interface/plot.py b/dstat_interface/plot.py index 4002efc36fb30dbd480711a1f62b0a2886b83085..b2c143c6710f20088f011aa2ed801aa62bd32832 100644 --- a/dstat_interface/plot.py +++ b/dstat_interface/plot.py @@ -31,6 +31,27 @@ from matplotlib.backends.backend_gtkagg \ import FigureCanvasGTKAgg as FigureCanvas from matplotlib.backends.backend_gtkagg \ import NavigationToolbar2GTKAgg as NavigationToolbar + +from numpy import sin, linspace, pi, mean +from scipy import fft, arange +from scipy.signal import blackman + +def plotSpectrum(y,Fs): + """ + Plots a Single-Sided Amplitude Spectrum of y(t) + """ + y = y-mean(y) + n = len(y) # length of the signal + k = arange(n) + T = n/Fs + frq = k/T # two sides frequency range + frq = frq[range(n/2)] # one side frequency range + W = blackman(n) + Y = fft(y*W)/n # fft computing and normalization + Y = abs(Y[range(n/2)]) + + + return (frq, Y) class plotbox(object): """Contains main data plot and associated methods.""" @@ -104,4 +125,22 @@ class plotbox(object): self.figure.canvas.draw() return True + +class ft_box(plotbox): + def updateline(self, Experiment, line_number): + x, y = plotSpectrum(Experiment.data[1+line_number*2], Experiment.parameters['adc_rate_hz']) + self.lines[line_number].set_ydata(y) + self.lines[line_number].set_xdata(x) + Experiment.ftdata = (x, y) + + def changetype(self, Experiment): + """Change plot type. Set axis labels and x bounds to those stored + in the Experiment instance. + """ + self.axe1.set_xlabel("Freq (Hz)") + self.axe1.set_ylabel("|Y| (A/Hz)") + self.axe1.set_xlim(0, Experiment.parameters['adc_rate_hz']/2) + + self.figure.canvas.draw() + diff --git a/dstat_interface/version.py b/dstat_interface/version.py new file mode 100644 index 0000000000000000000000000000000000000000..343f158f74594923d656e5be8ad57b09b3c56a1c --- /dev/null +++ b/dstat_interface/version.py @@ -0,0 +1,135 @@ +# -*- coding: utf-8 -*- + +"""Calculates the current version number. + +If possible, uses output of “git describe” modified to conform to the +visioning scheme that setuptools uses (see PEP 386). Releases must be +labelled with annotated tags (signed tags are annotated) of the following +format: + + v(.)+ [ {a|b|c|rc} (.)* ] + +If “git describe” returns an error (likely because we're in an unpacked copy +of a release tarball, rather than a git working copy), or returns a tag that +does not match the above format, version is read from RELEASE-VERSION file. + +To use this script, simply import it your setup.py file, and use the results +of getVersion() as your package version: + + import version + setup( + version=version.getVersion(), + . + . + . + ) + +This will automatically update the RELEASE-VERSION file. The RELEASE-VERSION +file should *not* be checked into git but it *should* be included in sdist +tarballs (as should version.py file). To do this, run: + + echo include RELEASE-VERSION version.py >>MANIFEST.in + echo RELEASE-VERSION >>.gitignore + +With that setup, a new release can be labelled by simply invoking: + + git tag -s v1.0 +""" + +__author__ = ('Douglas Creager ', + 'Michal Nazarewicz ') +__license__ = 'This file is placed into the public domain.' +__maintainer__ = 'Michal Nazarewicz' +__email__ = 'mina86@mina86.com' + +__all__ = ('getVersion') + + +import re +import subprocess +import sys + + +RELEASE_VERSION_FILE = 'RELEASE-VERSION' + +# http://www.python.org/dev/peps/pep-0386/ +_PEP386_SHORT_VERSION_RE = r'\d+(?:\.\d+)+(?:(?:[abc]|rc)\d+(?:\.\d+)*)?' +_PEP386_VERSION_RE = r'^%s(?:\.post\d+)?(?:\.dev\d+)?$' % ( + _PEP386_SHORT_VERSION_RE) +_GIT_DESCRIPTION_RE = r'^v(?P%s)-(?P\d+)-g(?P[\da-f]+)$' % ( + _PEP386_SHORT_VERSION_RE) + + +def readGitVersion(): + try: + proc = subprocess.Popen(('git', 'describe', '--long', + '--match', 'v[0-9]*.*'), + stdout=subprocess.PIPE, stderr=subprocess.PIPE) + data, _ = proc.communicate() + if proc.returncode: + return None + ver = data.splitlines()[0].strip() + proc = subprocess.Popen(('git', 'rev-parse', '--abbrev-ref', 'HEAD'), + stdout=subprocess.PIPE, stderr=subprocess.PIPE) + branch, _ = proc.communicate() + if proc.returncode: + return None + except: + return None + + if not ver: + return None + m = re.search(_GIT_DESCRIPTION_RE, ver) + if not m: + sys.stderr.write('version: git description (%s) is invalid, ' + 'ignoring\n' % ver) + return None + + commits = int(m.group('commits')) + + if not commits: + version = m.group('ver') + else: + version = '%s.post%d' % ( + m.group('ver'), commits) + + if branch.strip() != 'master': + version += '.dev%d' % int(m.group('sha'), 16) + + return version + + +def readReleaseVersion(): + try: + fd = open(RELEASE_VERSION_FILE) + try: + ver = fd.readline().strip() + finally: + fd.close() + if not re.search(_PEP386_VERSION_RE, ver): + sys.stderr.write('version: release version (%s) is invalid, ' + 'will use it anyway\n' % ver) + return ver + except: + return None + + +def writeReleaseVersion(version): + fd = open(RELEASE_VERSION_FILE, 'w') + fd.write('%s\n' % version) + fd.close() + + +def getVersion(): + release_version = readReleaseVersion() + version = readGitVersion() or release_version + if not version: + raise ValueError('Cannot find the version number') + if version != release_version: + writeReleaseVersion(version) + return version + + +if __name__ == '__main__': + print getVersion() + diff --git a/git-pre-commit.py b/git-pre-commit.py new file mode 100755 index 0000000000000000000000000000000000000000..3e4ea397cc7d19489fdd8a6effb47990248b3f70 --- /dev/null +++ b/git-pre-commit.py @@ -0,0 +1,12 @@ +#!/usr/bin/env python + +import os +import sys +from shutil import copyfile + +import version + +version.getVersion() +src = os.path.abspath('./RELEASE-VERSION') +dst = os.path.abspath('./dstat_interface/CURRENT-VERSION') +copyfile(src, dst)