<commit_before># coding: utf-8
import datetime
import logging
import operator
from enum import IntEnum
from io import StringIO

try:
    from urllib import urlopen, urlencode
except ImportError:
    from urllib.request import urlopen
    from urllib.parse import urlencode

import pandas as pd
from pandas.parser import CParserError

from finam.utils import is_container, smart_decode

__all__ = ['Market',
           'Timeframe',
           'FinamDownloadError',
           'FinamParsingError',
           'FinamTooLongTimeframeError',
           'FinamObjectNotFoundError',
           'Exporter']

logger = logging.getLogger()

class Market(IntEnum):

    """
    Markets mapped to ids used by finam.ru export

    List is imcomplete extend it when needed
    """

    BONDS = 2
    COMMODITIES = 24
    CURRENCIES = 45
    FUTURES = 14  # non-expired futures
    FUTURES_ARCHIVE = 17  # expired futures
    INDEXES = 6
    SHARES = 1
    USA = 25
    FUTURES_USA = 7
    ETF = 28
    ETF_MOEX = 515

class Timeframe(IntEnum):

    TICKS = 1
    MINUTES1 = 2
    MINUTES5 = 3
    MINUTES10 = 4
    MINUTES15 = 5
    MINUTES30 = 6
    HOURLY = 7
    DAILY = 8
    WEEKLY = 9
    MONTHLY = 10

class LookupComparator(IntEnum):

    EQUALS = 1
    STARTSWITH = 2
    CONTAINS = 3

class FinamExportError(Exception):
    pass

class FinamDownloadError(FinamExportError):
    pass

class FinamParsingError(FinamExportError):
    pass

class FinamObjectNotFoundError(FinamExportError):
    pass

class FinamTooLongTimeframeError(FinamExportError):
    pass

class ExporterMeta(object):

    FINAM_DICT_URL = 'https://www.finam.ru/cache/icharts/icharts.js'
    FINAM_CATEGORIES = -1

    def __init__(self, lazy=True):
        self._meta = None
        if not lazy:
            self.__maybe_load()

    def __parse_js_assignment(self, line):
        """
        Parses 1-line js assignment used by finam.ru

        Can be either (watch spaces between commas)
        var string_arr = ['string1','string2',...,'stringN'] or
        var ints_arr = [int1,...,intN]

        May also contain empty strings ['abc','','']
        """
        logger.debug('Parsing line starting with "{}"'.format(line[:20]))

        # extracting everything between array brackets
        start_char, end_char = '[', ']'
        start_idx = line.find(start_char)
        end_idx = line.find(end_char)
        if (start_idx == -1 or
                end_idx == -1):
            raise FinamParsingError('Unable to parse line: {}'.format(line))
        items = line[start_idx + 1:end_idx]

        # string items
        if items.startswith("'"):
            # it may contain ',' inside lines so cant split by ','
            # i.e. "GILEAD SCIENCES, INC."
            items = items.split("','")
            for i in (0, -1):
                items[i] = items[i].strip("'")
            return items

        # int items
        return items.split(',')

    def __parse(self, data):
        """
        Parses js file used by finam.ru export tool
        """
        cols = ('id', 'name', 'code', 'market')
        parsed = dict()
        for idx, col in enumerate(cols[:len(cols)]):
            parsed[col] = self.__parse_js_assignment(data[idx])
        df = pd.DataFrame(columns=cols, data=parsed)
        df['market'] = df['market'].astype(int)
        # junk data + non-int ids, we don't need it
        df = df[df.market != self.FINAM_CATEGORIES]
        # now we can coerce ids to ints
        df['id'] = df['id'].astype(int)
        df.set_index('id', inplace=True)
        df.sort('market', inplace=True)
        return df

    def _fetch(self):
        """
        Just fetches finam's metadata
        """
        logger.info('Fetching metadata from {}'.format(self.FINAM_DICT_URL))
        try:
            return urlopen(self.FINAM_DICT_URL).readlines()
        except IOError as e:
            raise FinamDownloadError('Unable to load contracts dictionary: {}'
                                     .format(e))

    def __decode_data(self, data):
        """
        Converts finam's charset to utf8
        """
        logger.info('Decoding response')
        try:
            return smart_decode(data)
        except UnicodeDecodeError as e:
            raise FinamExportError('Unable to decode dictionary content: {}'
                                   .format(e.message))

    def __maybe_load(self):
        """
        Downloads and parses finam's metadata if it's not done yet
        """
        if self._meta is not None:
            return

        data_cp1251 = self._fetch()
        data = self.__decode_data(data_cp1251)
        self._meta = self.__parse(data)

    @property
    def meta(self):
        # so that dataframe can't be modified externally
        return self._meta.copy(deep=True)

    def __apply_filter(self, col, val, comparator):
        """
        Builds a dataframe matching original dataframe with conditions passed

        The original dataframe is left intact
        """
        if not is_container(val):
            val = [val]

        if comparator == LookupComparator.EQUALS:
            # index must be sliced differently
            if col == 'id':
                expr = self._meta.index.isin(val)
            else:
                expr = self._meta[col].isin(val)
        else:
            if comparator == LookupComparator.STARTSWITH:
                op = 'startswith'
            else:
                op = 'contains'
            expr = self.__combine_filters(
                map(getattr(self._meta[col].str, op), val), operator.or_)
        return expr

    def __combine_filters(self, filters, op):
        itr = iter(filters)
        result = next(itr)
        for filter_ in itr:
            result = op(result, filter_)
        return result

    def lookup(self, id_=None, code=None, name=None, market=None,
               name_comparator=LookupComparator.CONTAINS,
               code_comparator=LookupComparator.EQUALS):
        """
        Looks up contracts matching specified combinations of requirements
        If multiple requirements are specified they will be ANDed

        Note that the same id can have multiple matches as an entry
        may appear in different markets
        """
        if not any((id_, code, name, market)):
            raise ValueError('Either id or code or name or market'
                             ' must be specified')

        self.__maybe_load()
        filters = []

        # applying filters
        filter_groups = (('id', id_, LookupComparator.EQUALS),
                         ('code', code, code_comparator),
                         ('name', name, name_comparator),
                         ('market', market, LookupComparator.EQUALS))

        for col, val, comparator in filter_groups:
            if val is not None:
                filters.append(self.__apply_filter(col, val, comparator))

        combined_filter = self.__combine_filters(filters, operator.and_)
        res = self._meta[combined_filter]
        if len(res) == 0:
            raise FinamObjectNotFoundError
        return res

class Exporter(object):

    DEFAULT_EXPORT_HOST = 'export.finam.ru'
    IMMUTABLE_PARAMS = {
        'd': 'd',
        'f': 'table',
        'e': '.csv',
        'dtf': '1',
        'tmf': '3',
        'MSOR': '0',
        'mstime': 'on',
        'mstimever': '1',
        'sep': '3',
        'sep2': '1',
        'at': '1'
    }

    ERROR_TOO_MUCH_WANTED = (u'Вы запросили данные за слишком '
                             u'большой временной период')

    def __init__(self, export_host=None):
        self._meta = ExporterMeta(lazy=True)
        if export_host is not None:
            self._export_host = export_host
        else:
            self._export_host = self.DEFAULT_EXPORT_HOST

    def __build_url(self, params):
        url = ('http://{}/table.csv?{}&{}'
               .format(self._export_host,
                       urlencode(self.IMMUTABLE_PARAMS),
                       urlencode(params)))
        return url

    def __do_sanity_checks(self, data):
        if self.ERROR_TOO_MUCH_WANTED in data:
            raise FinamTooLongTimeframeError

        if not all(c in data for c in '<>;'):
            raise FinamParsingError('Returned data doesnt seem like '
                                    'a valid csv dataset: {}'
                                    .format(data))

    def __decode_data(self, data):
        """
        Converts finam's charset to utf8
        """
        logger.info('Decoding response')
        try:
            return smart_decode(data)
        except UnicodeDecodeError as e:
            raise FinamExportError('Unable to decode content: {}'
                                   .format(e.message))

    def _fetch(self, url):
        logger.info('Loading data from {}'.format(url))
        try:
            return urlopen(url).read()
        except IOError as e:
            raise FinamDownloadError('Unable to download {}: {}'
                                     .format(url, e.message))

    def lookup(self, *args, **kwargs):
        return self._meta.lookup(*args, **kwargs)

    def download(self,
                 id_,
                 market,
                 start_date=datetime.date(2007, 1, 1),
                 end_date=None,
                 timeframe=Timeframe.DAILY):

        items = self._meta.lookup(id_=id_, market=market)
        # i.e. for markets 91, 519, 2
        # id duplicates are feasible, looks like corrupt data on finam
        # can do nothing about it but inform the user
        if len(items) != 1:
            raise FinamDownloadError('Duplicate items for id={} on market {}'
                                     .format(id_, market))
        code = items.iloc[0]['code']

        if end_date is None:
            end_date = datetime.date.today()

        params = {
            'p': timeframe.value,
            'em': id_,
            'market': market.value,
            'df': start_date.day,
            'mf': start_date.month - 1,
            'yf': start_date.year,
            'dt': end_date.day,
            'mt': end_date.month - 1,
            'yt': end_date.year,
            'cn': code,
            'code': code,
            # I would guess this param denotes 'data format'
            # that differs for ticks only
            'datf': 6 if timeframe == Timeframe.TICKS.value else 5
        }

        url = self.__build_url(params)
        # deliberately not using pd.read_csv's ability to fetch
        # urls to fully control what's happening
        data_cp1251 = self._fetch(url)
        data = self.__decode_data(data_cp1251)
        self.__do_sanity_checks(data)
        if timeframe == Timeframe.TICKS:
            date_cols = [2, 3]
        else:
            date_cols = [0, 1]

        try:
            df = pd.read_csv(StringIO(data),
                             index_col=0,
                             parse_dates={'index': date_cols},
                             sep=';')
            df.sort_index(inplace=True)
        except CParserError as e:
            raise FinamParsingError(e.message)

        return df
<commit_msg>* added RU SPB Exchange & stylistic changes<commit_after># coding: utf-8
import datetime
import logging
import operator
from enum import IntEnum
from io import StringIO

try:
    from urllib import urlopen, urlencode
except ImportError:
    from urllib.request import urlopen
    from urllib.parse import urlencode

import pandas as pd
from pandas.parser import CParserError

from finam.utils import is_container, smart_decode

__all__ = ['Market',
           'Timeframe',
           'FinamDownloadError',
           'FinamParsingError',
           'FinamTooLongTimeframeError',
           'FinamObjectNotFoundError',
           'Exporter']

logger = logging.getLogger(__name__)

class Market(IntEnum):

    """
    Markets mapped to ids used by finam.ru export

    List is incomplete, extend it when needed
    """

    BONDS = 2
    COMMODITIES = 24
    CURRENCIES = 45
    ETF = 28
    ETF_MOEX = 515
    FUTURES = 14  # non-expired futures
    FUTURES_ARCHIVE = 17  # expired futures
    FUTURES_USA = 7
    INDEXES = 6
    SHARES = 1
    SPB = 517
    USA = 25

class Timeframe(IntEnum):

    TICKS = 1
    MINUTES1 = 2
    MINUTES5 = 3
    MINUTES10 = 4
    MINUTES15 = 5
    MINUTES30 = 6
    HOURLY = 7
    DAILY = 8
    WEEKLY = 9
    MONTHLY = 10

class LookupComparator(IntEnum):

    EQUALS = 1
    STARTSWITH = 2
    CONTAINS = 3

class FinamExportError(Exception):
    pass

class FinamDownloadError(FinamExportError):
    pass

class FinamParsingError(FinamExportError):
    pass

class FinamObjectNotFoundError(FinamExportError):
    pass

class FinamTooLongTimeframeError(FinamExportError):
    pass

class ExporterMeta(object):

    FINAM_DICT_URL = 'https://www.finam.ru/cache/icharts/icharts.js'
    FINAM_CATEGORIES = -1

    def __init__(self, lazy=True):
        self._meta = None
        if not lazy:
            self._maybe_load()

    def _parse_js_assignment(self, line):
        """
        Parses 1-line js assignment used by finam.ru

        Can be either (watch spaces between commas)
        var string_arr = ['string1','string2',...,'stringN'] or
        var ints_arr = [int1,...,intN]

        May also contain empty strings ['abc','','']
        """
        logger.debug('Parsing line starting with "{}"'.format(line[:20]))

        # extracting everything between array brackets
        start_char, end_char = '[', ']'
        start_idx = line.find(start_char)
        end_idx = line.find(end_char)
        if (start_idx == -1 or
                end_idx == -1):
            raise FinamParsingError('Unable to parse line: {}'.format(line))
        items = line[start_idx + 1:end_idx]

        # string items
        if items.startswith("'"):
            # it may contain ',' inside lines so cant split by ','
            # i.e. "GILEAD SCIENCES, INC."
            items = items.split("','")
            for i in (0, -1):
                items[i] = items[i].strip("'")
            return items

        # int items
        return items.split(',')

    def _parse(self, data):
        """
        Parses js file used by finam.ru export tool
        """
        cols = ('id', 'name', 'code', 'market')
        parsed = dict()
        for idx, col in enumerate(cols[:len(cols)]):
            parsed[col] = self._parse_js_assignment(data[idx])
        df = pd.DataFrame(columns=cols, data=parsed)
        df['market'] = df['market'].astype(int)
        # junk data + non-int ids, we don't need it
        df = df[df.market != self.FINAM_CATEGORIES]
        # now we can coerce ids to ints
        df['id'] = df['id'].astype(int)
        df.set_index('id', inplace=True)
        df.sort('market', inplace=True)
        return df

    def _fetch(self):
        """
        Just fetches finam's metadata
        """
        logger.info('Fetching metadata from {}'.format(self.FINAM_DICT_URL))
        try:
            return urlopen(self.FINAM_DICT_URL).readlines()
        except IOError as e:
            raise FinamDownloadError('Unable to load contracts dictionary: {}'
                                     .format(e))

    def _decode_data(self, data):
        """
        Converts finam's charset to utf8
        """
        logger.info('Decoding response')
        try:
            return smart_decode(data)
        except UnicodeDecodeError as e:
            raise FinamExportError('Unable to decode dictionary content: {}'
                                   .format(e.message))

    def _maybe_load(self):
        """
        Downloads and parses finam's metadata if it's not done yet
        """
        if self._meta is not None:
            return

        data_cp1251 = self._fetch()
        data = self._decode_data(data_cp1251)
        self._meta = self._parse(data)

    @property
    def meta(self):
        # so that dataframe can't be modified externally
        return self._meta.copy(deep=True)

    def _apply_filter(self, col, val, comparator):
        """
        Builds a dataframe matching original dataframe with conditions passed

        The original dataframe is left intact
        """
        if not is_container(val):
            val = [val]

        if comparator == LookupComparator.EQUALS:
            # index must be sliced differently
            if col == 'id':
                expr = self._meta.index.isin(val)
            else:
                expr = self._meta[col].isin(val)
        else:
            if comparator == LookupComparator.STARTSWITH:
                op = 'startswith'
            else:
                op = 'contains'
            expr = self._combine_filters(
                map(getattr(self._meta[col].str, op), val), operator.or_)
        return expr

    def _combine_filters(self, filters, op):
        itr = iter(filters)
        result = next(itr)
        for filter_ in itr:
            result = op(result, filter_)
        return result

    def lookup(self, id_=None, code=None, name=None, market=None,
               name_comparator=LookupComparator.CONTAINS,
               code_comparator=LookupComparator.EQUALS):
        """
        Looks up contracts matching specified combinations of requirements
        If multiple requirements are specified they will be ANDed

        Note that the same id can have multiple matches as an entry
        may appear in different markets
        """
        if not any((id_, code, name, market)):
            raise ValueError('Either id or code or name or market'
                             ' must be specified')

        self._maybe_load()
        filters = []

        # applying filters
        filter_groups = (('id', id_, LookupComparator.EQUALS),
                         ('code', code, code_comparator),
                         ('name', name, name_comparator),
                         ('market', market, LookupComparator.EQUALS))

        for col, val, comparator in filter_groups:
            if val is not None:
                filters.append(self._apply_filter(col, val, comparator))

        combined_filter = self._combine_filters(filters, operator.and_)
        res = self._meta[combined_filter]
        if len(res) == 0:
            raise FinamObjectNotFoundError
        return res

class Exporter(object):

    DEFAULT_EXPORT_HOST = 'export.finam.ru'
    IMMUTABLE_PARAMS = {
        'd': 'd',
        'f': 'table',
        'e': '.csv',
        'dtf': '1',
        'tmf': '3',
        'MSOR': '0',
        'mstime': 'on',
        'mstimever': '1',
        'sep': '3',
        'sep2': '1',
        'at': '1'
    }

    ERROR_TOO_MUCH_WANTED = (u'Вы запросили данные за слишком '
                             u'большой временной период')

    def __init__(self, export_host=None):
        self._meta = ExporterMeta(lazy=True)
        if export_host is not None:
            self._export_host = export_host
        else:
            self._export_host = self.DEFAULT_EXPORT_HOST

    def _build_url(self, params):
        url = ('http://{}/table.csv?{}&{}'
               .format(self._export_host,
                       urlencode(self.IMMUTABLE_PARAMS),
                       urlencode(params)))
        return url

    def _do_sanity_checks(self, data):
        if self.ERROR_TOO_MUCH_WANTED in data:
            raise FinamTooLongTimeframeError

        if not all(c in data for c in '<>;'):
            raise FinamParsingError('Returned data doesnt seem like '
                                    'a valid csv dataset: {}'
                                    .format(data))

    def _decode_data(self, data):
        """
        Converts finam's charset to utf8
        """
        logger.info('Decoding response')
        try:
            return smart_decode(data)
        except UnicodeDecodeError as e:
            raise FinamExportError('Unable to decode content: {}'
                                   .format(e.message))

    def _fetch(self, url):
        logger.info('Loading data from {}'.format(url))
        try:
            return urlopen(url).read()
        except IOError as e:
            raise FinamDownloadError('Unable to download {}: {}'
                                     .format(url, e.message))

    def lookup(self, *args, **kwargs):
        return self._meta.lookup(*args, **kwargs)

    def download(self,
                 id_,
                 market,
                 start_date=datetime.date(2007, 1, 1),
                 end_date=None,
                 timeframe=Timeframe.DAILY):

        items = self._meta.lookup(id_=id_, market=market)
        # i.e. for markets 91, 519, 2
        # id duplicates are feasible, looks like corrupt data on finam
        # can do nothing about it but inform the user
        if len(items) != 1:
            raise FinamDownloadError('Duplicate items for id={} on market {}'
                                     .format(id_, market))
        code = items.iloc[0]['code']

        if end_date is None:
            end_date = datetime.date.today()

        params = {
            'p': timeframe.value,
            'em': id_,
            'market': market.value,
            'df': start_date.day,
            'mf': start_date.month - 1,
            'yf': start_date.year,
            'dt': end_date.day,
            'mt': end_date.month - 1,
            'yt': end_date.year,
            'cn': code,
            'code': code,
            # I would guess this param denotes 'data format'
            # that differs for ticks only
            'datf': 6 if timeframe == Timeframe.TICKS.value else 5
        }

        url = self._build_url(params)
        # deliberately not using pd.read_csv's ability to fetch
        # urls to fully control what's happening
        data_cp1251 = self._fetch(url)
        data = self._decode_data(data_cp1251)
        self._do_sanity_checks(data)
        if timeframe == Timeframe.TICKS:
            date_cols = [2, 3]
        else:
            date_cols = [0, 1]

        try:
            df = pd.read_csv(StringIO(data),
                             index_col=0,
                             parse_dates={'index': date_cols},
                             sep=';')
            df.sort_index(inplace=True)
        except CParserError as e:
            raise FinamParsingError(e.message)

        return df
<|endoftext|>


<commit_before>
import sys

if sys.platform != "darwin":
	print "GUI: your platform is probably not supported yet"
	def guiMain(): pass

import objc
from AppKit import *
from collections import deque

try: pools
except NameError: pools = deque()

# just in case that we are not the main thread
pools.append(NSAutoreleasePool.alloc().init())

import os, sys
from State import state, modules
from utils import *
import appinfo

mydir = os.path.dirname(__file__) or os.getcwd()
try:
	app
except NameError: # only declare if not yet declared
	app = None

def setupAppleMenu():

	# http://www.cocoabuilder.com/archive/cocoa/192181-initializing-the-menubar-without-interface-builder.html
	# By Robert Nikander

	mainMenu = NSMenu.alloc().initWithTitle_("MainMenu")
	mi = mainMenu.addItemWithTitle_action_keyEquivalent_("Apple", None, "")
	m = NSMenu.alloc().initWithTitle_("Apple")

	# strange hack
	app.setAppleMenu_(m)
	mainMenu.setSubmenu_forItem_(m, mi)
	mi = m.addItemWithTitle_action_keyEquivalent_('Quit', 'terminate:', 'q')

	app.setMainMenu_(mainMenu)

	return m

def setupWindow():
	# some example code: http://lists.apple.com/archives/cocoa-dev/2004/Jan/msg01389.html

	w = NSWindow.alloc()
	w.initWithContentRect_styleMask_backing_defer_(
		((200.0, 200.0), (250.0, 100.0)),
		NSTitledWindowMask |
		NSClosableWindowMask |
		NSResizableWindowMask,
		NSBackingStoreBuffered, False)
	w.setTitle_(appinfo.progname)

	#w.setReleasedWhenClosed_(False) # PyObjC will do the releasing when the Python var goes out of scope
	# or: we just retain() and don't keep a Python ref to it

	w.display()
	w.orderFrontRegardless()
	w.makeMainWindow()
	w.makeKeyWindow()

	app.activateIgnoringOtherApps_(True)
	# see http://stackoverflow.com/questions/12292151/crash-in-class-getname-in-applicationopenuntitledfile
	w.retain()

def setupAfterAppFinishedLaunching(delegate):
	state.quit = quit
	setupAppleMenu()
	setupWindow()
	app.updateWindows()
	print "setupAfterAppFinishedLaunching ready"

class PyAppDelegate(NSObject):
	__metaclass__ = ObjCClassAutorenamer

	# Doc for AppDelegate protocol:
	# https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/NSApplicationDelegate_Protocol/Reference/Reference.html

	def applicationDidFinishLaunching_(self, notification):
		print "AppDelegate didFinishLaunching"
		for m in modules: m.start()
		try:
			setupAfterAppFinishedLaunching(self)
		except:
			sys.excepthook(*sys.exc_info())

	def applicationShouldTerminate_(self, app):
		print "AppDelegate quit"
		for m in modules: m.stop()
		return NSTerminateNow

	def applicationOpenUntitledFile_(self, app):
		setupWindow()
		return True

	def userNotificationCenter_shouldPresentNotification_(self, notifCenter, notif):
		return True

def quit():
	app.terminate_(None)

def setup():
	icon = NSImage.alloc()
	icon.initWithContentsOfFile_(mydir + "/icon.icns")
	app.setApplicationIconImage_(icon)

	appDelegate = PyAppDelegate.alloc().init()
	app.setDelegate_(appDelegate)
	appDelegate.retain()

	app.finishLaunching()

try:
	isReload
except NameError:
	isReload = False
else:
	isReload = True

def reloadModuleHandling():
	print "GUI module reload handler ..."

	for w in app.windows():
		w.close()

	appDelegate = PyAppDelegate.alloc().init()
	app.setDelegate_(appDelegate)
	appDelegate.retain()

	try:
		setupAfterAppFinishedLaunching(appDelegate)
	except:
		sys.excepthook(*sys.exc_info())

def guiMain():
	# This is run from the module system in another thread.
	# We handle some background tasks here.
	# For now, this is a simple stdin handler because the standard stdin handler will not run if we have a GUI.
	import stdinconsole
	fd = sys.stdin.fileno()
	if os.isatty(fd):
		stdinconsole.setTtyNoncanonical(fd, timeout=1)
	else:
		return # stdin handler probably not needed, so just exit this thread

	from threading import currentThread
	thread = currentThread()
	while not thread.cancel:
		ch = os.read(fd,7)
		if ch:
			stdinconsole.handleInput(ch)

def main():
	""" This is called from main.py and will enter the NSApp main loop """
	assert NSThread.isMainThread()
	global app

	app = NSApplication.sharedApplication()
	setup()

	print "entering GUI main loop"
	app.run()

	sys.exit()

if isReload:
	do_in_mainthread(reloadModuleHandling)

if __name__ == "__main__":
	main()

# keep old pools. there is no real safe way to know whether we still have some refs to objects
<commit_msg>remove comment<commit_after>
import sys

if sys.platform != "darwin":
	print "GUI: your platform is probably not supported yet"
	def guiMain(): pass

import objc
from AppKit import *
from collections import deque

try: pools
except NameError: pools = deque()

# just in case that we are not the main thread
pools.append(NSAutoreleasePool.alloc().init())

import os, sys
from State import state, modules
from utils import *
import appinfo

mydir = os.path.dirname(__file__) or os.getcwd()
try:
	app
except NameError: # only declare if not yet declared
	app = None

def setupAppleMenu():

	# http://www.cocoabuilder.com/archive/cocoa/192181-initializing-the-menubar-without-interface-builder.html
	# By Robert Nikander

	mainMenu = NSMenu.alloc().initWithTitle_("MainMenu")
	mi = mainMenu.addItemWithTitle_action_keyEquivalent_("Apple", None, "")
	m = NSMenu.alloc().initWithTitle_("Apple")

	# strange hack
	app.setAppleMenu_(m)
	mainMenu.setSubmenu_forItem_(m, mi)
	mi = m.addItemWithTitle_action_keyEquivalent_('Quit', 'terminate:', 'q')

	app.setMainMenu_(mainMenu)

	return m

def setupWindow():
	# some example code: http://lists.apple.com/archives/cocoa-dev/2004/Jan/msg01389.html

	w = NSWindow.alloc()
	w.initWithContentRect_styleMask_backing_defer_(
		((200.0, 200.0), (250.0, 100.0)),
		NSTitledWindowMask |
		NSClosableWindowMask |
		NSResizableWindowMask,
		NSBackingStoreBuffered, False)
	w.setTitle_(appinfo.progname)

	w.display()
	w.orderFrontRegardless()
	w.makeMainWindow()
	w.makeKeyWindow()

	app.activateIgnoringOtherApps_(True)
	# see http://stackoverflow.com/questions/12292151/crash-in-class-getname-in-applicationopenuntitledfile
	w.retain()

def setupAfterAppFinishedLaunching(delegate):
	state.quit = quit
	setupAppleMenu()
	setupWindow()
	app.updateWindows()
	print "setupAfterAppFinishedLaunching ready"

class PyAppDelegate(NSObject):
	__metaclass__ = ObjCClassAutorenamer

	# Doc for AppDelegate protocol:
	# https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/NSApplicationDelegate_Protocol/Reference/Reference.html

	def applicationDidFinishLaunching_(self, notification):
		print "AppDelegate didFinishLaunching"
		for m in modules: m.start()
		try:
			setupAfterAppFinishedLaunching(self)
		except:
			sys.excepthook(*sys.exc_info())

	def applicationShouldTerminate_(self, app):
		print "AppDelegate quit"
		for m in modules: m.stop()
		return NSTerminateNow

	def applicationOpenUntitledFile_(self, app):
		setupWindow()
		return True

	def userNotificationCenter_shouldPresentNotification_(self, notifCenter, notif):
		return True

def quit():
	app.terminate_(None)

def setup():
	icon = NSImage.alloc()
	icon.initWithContentsOfFile_(mydir + "/icon.icns")
	app.setApplicationIconImage_(icon)

	appDelegate = PyAppDelegate.alloc().init()
	app.setDelegate_(appDelegate)
	appDelegate.retain()

	app.finishLaunching()

try:
	isReload
except NameError:
	isReload = False
else:
	isReload = True

def reloadModuleHandling():
	print "GUI module reload handler ..."

	for w in app.windows():
		w.close()

	appDelegate = PyAppDelegate.alloc().init()
	app.setDelegate_(appDelegate)
	appDelegate.retain()

	try:
		setupAfterAppFinishedLaunching(appDelegate)
	except:
		sys.excepthook(*sys.exc_info())

def guiMain():
	# This is run from the module system in another thread.
	# We handle some background tasks here.
	# For now, this is a simple stdin handler because the standard stdin handler will not run if we have a GUI.
	import stdinconsole
	fd = sys.stdin.fileno()
	if os.isatty(fd):
		stdinconsole.setTtyNoncanonical(fd, timeout=1)
	else:
		return # stdin handler probably not needed, so just exit this thread

	from threading import currentThread
	thread = currentThread()
	while not thread.cancel:
		ch = os.read(fd,7)
		if ch:
			stdinconsole.handleInput(ch)

def main():
	""" This is called from main.py and will enter the NSApp main loop """
	assert NSThread.isMainThread()
	global app

	app = NSApplication.sharedApplication()
	setup()

	print "entering GUI main loop"
	app.run()

	sys.exit()

if isReload:
	do_in_mainthread(reloadModuleHandling)

if __name__ == "__main__":
	main()

# keep old pools. there is no real safe way to know whether we still have some refs to objects
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
from __future__ import with_statement
import os
import pep8
import sys
import stat
from subprocess import Popen, PIPE
import shutil
import tempfile
try:
    from configparser import ConfigParser
except ImportError:   # Python 2
    from ConfigParser import ConfigParser

from flake8 import compat
from flake8.engine import get_parser, get_style_guide
from flake8.main import DEFAULT_CONFIG

def git_hook(complexity=-1, strict=False, ignore=None, lazy=False):
    """This is the function used by the git hook.

    :param int complexity: (optional), any value > 0 enables complexity
        checking with mccabe
    :param bool strict: (optional), if True, this returns the total number of
        errors which will cause the hook to fail
    :param str ignore: (optional), a comma-separated list of errors and
        warnings to ignore
    :param bool lazy: (optional), allows for the instances where you don't add
        the files to the index before running a commit, e.g., git commit -a
    :returns: total number of errors if strict is True, otherwise 0
    """
    gitcmd = "git diff-index --cached --name-only --diff-filter=ACMRTUXB HEAD"
    if lazy:
        # Catch all files, including those not added to the index
        gitcmd = gitcmd.replace('--cached ', '')

    if hasattr(ignore, 'split'):
        ignore = ignore.split(',')

    # Returns the exit code, list of files modified, list of error messages
    _, files_modified, _ = run(gitcmd)

    # We only want to pass ignore and max_complexity if they differ from the
    # defaults so that we don't override a local configuration file
    options = {}
    if ignore:
        options['ignore'] = ignore
    if complexity > -1:
        options['max_complexity'] = complexity

    tmpdir = tempfile.mkdtemp()

    flake8_style = get_style_guide(config_file=DEFAULT_CONFIG, paths=['.'],
                                   **options)
    filepatterns = flake8_style.options.filename

    # Copy staged versions to temporary directory
    files_to_check = []
    try:
        for file_ in files_modified:
            # get the staged version of the file
            gitcmd_getstaged = "git show :%s" % file_
            _, out, _ = run(gitcmd_getstaged, raw_output=True, decode=False)
            # write the staged version to temp dir with its full path to
            # avoid overwriting files with the same name
            dirname, filename = os.path.split(os.path.abspath(file_))
            prefix = os.path.commonprefix([dirname, tmpdir])
            dirname = compat.relpath(dirname, start=prefix)
            dirname = os.path.join(tmpdir, dirname)
            if not os.path.isdir(dirname):
                os.makedirs(dirname)

            # check_files() only does this check if passed a dir; so we do it
            if ((pep8.filename_match(file_, filepatterns) and
                 not flake8_style.excluded(file_))):

                filename = os.path.join(dirname, filename)
                files_to_check.append(filename)
                # write staged version of file to temporary directory
                with open(filename, "wb") as fh:
                    fh.write(out)

        # Run the checks
        report = flake8_style.check_files(files_to_check)
    # remove temporary directory
    finally:
        shutil.rmtree(tmpdir, ignore_errors=True)

    if strict:
        return report.total_errors

    return 0

def hg_hook(ui, repo, **kwargs):
    """This is the function executed directly by Mercurial as part of the
    hook. This is never called directly by the user, so the parameters are
    undocumented. If you would like to learn more about them, please feel free
    to read the official Mercurial documentation.
    """
    complexity = ui.config('flake8', 'complexity', default=-1)
    strict = ui.configbool('flake8', 'strict', default=True)
    ignore = ui.config('flake8', 'ignore', default=None)
    config = ui.config('flake8', 'config', default=DEFAULT_CONFIG)

    paths = _get_files(repo, **kwargs)

    # We only want to pass ignore and max_complexity if they differ from the
    # defaults so that we don't override a local configuration file
    options = {}
    if ignore:
        options['ignore'] = ignore
    if complexity > -1:
        options['max_complexity'] = complexity

    flake8_style = get_style_guide(config_file=config, paths=['.'],
                                   **options)
    report = flake8_style.check_files(paths)

    if strict:
        return report.total_errors

    return 0

def run(command, raw_output=False, decode=True):
    p = Popen(command.split(), stdout=PIPE, stderr=PIPE)
    (stdout, stderr) = p.communicate()
    # On python 3, subprocess.Popen returns bytes objects which expect
    # endswith to be given a bytes object or a tuple of bytes but not native
    # string objects. This is simply less mysterious than using b'.py' in the
    # endswith method. That should work but might still fail horribly.
    if decode:
        if hasattr(stdout, 'decode'):
            stdout = stdout.decode('utf-8')
        if hasattr(stderr, 'decode'):
            stderr = stderr.decode('utf-8')
    if not raw_output:
        stdout = [line.strip() for line in stdout.splitlines()]
        stderr = [line.strip() for line in stderr.splitlines()]
    return (p.returncode, stdout, stderr)

def _get_files(repo, **kwargs):
    seen = set()
    for rev in range(repo[kwargs['node']], len(repo)):
        for file_ in repo[rev].files():
            file_ = os.path.join(repo.root, file_)
            if file_ in seen or not os.path.exists(file_):
                continue
            seen.add(file_)
            if file_.endswith('.py'):
                yield file_

def find_vcs():
    _, git_dir, _ = run('git rev-parse --git-dir')
    if git_dir and os.path.isdir(git_dir[0]):
        if not os.path.isdir(os.path.join(git_dir[0], 'hooks')):
            os.mkdir(os.path.join(git_dir[0], 'hooks'))
        return os.path.join(git_dir[0], 'hooks', 'pre-commit')
    _, hg_dir, _ = run('hg root')
    if hg_dir and os.path.isdir(hg_dir[0]):
        return os.path.join(hg_dir[0], '.hg', 'hgrc')
    return ''

def get_git_config(option, opt_type='', convert_type=True):
    # type can be --bool, --int or an empty string
    _, git_cfg_value, _ = run('git config --get %s %s' % (opt_type, option),
                              raw_output=True)
    git_cfg_value = git_cfg_value.strip()
    if not convert_type:
        return git_cfg_value
    if opt_type == '--bool':
        git_cfg_value = git_cfg_value.lower() == 'true'
    elif git_cfg_value and opt_type == '--int':
        git_cfg_value = int(git_cfg_value)
    return git_cfg_value

_params = {
    'FLAKE8_COMPLEXITY': '--int',
    'FLAKE8_STRICT': '--bool',
    'FLAKE8_IGNORE': '',
    'FLAKE8_LAZY': '--bool',
}

def get_git_param(option, default=''):
    global _params
    opt_type = _params[option]
    param_value = get_git_config(option.lower().replace('_', '.'),
                                 opt_type=opt_type, convert_type=False)
    if param_value == '':
        param_value = os.environ.get(option, default)
    if opt_type == '--bool' and not isinstance(param_value, bool):
        param_value = param_value.lower() == 'true'
    elif param_value and opt_type == '--int':
        param_value = int(param_value)
    return param_value

git_hook_file = """#!/usr/bin/env python
import sys
from flake8.hooks import git_hook, get_git_param

# `get_git_param` will retrieve configuration from your local git config and
# then fall back to using the environment variables that the hook has always
# supported.
# For example, to set the complexity, you'll need to do:
#   git config flake8.complexity 10
COMPLEXITY = get_git_param('FLAKE8_COMPLEXITY', 10)
STRICT = get_git_param('FLAKE8_STRICT', False)
IGNORE = get_git_param('FLAKE8_IGNORE', None)
LAZY = get_git_param('FLAKE8_LAZY', False)

if __name__ == '__main__':
    sys.exit(git_hook(
        complexity=COMPLEXITY,
        strict=STRICT,
        ignore=IGNORE,
        lazy=LAZY,
        ))
"""

def _install_hg_hook(path):
    getenv = os.environ.get
    if not os.path.isfile(path):
        # Make the file so we can avoid IOError's
        open(path, 'w').close()

    c = ConfigParser()
    c.readfp(open(path, 'r'))
    if not c.has_section('hooks'):
        c.add_section('hooks')

    if not c.has_option('hooks', 'commit'):
        c.set('hooks', 'commit', 'python:flake8.hooks.hg_hook')

    if not c.has_option('hooks', 'qrefresh'):
        c.set('hooks', 'qrefresh', 'python:flake8.hooks.hg_hook')

    if not c.has_section('flake8'):
        c.add_section('flake8')

    if not c.has_option('flake8', 'complexity'):
        c.set('flake8', 'complexity', str(getenv('FLAKE8_COMPLEXITY', 10)))

    if not c.has_option('flake8', 'strict'):
        c.set('flake8', 'strict', getenv('FLAKE8_STRICT', False))

    if not c.has_option('flake8', 'ignore'):
        c.set('flake8', 'ignore', getenv('FLAKE8_IGNORE', ''))

    if not c.has_option('flake8', 'lazy'):
        c.set('flake8', 'lazy', getenv('FLAKE8_LAZY', False))

    with open(path, 'w') as fd:
        c.write(fd)

def install_hook():
    vcs = find_vcs()

    if not vcs:
        p = get_parser()[0]
        sys.stderr.write('Error: could not find either a git or mercurial '
                         'directory. Please re-run this in a proper '
                         'repository.')
        p.print_help()
        sys.exit(1)

    status = 0
    if 'git' in vcs:
        if os.path.exists(vcs):
            sys.exit('Error: hook already exists (%s)' % vcs)
        with open(vcs, 'w') as fd:
            fd.write(git_hook_file)
        # rwxr--r--
        os.chmod(vcs, stat.S_IRWXU | stat.S_IRGRP | stat.S_IROTH)
    elif 'hg' in vcs:
        _install_hg_hook(vcs)
    else:
        status = 1

    sys.exit(status)
<commit_msg>Fix checking for repo dir without git/hg<commit_after># -*- coding: utf-8 -*-
from __future__ import with_statement
import os
import pep8
import sys
import stat
from subprocess import Popen, PIPE
import shutil
import tempfile
try:
    from configparser import ConfigParser
except ImportError:   # Python 2
    from ConfigParser import ConfigParser

from flake8 import compat
from flake8.engine import get_parser, get_style_guide
from flake8.main import DEFAULT_CONFIG

def git_hook(complexity=-1, strict=False, ignore=None, lazy=False):
    """This is the function used by the git hook.

    :param int complexity: (optional), any value > 0 enables complexity
        checking with mccabe
    :param bool strict: (optional), if True, this returns the total number of
        errors which will cause the hook to fail
    :param str ignore: (optional), a comma-separated list of errors and
        warnings to ignore
    :param bool lazy: (optional), allows for the instances where you don't add
        the files to the index before running a commit, e.g., git commit -a
    :returns: total number of errors if strict is True, otherwise 0
    """
    gitcmd = "git diff-index --cached --name-only --diff-filter=ACMRTUXB HEAD"
    if lazy:
        # Catch all files, including those not added to the index
        gitcmd = gitcmd.replace('--cached ', '')

    if hasattr(ignore, 'split'):
        ignore = ignore.split(',')

    # Returns the exit code, list of files modified, list of error messages
    _, files_modified, _ = run(gitcmd)

    # We only want to pass ignore and max_complexity if they differ from the
    # defaults so that we don't override a local configuration file
    options = {}
    if ignore:
        options['ignore'] = ignore
    if complexity > -1:
        options['max_complexity'] = complexity

    tmpdir = tempfile.mkdtemp()

    flake8_style = get_style_guide(config_file=DEFAULT_CONFIG, paths=['.'],
                                   **options)
    filepatterns = flake8_style.options.filename

    # Copy staged versions to temporary directory
    files_to_check = []
    try:
        for file_ in files_modified:
            # get the staged version of the file
            gitcmd_getstaged = "git show :%s" % file_
            _, out, _ = run(gitcmd_getstaged, raw_output=True, decode=False)
            # write the staged version to temp dir with its full path to
            # avoid overwriting files with the same name
            dirname, filename = os.path.split(os.path.abspath(file_))
            prefix = os.path.commonprefix([dirname, tmpdir])
            dirname = compat.relpath(dirname, start=prefix)
            dirname = os.path.join(tmpdir, dirname)
            if not os.path.isdir(dirname):
                os.makedirs(dirname)

            # check_files() only does this check if passed a dir; so we do it
            if ((pep8.filename_match(file_, filepatterns) and
                 not flake8_style.excluded(file_))):

                filename = os.path.join(dirname, filename)
                files_to_check.append(filename)
                # write staged version of file to temporary directory
                with open(filename, "wb") as fh:
                    fh.write(out)

        # Run the checks
        report = flake8_style.check_files(files_to_check)
    # remove temporary directory
    finally:
        shutil.rmtree(tmpdir, ignore_errors=True)

    if strict:
        return report.total_errors

    return 0

def hg_hook(ui, repo, **kwargs):
    """This is the function executed directly by Mercurial as part of the
    hook. This is never called directly by the user, so the parameters are
    undocumented. If you would like to learn more about them, please feel free
    to read the official Mercurial documentation.
    """
    complexity = ui.config('flake8', 'complexity', default=-1)
    strict = ui.configbool('flake8', 'strict', default=True)
    ignore = ui.config('flake8', 'ignore', default=None)
    config = ui.config('flake8', 'config', default=DEFAULT_CONFIG)

    paths = _get_files(repo, **kwargs)

    # We only want to pass ignore and max_complexity if they differ from the
    # defaults so that we don't override a local configuration file
    options = {}
    if ignore:
        options['ignore'] = ignore
    if complexity > -1:
        options['max_complexity'] = complexity

    flake8_style = get_style_guide(config_file=config, paths=['.'],
                                   **options)
    report = flake8_style.check_files(paths)

    if strict:
        return report.total_errors

    return 0

def run(command, raw_output=False, decode=True):
    p = Popen(command.split(), stdout=PIPE, stderr=PIPE)
    (stdout, stderr) = p.communicate()
    # On python 3, subprocess.Popen returns bytes objects which expect
    # endswith to be given a bytes object or a tuple of bytes but not native
    # string objects. This is simply less mysterious than using b'.py' in the
    # endswith method. That should work but might still fail horribly.
    if decode:
        if hasattr(stdout, 'decode'):
            stdout = stdout.decode('utf-8')
        if hasattr(stderr, 'decode'):
            stderr = stderr.decode('utf-8')
    if not raw_output:
        stdout = [line.strip() for line in stdout.splitlines()]
        stderr = [line.strip() for line in stderr.splitlines()]
    return (p.returncode, stdout, stderr)

def _get_files(repo, **kwargs):
    seen = set()
    for rev in range(repo[kwargs['node']], len(repo)):
        for file_ in repo[rev].files():
            file_ = os.path.join(repo.root, file_)
            if file_ in seen or not os.path.exists(file_):
                continue
            seen.add(file_)
            if file_.endswith('.py'):
                yield file_

def find_vcs():
    try:
        _, git_dir, _ = run('git rev-parse --git-dir')
        if git_dir and os.path.isdir(git_dir[0]):
            if not os.path.isdir(os.path.join(git_dir[0], 'hooks')):
                os.mkdir(os.path.join(git_dir[0], 'hooks'))
            return os.path.join(git_dir[0], 'hooks', 'pre-commit')
    except OSError:
        pass
    try:
        _, hg_dir, _ = run('hg root')
        if hg_dir and os.path.isdir(hg_dir[0]):
            return os.path.join(hg_dir[0], '.hg', 'hgrc')
    except OSError:
        pass
    return ''

def get_git_config(option, opt_type='', convert_type=True):
    # type can be --bool, --int or an empty string
    _, git_cfg_value, _ = run('git config --get %s %s' % (opt_type, option),
                              raw_output=True)
    git_cfg_value = git_cfg_value.strip()
    if not convert_type:
        return git_cfg_value
    if opt_type == '--bool':
        git_cfg_value = git_cfg_value.lower() == 'true'
    elif git_cfg_value and opt_type == '--int':
        git_cfg_value = int(git_cfg_value)
    return git_cfg_value

_params = {
    'FLAKE8_COMPLEXITY': '--int',
    'FLAKE8_STRICT': '--bool',
    'FLAKE8_IGNORE': '',
    'FLAKE8_LAZY': '--bool',
}

def get_git_param(option, default=''):
    global _params
    opt_type = _params[option]
    param_value = get_git_config(option.lower().replace('_', '.'),
                                 opt_type=opt_type, convert_type=False)
    if param_value == '':
        param_value = os.environ.get(option, default)
    if opt_type == '--bool' and not isinstance(param_value, bool):
        param_value = param_value.lower() == 'true'
    elif param_value and opt_type == '--int':
        param_value = int(param_value)
    return param_value

git_hook_file = """#!/usr/bin/env python
import sys
from flake8.hooks import git_hook, get_git_param

# `get_git_param` will retrieve configuration from your local git config and
# then fall back to using the environment variables that the hook has always
# supported.
# For example, to set the complexity, you'll need to do:
#   git config flake8.complexity 10
COMPLEXITY = get_git_param('FLAKE8_COMPLEXITY', 10)
STRICT = get_git_param('FLAKE8_STRICT', False)
IGNORE = get_git_param('FLAKE8_IGNORE', None)
LAZY = get_git_param('FLAKE8_LAZY', False)

if __name__ == '__main__':
    sys.exit(git_hook(
        complexity=COMPLEXITY,
        strict=STRICT,
        ignore=IGNORE,
        lazy=LAZY,
        ))
"""

def _install_hg_hook(path):
    getenv = os.environ.get
    if not os.path.isfile(path):
        # Make the file so we can avoid IOError's
        open(path, 'w').close()

    c = ConfigParser()
    c.readfp(open(path, 'r'))
    if not c.has_section('hooks'):
        c.add_section('hooks')

    if not c.has_option('hooks', 'commit'):
        c.set('hooks', 'commit', 'python:flake8.hooks.hg_hook')

    if not c.has_option('hooks', 'qrefresh'):
        c.set('hooks', 'qrefresh', 'python:flake8.hooks.hg_hook')

    if not c.has_section('flake8'):
        c.add_section('flake8')

    if not c.has_option('flake8', 'complexity'):
        c.set('flake8', 'complexity', str(getenv('FLAKE8_COMPLEXITY', 10)))

    if not c.has_option('flake8', 'strict'):
        c.set('flake8', 'strict', getenv('FLAKE8_STRICT', False))

    if not c.has_option('flake8', 'ignore'):
        c.set('flake8', 'ignore', getenv('FLAKE8_IGNORE', ''))

    if not c.has_option('flake8', 'lazy'):
        c.set('flake8', 'lazy', getenv('FLAKE8_LAZY', False))

    with open(path, 'w') as fd:
        c.write(fd)

def install_hook():
    vcs = find_vcs()

    if not vcs:
        p = get_parser()[0]
        sys.stderr.write('Error: could not find either a git or mercurial '
                         'directory. Please re-run this in a proper '
                         'repository.\n')
        p.print_help()
        sys.exit(1)

    status = 0
    if 'git' in vcs:
        if os.path.exists(vcs):
            sys.exit('Error: hook already exists (%s)' % vcs)
        with open(vcs, 'w') as fd:
            fd.write(git_hook_file)
        # rwxr--r--
        os.chmod(vcs, stat.S_IRWXU | stat.S_IRGRP | stat.S_IROTH)
    elif 'hg' in vcs:
        _install_hg_hook(vcs)
    else:
        status = 1

    sys.exit(status)
<|endoftext|>


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

import sys
import os
import re

def name_and_owner(s):
    data = s.split('[')
    name = data[0].strip()
    owner = None

    if len(data) > 1:
        owner = data[1]
        if owner[-1] != ']':
            raise Exception("Closing bracket missing in owner")

        owner = owner[:-1].strip()

    return [name, owner]

class Team:
    def __init__(self, name):
        data = name.split('(')
        self.name = data[0].strip()
        self.shortname = self.name

        if len(data) > 1:
            self.shortname = data[1]
            if self.shortname[-1] != ')':
                raise Exception("Closing parenthesis missing in team short name '{}'".format(name))

            self.shortname = self.shortname[:-1].strip()

        self.ftes = 0
        self.contr = 0
        self.reqs = 0

    @property
    def headcount(self):
        return self.ftes + self.contr + self.reqs

class Initiative:
    def __init__(self, name):
        [self.name, self.owner] = name_and_owner(name)
        self.toplinegoals = []
        self.kpis = []
        self.projects = []

class Project:
    def __init__(self, name, initiative):
        [self.name, self.owner] = name_and_owner(name)
        self.initiative = initiative
        self.when = None
        self.targets = []
        self.dependencies = ""
        self.res = {}
        self.res_total = 0.0

class Target:
    def __init__(self, name, project):
        [self.name, self.owner] = name_and_owner(name)
        self.project = project
        self.when = None
        self.dependencies = ""
        self.resources = None

    def addResources(self, r):
        if self.resources == None:
            self.resources = {}

        r = r.strip()

        if len(r) == 0:
            return

        if r == '0':
            r = "none 0"

        for m in r.split(','):
            team = ""
            res = "0"
            try:
                m = m.strip()
                (team, res) = m.split()

                team = team.strip()
                res = res.strip()

                if res == "?":
                    if verbose:
                        print("Resourcing in {} TBD for {}" \
                              .format(team, self.name))
                elif res.startswith('$'):
                    team += " ($)"
                    res = res[1:]
                    self.resources[team] = self.resources.setdefault(team, 0) + float(res)
                else:
                    self.resources[team] = self.resources.setdefault(team, 0) + float(res)
            except:
                if verbose:
                    print("Invalid resource declaration '{}' in target {}" \
                          .format(m, self.name))

cur_team = None
cur_initiative = None
cur_project = None
cur_target = None

in_teams = None
in_topline_goal = False
in_kpi = False

initiatives = []
maintenance = None
teams = {}

verbose = len(sys.argv) > 1 and sys.argv[1] == "-v"

topline_goals = {
    "Build for Quality": "A. Build for Quality",
    "Invite Participation": "B. Invite Participation ",
    "Be Clear, Compelling & Focused": "C. Be Clear, Compelling & Focused ",
    "Grow Our Influence": "D. Grow Our Influence ",
    "Prototype the Future": "E. Prototype the Future "
}

INPUT_PATH = os.path.join(os.path.expanduser("~"),
                          "Downloads/2016H1Initiatives.txt")

if os.path.exists(INPUT_PATH):
    os.rename(INPUT_PATH, INPUT_PATH + ".tmp")

with open(os.path.join(INPUT_PATH + ".tmp"), "r") as f:
    for line in f:
        line = line[:-1]
        if line.encode('utf-8').startswith(b'\xef\xbb\xbf'):
            line = line[1:]

        origline = line
        line = line.strip()

        line = re.sub(r'\[..?\]', '', line)

        if len(line) == 0:
            continue

        #print(origline)

        if line.startswith("* Not going to happen"):
            break

        if origline == "Teams:":
            in_teams = True
            continue

        if in_teams:
            if line.startswith("* Notes: "):
                continue

            if line.startswith("* Team: "):
                cur_team = Team(line[8:])

                teams[cur_team.shortname] = cur_team

                continue

            if line.startswith("* Current FTEs:"):
                ftes = line[15:].strip()

                if len(ftes) > 0:
                    cur_team.ftes = int(ftes)

                continue

            if line.startswith("* Open reqs:"):
                reqs = line[12:].strip()

                if len(reqs) > 0:
                    cur_team.reqs = int(reqs)

                continue

            if line.startswith("* Current Contractors:"):
                contr = line[22:].strip()

                if len(contr) > 0:
                    cur_team.contr = int(contr)

                continue

            if line.startswith("* Initiative: "):
                in_teams = False
            else:
                if verbose:
                    print(line)

                continue

        if line.startswith("* Initiative: Platform Maintenance"):
            cur_initiative = Initiative(line[14:])
            maintenance = cur_initiative
            cur_project = None
            continue

        if line.startswith("* Initiative: "):
            cur_initiative = Initiative(line[14:])
            initiatives.append(cur_initiative)
            cur_project = None
            continue

        if line.startswith("* Supported topline goals:"):
            in_topline_goal = True
            continue

        if line.startswith("* Key Performance Indicators (KPI):"):
            in_topline_goal = False
            in_kpi = True
            continue

        if line.startswith("* Project/Deliverable: "):
            in_topline_goal = False
            in_kpi = False

            cur_project = Project(line[23:], cur_initiative)
            cur_initiative.projects.append(cur_project)
            continue

        if line.startswith("* Team: ") and cur_initiative == maintenance:
            in_topline_goal = False
            in_kpi = False

            cur_project = Project(line[8:], cur_initiative)
            cur_initiative.projects.append(cur_project)
            continue

        if line.startswith("* Target:"):
            cur_project.targets.append(Target(line[10:], cur_project))

            continue

        if line.startswith("* Drivers: ") or \
           line.startswith("* Notes: ") or \
           line.startswith("* Feedback Notes: "):
            continue

        if origline.startswith("      * When:"):
            cur_project.when = line[8:]
            continue

        if origline.startswith("         * When:"):
            cur_project.targets[-1].when = line[8:]
            continue

        if origline.startswith("      * Dependencies: "):
            cur_project.dependencies = line[16:]
            continue

        if origline.startswith("         * Dependencies: "):
            cur_project.targets[-1].dependencies = line[16:]
            continue

        if line.startswith("* Resources:"):
            cur_project.targets[-1].addResources(line[12:])
            continue

        if in_topline_goal is True:
            cur_initiative.toplinegoals.append(line[2:])
            continue

        if in_kpi is True:
            cur_initiative.kpis.append(line[2:])
            continue

        if verbose:
            print(origline)

if verbose:
    print("\n\n\ndump:\n\n")

def dump_all():
    for i in initiatives:
        print(i.name)
        print("goals: {}".format(i.toplinegoals))
        print("kpis: {}".format(i.kpis))
        for p in i.projects:
            print("  project: {}".format(p.name))

            if p.when != None:
                print("    when: {}".format(p.when))

            for t in p.targets:
                print("    target: {}".format(t.name))

                if t.when != None:
                    print("      when: {}".format(t.when))
def dump_CSV_all():
    for i in initiatives:
        for p in i.projects:
            targets = ""
            for t in p.targets:
                if targets != "":
                    targets += '\n'

                when = None
                if t.when != None:
                    when = t.when
                elif p.when != None:
                    when = p.when

                targets += t.name

                if when != None:
                    targets += " [{}]".format(when)

            if not i.toplinegoals[0] in topline_goals:
                raise Exception("Invalid topline goal: '{}'".format(i.toplinegoals[0]))

            print('"{}", "{}", "", "{}", "", "{}", "{}"' \
                  .format(topline_goals[i.toplinegoals[0]], i.name, p.name,
                          targets, p.when or ""))

def dump_resources():
    dollar_targets = []
    def dump_initiative_resources(initiatives):
        res = {}
        for i in initiatives:
            for p in i.projects:
                if i != maintenance:
                    if not p.owner:
                        print("Missing owner for project {}".format(p.name))

                for t in p.targets:
                    if t.resources == None:
                        print("Resource declaration missing from {}, {}" \
                              .format(p.name, t.name))
                    elif t.resources:
                        for r in t.resources:
                            if r == '':
                                print("Missing team name for target {}" \
                                      .format(t.name))

                                continue

                            if r.endswith(" ($)"):
                                dollar_targets.append(t)

                                continue

                            res[r] = res.setdefault(r, 0) + t.resources[r]
                            p.res[r] = p.res.setdefault(r, 0) + t.resources[r]
                            p.res_total += t.resources[r]

                        #print("{}, {}: {}".format(p.name, t.name, t.resources))
                    else:
                        if verbose:
                            print("No resource request for target {}." \
                                  .format(t.name))

        return res

    res = dump_initiative_resources(initiatives + [maintenance])

    def dump_res(res, title, dump_delta = False):
        print("\n\n" + title)
        total = 0.0

        for team in sorted(res.keys(), key=str.lower):
            if team == "none" and res[team] == 0:
                continue

            delta = ""

            if dump_delta:
                s = " (unknown team"

                if team in teams:
                    t = teams[team]
                    if t.headcount == 0:
                        s = " (no current data"
                    else:
                        new = res[team] - t.headcount
                        reqs = t.reqs

                        s = " (current {}".format(t.ftes + t.contr)

                        if t.reqs:
                            s += ", reqs {}".format(t.reqs)

                        s += ", new {}{:.2f}" \
                             .format('+' if new >= 0 else '', new)

                delta = s + ")"

            print("  {: <15s}: {:.2f}{}" \
                  .format(team, res[team], delta))

            total += res[team]

        print("  ----------------------\n  Total          : {:.2f}".format(total))

    dump_res(res, "All resources", True)

    projects = []

    for i in initiatives:
        projects += i.projects

    for p in sorted(projects, key=lambda p: -p.res_total):
        dump_res(p.res, "Project: {} [{}]".format(p.name, p.owner))

    for p in sorted(maintenance.projects, key=lambda p: -p.res_total):
        dump_res(p.res, "Maintenance: {}".format(p.name))

    print("\nMoney requested:")

    for t in dollar_targets:
        for r in t.resources:
            if not r.endswith(" ($)"):
                continue

            print("  ${}k allocated for {} for {}, {}".format(int(t.resources[r] / 1000), r, t.project.name, t.name))

def dump_CSV_projects():
    res = {}
    for i in initiatives:
        for p in i.projects:
            when = ""
            if p.when != None:
                when = p.when

            if not i.toplinegoals[0] in topline_goals:
                raise Exception("Invalid topline goal: '{}'" \
                                .format(i.toplinegoals[0]))

            print('"{}", "{}", "", "{}", "", "", "{}"' \
                  .format(topline_goals[i.toplinegoals[0]], i.name, p.name,
                          when))

dump_resources()
<commit_msg>Whitespace cleanup...<commit_after>#!/usr/bin/env python

import sys
import os
import re

def name_and_owner(s):
    data = s.split('[')
    name = data[0].strip()
    owner = None

    if len(data) > 1:
        owner = data[1]
        if owner[-1] != ']':
            raise Exception("Closing bracket missing in owner")

        owner = owner[:-1].strip()

    return [name, owner]

class Team:
    def __init__(self, name):
        data = name.split('(')
        self.name = data[0].strip()
        self.shortname = self.name

        if len(data) > 1:
            self.shortname = data[1]
            if self.shortname[-1] != ')':
                raise Exception("Closing parenthesis missing in team short name '{}'".format(name))

            self.shortname = self.shortname[:-1].strip()

        self.ftes = 0
        self.contr = 0
        self.reqs = 0

    @property
    def headcount(self):
        return self.ftes + self.contr + self.reqs

class Initiative:
    def __init__(self, name):
        [self.name, self.owner] = name_and_owner(name)
        self.toplinegoals = []
        self.kpis = []
        self.projects = []

class Project:
    def __init__(self, name, initiative):
        [self.name, self.owner] = name_and_owner(name)
        self.initiative = initiative
        self.when = None
        self.targets = []
        self.dependencies = ""
        self.res = {}
        self.res_total = 0.0

class Target:
    def __init__(self, name, project):
        [self.name, self.owner] = name_and_owner(name)
        self.project = project
        self.when = None
        self.dependencies = ""
        self.resources = None

    def addResources(self, r):
        if self.resources == None:
            self.resources = {}

        r = r.strip()

        if len(r) == 0:
            return

        if r == '0':
            r = "none 0"

        for m in r.split(','):
            team = ""
            res = "0"
            try:
                m = m.strip()
                (team, res) = m.split()

                team = team.strip()
                res = res.strip()

                if res == "?":
                    if verbose:
                        print("Resourcing in {} TBD for {}" \
                              .format(team, self.name))
                elif res.startswith('$'):
                    team += " ($)"
                    res = res[1:]
                    self.resources[team] = self.resources.setdefault(team, 0) + float(res)
                else:
                    self.resources[team] = self.resources.setdefault(team, 0) + float(res)
            except:
                if verbose:
                    print("Invalid resource declaration '{}' in target {}" \
                          .format(m, self.name))

cur_team = None
cur_initiative = None
cur_project = None
cur_target = None

in_teams = None
in_topline_goal = False
in_kpi = False

initiatives = []
maintenance = None
teams = {}

verbose = len(sys.argv) > 1 and sys.argv[1] == "-v"

topline_goals = {
    "Build for Quality": "A. Build for Quality",
    "Invite Participation": "B. Invite Participation ",
    "Be Clear, Compelling & Focused": "C. Be Clear, Compelling & Focused ",
    "Grow Our Influence": "D. Grow Our Influence ",
    "Prototype the Future": "E. Prototype the Future "
}

INPUT_PATH = os.path.join(os.path.expanduser("~"),
                          "Downloads/2016H1Initiatives.txt")

if os.path.exists(INPUT_PATH):
    os.rename(INPUT_PATH, INPUT_PATH + ".tmp")

with open(os.path.join(INPUT_PATH + ".tmp"), "r") as f:
    for line in f:
        line = line[:-1]
        if line.encode('utf-8').startswith(b'\xef\xbb\xbf'):
            line = line[1:]

        origline = line
        line = line.strip()

        line = re.sub(r'\[..?\]', '', line)

        if len(line) == 0:
            continue

        #print(origline)

        if line.startswith("* Not going to happen"):
            break

        if origline == "Teams:":
            in_teams = True
            continue

        if in_teams:
            if line.startswith("* Notes: "):
                continue

            if line.startswith("* Team: "):
                cur_team = Team(line[8:])

                teams[cur_team.shortname] = cur_team

                continue

            if line.startswith("* Current FTEs:"):
                ftes = line[15:].strip()

                if len(ftes) > 0:
                    cur_team.ftes = int(ftes)

                continue

            if line.startswith("* Open reqs:"):
                reqs = line[12:].strip()

                if len(reqs) > 0:
                    cur_team.reqs = int(reqs)

                continue

            if line.startswith("* Current Contractors:"):
                contr = line[22:].strip()

                if len(contr) > 0:
                    cur_team.contr = int(contr)

                continue

            if line.startswith("* Initiative: "):
                in_teams = False
            else:
                if verbose:
                    print(line)

                continue

        if line.startswith("* Initiative: Platform Maintenance"):
            cur_initiative = Initiative(line[14:])
            maintenance = cur_initiative
            cur_project = None
            continue

        if line.startswith("* Initiative: "):
            cur_initiative = Initiative(line[14:])
            initiatives.append(cur_initiative)
            cur_project = None
            continue

        if line.startswith("* Supported topline goals:"):
            in_topline_goal = True
            continue

        if line.startswith("* Key Performance Indicators (KPI):"):
            in_topline_goal = False
            in_kpi = True
            continue

        if line.startswith("* Project/Deliverable: "):
            in_topline_goal = False
            in_kpi = False

            cur_project = Project(line[23:], cur_initiative)
            cur_initiative.projects.append(cur_project)
            continue

        if line.startswith("* Team: ") and cur_initiative == maintenance:
            in_topline_goal = False
            in_kpi = False

            cur_project = Project(line[8:], cur_initiative)
            cur_initiative.projects.append(cur_project)
            continue

        if line.startswith("* Target:"):
            cur_project.targets.append(Target(line[10:], cur_project))

            continue

        if line.startswith("* Drivers: ") or \
           line.startswith("* Notes: ") or \
           line.startswith("* Feedback Notes: "):
            continue

        if origline.startswith("      * When:"):
            cur_project.when = line[8:]
            continue

        if origline.startswith("         * When:"):
            cur_project.targets[-1].when = line[8:]
            continue

        if origline.startswith("      * Dependencies: "):
            cur_project.dependencies = line[16:]
            continue

        if origline.startswith("         * Dependencies: "):
            cur_project.targets[-1].dependencies = line[16:]
            continue

        if line.startswith("* Resources:"):
            cur_project.targets[-1].addResources(line[12:])
            continue

        if in_topline_goal is True:
            cur_initiative.toplinegoals.append(line[2:])
            continue

        if in_kpi is True:
            cur_initiative.kpis.append(line[2:])
            continue

        if verbose:
            print(origline)

if verbose:
    print("\n\n\ndump:\n\n")

def dump_all():
    for i in initiatives:
        print(i.name)
        print("goals: {}".format(i.toplinegoals))
        print("kpis: {}".format(i.kpis))
        for p in i.projects:
            print("  project: {}".format(p.name))

            if p.when != None:
                print("    when: {}".format(p.when))

            for t in p.targets:
                print("    target: {}".format(t.name))

                if t.when != None:
                    print("      when: {}".format(t.when))
def dump_CSV_all():
    for i in initiatives:
        for p in i.projects:
            targets = ""
            for t in p.targets:
                if targets != "":
                    targets += '\n'

                when = None
                if t.when != None:
                    when = t.when
                elif p.when != None:
                    when = p.when

                targets += t.name

                if when != None:
                    targets += " [{}]".format(when)

            if not i.toplinegoals[0] in topline_goals:
                raise Exception("Invalid topline goal: '{}'" \
                                .format(i.toplinegoals[0]))

            print('"{}", "{}", "", "{}", "", "{}", "{}"' \
                  .format(topline_goals[i.toplinegoals[0]], i.name, p.name,
                          targets, p.when or ""))

def dump_resources():
    dollar_targets = []
    def dump_initiative_resources(initiatives):
        res = {}
        for i in initiatives:
            for p in i.projects:
                if i != maintenance:
                    if not p.owner:
                        print("Missing owner for project {}".format(p.name))

                for t in p.targets:
                    if t.resources == None:
                        print("Resource declaration missing from {}, {}" \
                              .format(p.name, t.name))
                    elif t.resources:
                        for r in t.resources:
                            if r == '':
                                print("Missing team name for target {}" \
                                      .format(t.name))

                                continue

                            if r.endswith(" ($)"):
                                dollar_targets.append(t)

                                continue

                            res[r] = res.setdefault(r, 0) + t.resources[r]
                            p.res[r] = p.res.setdefault(r, 0) + t.resources[r]
                            p.res_total += t.resources[r]

                        #print("{}, {}: {}".format(p.name, t.name, t.resources))
                    else:
                        if verbose:
                            print("No resource request for target {}." \
                                  .format(t.name))

        return res

    res = dump_initiative_resources(initiatives + [maintenance])

    def dump_res(res, title, dump_delta = False):
        print("\n\n" + title)
        total = 0.0

        for team in sorted(res.keys(), key=str.lower):
            if team == "none" and res[team] == 0:
                continue

            delta = ""

            if dump_delta:
                s = " (unknown team"

                if team in teams:
                    t = teams[team]
                    if t.headcount == 0:
                        s = " (no current data"
                    else:
                        new = res[team] - t.headcount
                        reqs = t.reqs

                        s = " (current {}".format(t.ftes + t.contr)

                        if t.reqs:
                            s += ", reqs {}".format(t.reqs)

                        s += ", new {}{:.2f}" \
                             .format('+' if new >= 0 else '', new)

                delta = s + ")"

            print("  {: <15s}: {:.2f}{}" \
                  .format(team, res[team], delta))

            total += res[team]

        print("  ----------------------\n  Total          : {:.2f}" \
              .format(total))

    dump_res(res, "All resources", True)

    projects = []

    for i in initiatives:
        projects += i.projects

    for p in sorted(projects, key=lambda p: -p.res_total):
        dump_res(p.res, "Project: {} [{}]".format(p.name, p.owner))

    for p in sorted(maintenance.projects, key=lambda p: -p.res_total):
        dump_res(p.res, "Maintenance: {}".format(p.name))

    print("\nMoney requested:")

    for t in dollar_targets:
        for r in t.resources:
            if not r.endswith(" ($)"):
                continue

            print("  ${}k allocated for {} for {}, {}" \
                  .format(int(t.resources[r] / 1000), r, t.project.name,
                          t.name))

def dump_CSV_projects():
    res = {}
    for i in initiatives:
        for p in i.projects:
            when = ""
            if p.when != None:
                when = p.when

            if not i.toplinegoals[0] in topline_goals:
                raise Exception("Invalid topline goal: '{}'" \
                                .format(i.toplinegoals[0]))

            print('"{}", "{}", "", "{}", "", "", "{}"' \
                  .format(topline_goals[i.toplinegoals[0]], i.name, p.name,
                          when))

dump_resources()
<|endoftext|>


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

"""
ZetCode PySide tutorial

In this example, we position two push
buttons in the bottom-right corner
of the window.

author: Jan Bodnar
website: zetcode.com
last edited: August 2011
"""

import sys
from PySide import QtGui
from os import listdir

_main_path = "test_images/"

class Example(QtGui.QWidget):

    def __init__(self):
        super(Example, self).__init__()

        self.initUI()

    def list_files(self):
        path = '/Users/james/projects/cryptopear/test_images/'
        files = listdir(path)
        return map(lambda f: path + f, files)

    def initUI(self):

        self.paths = self.list_files()

        pixmap = QtGui.QPixmap(self.paths.pop(0)).scaledToHeight(200)
        self.lbl = QtGui.QLabel(self)
        self.lbl.setPixmap(pixmap)

        btn_accept = QtGui.QPushButton("Accept")
        btn_reject = QtGui.QPushButton("Reject")
        btn_accept.clicked.connect(self.accept_participent)
        btn_reject.clicked.connect(self.reject_participent)

        hbox = QtGui.QHBoxLayout()
        hbox.addStretch(1)
        hbox.addWidget(btn_accept)
        hbox.addWidget(btn_reject)

        vbox = QtGui.QVBoxLayout()
        vbox.addStretch(1)
        vbox.addWidget(self.lbl)
        vbox.addLayout(hbox)

        self.setLayout(vbox)

        #starting x, starting y, width, height
        self.setGeometry(0, 0, 500, 300)
        self.setWindowTitle('CryptoPear')
        self.show()

    def set_picture(self, file_path):
        image = QtGui.QImage(file_path)
        if image.isNull():
            QtGui.QMessageBox.information(self, "Image Viewer",
                    "Cannot load file")
            return

        self.lbl.setPixmap(QtGui.QPixmap.fromImage(image).scaledToHeight(200))
        self.lbl.adjustSize()

    def accept_participent(self):
        self.set_picture(self.paths.pop(0))

    def reject_participent(self):
        print "participant rejected"

def main():

    app = QtGui.QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())

if __name__ == '__main__':
    main()
<commit_msg>added space in gui for text<commit_after>#!/usr/bin/python
# -*- coding: utf-8 -*-

"""
ZetCode PySide tutorial

In this example, we position two push
buttons in the bottom-right corner
of the window.

author: Jan Bodnar
website: zetcode.com
last edited: August 2011
"""

import sys
from PySide import QtGui
from os import listdir

_main_path = "test_images/"

class Example(QtGui.QWidget):

    def __init__(self):
        super(Example, self).__init__()

        self.initUI()

    def list_files(self):
        path = '/Users/james/projects/cryptopear/test_images/'
        files = listdir(path)
        return map(lambda f: path + f, files)

    def initUI(self):

        self.paths = self.list_files()

        pixmap = QtGui.QPixmap(self.paths.pop(0)).scaledToHeight(200)
        self.lbl = QtGui.QLabel(self)
        self.lbl.setPixmap(pixmap)

        btn_accept = QtGui.QPushButton("Accept")
        btn_reject = QtGui.QPushButton("Reject")
        btn_accept.clicked.connect(self.accept_participent)
        btn_reject.clicked.connect(self.reject_participent)

        hbox = QtGui.QHBoxLayout()
        hbox.addStretch(1)
        hbox.addWidget(btn_accept)
        hbox.addWidget(btn_reject)

        chatbox = QtGui.QTextEdit(self)
        chatbox.setMinimumHeight(400)
        chatbox.setReadOnly(True)

        text_entry = QtGui.QTextEdit(self)
        btn_submit = QtGui.QPushButton("Submit")
        text_hbox = QtGui.QHBoxLayout()
        text_hbox.addWidget(text_entry)
        text_hbox.addWidget(btn_submit)

        vbox = QtGui.QVBoxLayout()
        vbox.addStretch(1)
        vbox.addWidget(self.lbl)
        vbox.addLayout(hbox)
        vbox.addWidget(chatbox)
        vbox.addLayout(text_hbox)
        self.setLayout(vbox)

        #starting x, starting y, width, height
        self.setGeometry(0, 0, 500, 700)
        self.setWindowTitle('CryptoPear')
        self.show()

    def set_picture(self, file_path):
        image = QtGui.QImage(file_path)
        if image.isNull():
            QtGui.QMessageBox.information(self, "Image Viewer",
                    "Cannot load file")
            return

        self.lbl.setPixmap(QtGui.QPixmap.fromImage(image).scaledToHeight(200))
        self.lbl.adjustSize()

    def accept_participent(self):
        self.set_picture(self.paths.pop(0))

    def reject_participent(self):
        print "participant rejected"

def main():

    app = QtGui.QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())

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