<commit_before># Copyright 2014 OpenStack Foundation
# Copyright 2014 Hewlett-Packard Development Company, L.P.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.

import argparse
import logging
import os
import sys
import threading
import webbrowser

import urwid

from gertty import db
from gertty import config
from gertty import gitrepo
from gertty import keymap
from gertty import mywid
from gertty import sync
from gertty import search
from gertty.view import change_list as view_change_list
from gertty.view import project_list as view_project_list
from gertty.view import change as view_change
import gertty.view

WELCOME_TEXT = """\
Welcome to Gertty!

To get started, you should subscribe to some projects.  Press the "L"
key (shift-L) to list all the projects, navigate to the ones you are
interested in, and then press "s" to subscribe to them.  Gertty will
automatically sync changes in your subscribed projects.

Press the F1 key anywhere to get help.  Your terminal emulator may
require you to press function-F1 or alt-F1 instead.

"""

class StatusHeader(urwid.WidgetWrap):
    def __init__(self, app):
        super(StatusHeader, self).__init__(urwid.Columns([]))
        self.app = app
        self.title = urwid.Text(u'Start')
        self.error = urwid.Text('')
        self.offline = urwid.Text('')
        self.sync = urwid.Text(u'Sync: 0')
        self._w.contents.append((self.title, ('pack', None, False)))
        self._w.contents.append((urwid.Text(u''), ('weight', 1, False)))
        self._w.contents.append((self.error, ('pack', None, False)))
        self._w.contents.append((self.offline, ('pack', None, False)))
        self._w.contents.append((self.sync, ('pack', None, False)))

    def update(self, title=None, error=False, offline=None):
        if title:
            self.title.set_text(title)
        if error:
            self.error.set_text(('error', u'Error'))
        if offline is not None:
            if offline:
                self.error.set_text(u'Offline')
            else:
                self.error.set_text(u'')
        self.sync.set_text(u' Sync: %i' % self.app.sync.queue.qsize())

class SearchDialog(mywid.ButtonDialog):
    signals = ['search', 'cancel']
    def __init__(self, app):
        self.app = app
        search_button = mywid.FixedButton('Search')
        cancel_button = mywid.FixedButton('Cancel')
        urwid.connect_signal(search_button, 'click',
                             lambda button:self._emit('search'))
        urwid.connect_signal(cancel_button, 'click',
                             lambda button:self._emit('cancel'))
        super(SearchDialog, self).__init__("Search",
                                           "Enter a change number or search string.",
                                           entry_prompt="Search: ",
                                           buttons=[search_button,
                                                    cancel_button])

    def keypress(self, size, key):
        r = super(SearchDialog, self).keypress(size, key)
        commands = self.app.config.keymap.getCommands(r)
        if keymap.ACTIVATE in commands:
            self._emit('search')
            return None
        return r

class App(object):
    def __init__(self, server=None, palette='default', keymap='default',
                 debug=False, disable_sync=False, fetch_missing_refs=False):
        self.server = server
        self.config = config.Config(server, palette, keymap)
        if debug:
            level = logging.DEBUG
        else:
            level = logging.WARNING
        logging.basicConfig(filename=self.config.log_file, filemode='w',
                            format='%(asctime)s %(message)s',
                            level=level)
        self.log = logging.getLogger('gertty.App')
        self.log.debug("Starting")

        self.fetch_missing_refs = fetch_missing_refs
        self.config.keymap.updateCommandMap()
        self.search = search.SearchCompiler(self)
        self.db = db.Database(self)
        self.sync = sync.Sync(self)

        self.screens = []
        self.status = StatusHeader(self)
        self.header = urwid.AttrMap(self.status, 'header')
        screen = view_project_list.ProjectListView(self)
        self.status.update(title=screen.title)
        self.loop = urwid.MainLoop(screen, palette=self.config.palette.getPalette(),
                                   unhandled_input=self.unhandledInput)

        has_subscribed_projects = False
        with self.db.getSession() as session:
            if session.getProjects(subscribed=True):
                has_subscribed_projects = True
        if not has_subscribed_projects:
            self.welcome()

        self.sync_pipe = self.loop.watch_pipe(self.refresh)
        self.loop.screen.tty_signal_keys(start='undefined', stop='undefined')
        #self.loop.screen.set_terminal_properties(colors=88)
        if not disable_sync:
            self.sync_thread = threading.Thread(target=self.sync.run, args=(self.sync_pipe,))
            self.sync_thread.daemon = True
            self.sync_thread.start()
        else:
            self.sync_thread = None
            self.sync.offline = True
            self.status.update(offline=True)

    def run(self):
        self.loop.run()

    def _quit(self, widget=None):
        raise urwid.ExitMainLoop()

    def quit(self):
        dialog = mywid.YesNoDialog(u'Quit',
                                   u'Are you sure you want to quit?')
        urwid.connect_signal(dialog, 'no', self.backScreen)
        urwid.connect_signal(dialog, 'yes', self._quit)

        self.popup(dialog)

    def changeScreen(self, widget, push=True):
        self.log.debug("Changing screen to %s" % (widget,))
        self.status.update(title=widget.title)
        if push:
            self.screens.append(self.loop.widget)
        self.loop.widget = widget

    def backScreen(self, target_widget=None):
        if not self.screens:
            return
        while self.screens:
            widget = self.screens.pop()
            if (not target_widget) or (widget is target_widget):
                break
        self.log.debug("Popping screen to %s" % (widget,))
        if hasattr(widget, 'title'):
            self.status.update(title=widget.title)
        self.loop.widget = widget
        self.refresh()

    def findChangeList(self):
        for widget in reversed(self.screens):
            if isinstance(widget, view_change_list.ChangeListView):
                return widget
        return None

    def clearHistory(self):
        self.log.debug("Clearing screen history")
        while self.screens:
            widget = self.screens.pop()
            self.loop.widget = widget

    def refresh(self, data=None):
        widget = self.loop.widget
        while isinstance(widget, urwid.Overlay):
            widget = widget.contents[0][0]
        widget.refresh()

    def popup(self, widget,
              relative_width=50, relative_height=25,
              min_width=20, min_height=8):
        overlay = urwid.Overlay(widget, self.loop.widget,
                                'center', ('relative', relative_width),
                                'middle', ('relative', relative_height),
                                min_width=min_width, min_height=min_height)
        self.log.debug("Overlaying %s on screen %s" % (widget, self.loop.widget))
        self.screens.append(self.loop.widget)
        self.loop.widget = overlay

    def help(self):
        if not hasattr(self.loop.widget, 'help'):
            return
        global_help = [(self.config.keymap.formatKeys(k), t)
                       for (k, t) in mywid.GLOBAL_HELP]
        for d in self.config.dashboards.values():
            global_help.append((keymap.formatKey(d['key']), d['name']))
        parts = [('Global Keys', global_help),
                 ('This Screen', self.loop.widget.help())]
        keylen = 0
        for title, items in parts:
            for keys, text in items:
                keylen = max(len(keys), keylen)
        text = ''
        for title, items in parts:
            if text:
                text += '\n'
            text += title+'\n'
            text += '%s\n' % ('='*len(title),)
            for keys, cmdtext in items:
                text += '{keys:{width}} {text}\n'.format(
                    keys=keys, width=keylen, text=cmdtext)
        dialog = mywid.MessageDialog('Help', text)
        lines = text.split('\n')
        urwid.connect_signal(dialog, 'close',
            lambda button: self.backScreen())
        self.popup(dialog, min_width=76, min_height=len(lines)+4)

    def welcome(self):
        text = WELCOME_TEXT
        dialog = mywid.MessageDialog('Welcome', text)
        lines = text.split('\n')
        urwid.connect_signal(dialog, 'close',
            lambda button: self.backScreen())
        self.popup(dialog, min_width=76, min_height=len(lines)+4)

    def _syncOneChangeFromQuery(self, query):
        number = changeid = None
        if query.startswith("change:"):
            number = query.split(':')[1].strip()
            try:
                number = int(number)
            except ValueError:
                number = None
                changeid = query.split(':')[1].strip()
        if not (number or changeid):
            return
        with self.db.getSession() as session:
            if number:
                change = session.getChangeByNumber(number)
            elif changeid:
                change = session.getChangeByChangeID(changeid)
            change_key = change and change.key or None
        if change_key is None:
            if self.sync.offline:
                raise Exception('Can not sync change while offline.')
            task = sync.SyncChangeByNumberTask(number or changeid, sync.HIGH_PRIORITY)
            self.sync.submitTask(task)
            succeeded = task.wait(300)
            if not succeeded:
                raise Exception('Unable to find change.')
            for subtask in task.tasks:
                succeeded = subtask.wait(300)
                if not succeeded:
                    raise Exception('Unable to sync change.')
            with self.db.getSession() as session:
                if number:
                    change = session.getChangeByNumber(number)
                elif changeid:
                    change = session.getChangeByChangeID(changeid)
                change_key = change and change.key or None
        if change_key is None:
            raise Exception('Change is not in local database.')

    def doSearch(self, query):
        self.log.debug("Search query: %s" % query)
        try:
            self._syncOneChangeFromQuery(query)
        except Exception as e:
            return self.error(e.message)
        with self.db.getSession() as session:
            try:
                changes = session.getChanges(query)
            except gertty.search.SearchSyntaxError as e:
                return self.error(e.message)
            change_key = None
            if len(changes) == 1:
                change_key = changes[0].key
        try:
            if change_key:
                view = view_change.ChangeView(self, change_key)
            else:
                view = view_change_list.ChangeListView(self, query)
            self.changeScreen(view)
        except gertty.view.DisplayError as e:
            return self.error(e.message)

    def searchDialog(self):
        dialog = SearchDialog(self)
        urwid.connect_signal(dialog, 'cancel',
            lambda button: self.backScreen())
        urwid.connect_signal(dialog, 'search',
            lambda button: self._searchDialog(dialog))
        self.popup(dialog, min_width=76, min_height=8)

    def _searchDialog(self, dialog):
        self.backScreen()
        query = dialog.entry.edit_text
        try:
            query = 'change:%s' % int(query)
        except ValueError:
            pass
        self.doSearch(query)

    def error(self, message):
        dialog = mywid.MessageDialog('Error', message)
        urwid.connect_signal(dialog, 'close',
                             lambda button: self.backScreen())
        self.popup(dialog, min_height=4)
        return None

    def unhandledInput(self, key):
        commands = self.config.keymap.getCommands(key)
        if keymap.PREV_SCREEN in commands:
            self.backScreen()
        elif keymap.HELP in commands:
            self.help()
        elif keymap.QUIT in commands:
            self.quit()
        elif keymap.CHANGE_SEARCH in commands:
            self.searchDialog()
        elif key in self.config.dashboards:
            d = self.config.dashboards[key]
            self.clearHistory()
            view = view_change_list.ChangeListView(self, d['query'], d['name'])
            self.changeScreen(view)

    def getRepo(self, project_name):
        local_path = os.path.join(self.config.git_root, project_name)
        local_root = os.path.abspath(self.config.git_root)
        assert os.path.commonprefix((local_root, local_path)) == local_root
        return gitrepo.Repo(self.config.url+'p/'+project_name,
                            local_path)

    def openURL(self, url):
        self.log.debug("Open URL %s" % url)
        webbrowser.open_new_tab(url)

def version():
    from gertty.version import version_info as gertty_version_info
    return "Gertty version: %s" % gertty_version_info.version_string()

def main():
    parser = argparse.ArgumentParser(
        description='Console client for Gerrit Code Review.')
    parser.add_argument('-d', dest='debug', action='store_true',
                        help='enable debug logging')
    parser.add_argument('--no-sync', dest='no_sync', action='store_true',
                        help='disable remote syncing')
    parser.add_argument('--fetch-missing-refs', dest='fetch_missing_refs',
                        action='store_true',
                        help='fetch any refs missing from local repos')
    parser.add_argument('--version', dest='version', action='version',
                        version=version(),
                        help='show zuul version')
    parser.add_argument('-p', dest='palette', default='default',
                        help='Color palette to use')
    parser.add_argument('-k', dest='keymap', default='default',
                        help='Keymap to use')
    parser.add_argument('server', nargs='?',
                        help='the server to use (as specified in config file)')
    args = parser.parse_args()
    g = App(args.server, args.palette, args.keymap, args.debug, args.no_sync,
            args.fetch_missing_refs)
    g.run()

if __name__ == '__main__':
    main()
<commit_msg>Add command line options to print palette and keymap<commit_after># Copyright 2014 OpenStack Foundation
# Copyright 2014 Hewlett-Packard Development Company, L.P.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.

import argparse
import logging
import os
import sys
import threading
import webbrowser

import urwid

from gertty import db
from gertty import config
from gertty import gitrepo
from gertty import keymap
from gertty import mywid
from gertty import palette
from gertty import sync
from gertty import search
from gertty.view import change_list as view_change_list
from gertty.view import project_list as view_project_list
from gertty.view import change as view_change
import gertty.view
import gertty.version

WELCOME_TEXT = """\
Welcome to Gertty!

To get started, you should subscribe to some projects.  Press the "L"
key (shift-L) to list all the projects, navigate to the ones you are
interested in, and then press "s" to subscribe to them.  Gertty will
automatically sync changes in your subscribed projects.

Press the F1 key anywhere to get help.  Your terminal emulator may
require you to press function-F1 or alt-F1 instead.

"""

class StatusHeader(urwid.WidgetWrap):
    def __init__(self, app):
        super(StatusHeader, self).__init__(urwid.Columns([]))
        self.app = app
        self.title = urwid.Text(u'Start')
        self.error = urwid.Text('')
        self.offline = urwid.Text('')
        self.sync = urwid.Text(u'Sync: 0')
        self._w.contents.append((self.title, ('pack', None, False)))
        self._w.contents.append((urwid.Text(u''), ('weight', 1, False)))
        self._w.contents.append((self.error, ('pack', None, False)))
        self._w.contents.append((self.offline, ('pack', None, False)))
        self._w.contents.append((self.sync, ('pack', None, False)))

    def update(self, title=None, error=False, offline=None):
        if title:
            self.title.set_text(title)
        if error:
            self.error.set_text(('error', u'Error'))
        if offline is not None:
            if offline:
                self.error.set_text(u'Offline')
            else:
                self.error.set_text(u'')
        self.sync.set_text(u' Sync: %i' % self.app.sync.queue.qsize())

class SearchDialog(mywid.ButtonDialog):
    signals = ['search', 'cancel']
    def __init__(self, app):
        self.app = app
        search_button = mywid.FixedButton('Search')
        cancel_button = mywid.FixedButton('Cancel')
        urwid.connect_signal(search_button, 'click',
                             lambda button:self._emit('search'))
        urwid.connect_signal(cancel_button, 'click',
                             lambda button:self._emit('cancel'))
        super(SearchDialog, self).__init__("Search",
                                           "Enter a change number or search string.",
                                           entry_prompt="Search: ",
                                           buttons=[search_button,
                                                    cancel_button])

    def keypress(self, size, key):
        r = super(SearchDialog, self).keypress(size, key)
        commands = self.app.config.keymap.getCommands(r)
        if keymap.ACTIVATE in commands:
            self._emit('search')
            return None
        return r

class App(object):
    def __init__(self, server=None, palette='default', keymap='default',
                 debug=False, disable_sync=False, fetch_missing_refs=False):
        self.server = server
        self.config = config.Config(server, palette, keymap)
        if debug:
            level = logging.DEBUG
        else:
            level = logging.WARNING
        logging.basicConfig(filename=self.config.log_file, filemode='w',
                            format='%(asctime)s %(message)s',
                            level=level)
        self.log = logging.getLogger('gertty.App')
        self.log.debug("Starting")

        self.fetch_missing_refs = fetch_missing_refs
        self.config.keymap.updateCommandMap()
        self.search = search.SearchCompiler(self)
        self.db = db.Database(self)
        self.sync = sync.Sync(self)

        self.screens = []
        self.status = StatusHeader(self)
        self.header = urwid.AttrMap(self.status, 'header')
        screen = view_project_list.ProjectListView(self)
        self.status.update(title=screen.title)
        self.loop = urwid.MainLoop(screen, palette=self.config.palette.getPalette(),
                                   unhandled_input=self.unhandledInput)

        has_subscribed_projects = False
        with self.db.getSession() as session:
            if session.getProjects(subscribed=True):
                has_subscribed_projects = True
        if not has_subscribed_projects:
            self.welcome()

        self.sync_pipe = self.loop.watch_pipe(self.refresh)
        self.loop.screen.tty_signal_keys(start='undefined', stop='undefined')
        #self.loop.screen.set_terminal_properties(colors=88)
        if not disable_sync:
            self.sync_thread = threading.Thread(target=self.sync.run, args=(self.sync_pipe,))
            self.sync_thread.daemon = True
            self.sync_thread.start()
        else:
            self.sync_thread = None
            self.sync.offline = True
            self.status.update(offline=True)

    def run(self):
        self.loop.run()

    def _quit(self, widget=None):
        raise urwid.ExitMainLoop()

    def quit(self):
        dialog = mywid.YesNoDialog(u'Quit',
                                   u'Are you sure you want to quit?')
        urwid.connect_signal(dialog, 'no', self.backScreen)
        urwid.connect_signal(dialog, 'yes', self._quit)

        self.popup(dialog)

    def changeScreen(self, widget, push=True):
        self.log.debug("Changing screen to %s" % (widget,))
        self.status.update(title=widget.title)
        if push:
            self.screens.append(self.loop.widget)
        self.loop.widget = widget

    def backScreen(self, target_widget=None):
        if not self.screens:
            return
        while self.screens:
            widget = self.screens.pop()
            if (not target_widget) or (widget is target_widget):
                break
        self.log.debug("Popping screen to %s" % (widget,))
        if hasattr(widget, 'title'):
            self.status.update(title=widget.title)
        self.loop.widget = widget
        self.refresh()

    def findChangeList(self):
        for widget in reversed(self.screens):
            if isinstance(widget, view_change_list.ChangeListView):
                return widget
        return None

    def clearHistory(self):
        self.log.debug("Clearing screen history")
        while self.screens:
            widget = self.screens.pop()
            self.loop.widget = widget

    def refresh(self, data=None):
        widget = self.loop.widget
        while isinstance(widget, urwid.Overlay):
            widget = widget.contents[0][0]
        widget.refresh()

    def popup(self, widget,
              relative_width=50, relative_height=25,
              min_width=20, min_height=8):
        overlay = urwid.Overlay(widget, self.loop.widget,
                                'center', ('relative', relative_width),
                                'middle', ('relative', relative_height),
                                min_width=min_width, min_height=min_height)
        self.log.debug("Overlaying %s on screen %s" % (widget, self.loop.widget))
        self.screens.append(self.loop.widget)
        self.loop.widget = overlay

    def help(self):
        if not hasattr(self.loop.widget, 'help'):
            return
        global_help = [(self.config.keymap.formatKeys(k), t)
                       for (k, t) in mywid.GLOBAL_HELP]
        for d in self.config.dashboards.values():
            global_help.append((keymap.formatKey(d['key']), d['name']))
        parts = [('Global Keys', global_help),
                 ('This Screen', self.loop.widget.help())]
        keylen = 0
        for title, items in parts:
            for keys, text in items:
                keylen = max(len(keys), keylen)
        text = ''
        for title, items in parts:
            if text:
                text += '\n'
            text += title+'\n'
            text += '%s\n' % ('='*len(title),)
            for keys, cmdtext in items:
                text += '{keys:{width}} {text}\n'.format(
                    keys=keys, width=keylen, text=cmdtext)
        dialog = mywid.MessageDialog('Help', text)
        lines = text.split('\n')
        urwid.connect_signal(dialog, 'close',
            lambda button: self.backScreen())
        self.popup(dialog, min_width=76, min_height=len(lines)+4)

    def welcome(self):
        text = WELCOME_TEXT
        dialog = mywid.MessageDialog('Welcome', text)
        lines = text.split('\n')
        urwid.connect_signal(dialog, 'close',
            lambda button: self.backScreen())
        self.popup(dialog, min_width=76, min_height=len(lines)+4)

    def _syncOneChangeFromQuery(self, query):
        number = changeid = None
        if query.startswith("change:"):
            number = query.split(':')[1].strip()
            try:
                number = int(number)
            except ValueError:
                number = None
                changeid = query.split(':')[1].strip()
        if not (number or changeid):
            return
        with self.db.getSession() as session:
            if number:
                change = session.getChangeByNumber(number)
            elif changeid:
                change = session.getChangeByChangeID(changeid)
            change_key = change and change.key or None
        if change_key is None:
            if self.sync.offline:
                raise Exception('Can not sync change while offline.')
            task = sync.SyncChangeByNumberTask(number or changeid, sync.HIGH_PRIORITY)
            self.sync.submitTask(task)
            succeeded = task.wait(300)
            if not succeeded:
                raise Exception('Unable to find change.')
            for subtask in task.tasks:
                succeeded = subtask.wait(300)
                if not succeeded:
                    raise Exception('Unable to sync change.')
            with self.db.getSession() as session:
                if number:
                    change = session.getChangeByNumber(number)
                elif changeid:
                    change = session.getChangeByChangeID(changeid)
                change_key = change and change.key or None
        if change_key is None:
            raise Exception('Change is not in local database.')

    def doSearch(self, query):
        self.log.debug("Search query: %s" % query)
        try:
            self._syncOneChangeFromQuery(query)
        except Exception as e:
            return self.error(e.message)
        with self.db.getSession() as session:
            try:
                changes = session.getChanges(query)
            except gertty.search.SearchSyntaxError as e:
                return self.error(e.message)
            change_key = None
            if len(changes) == 1:
                change_key = changes[0].key
        try:
            if change_key:
                view = view_change.ChangeView(self, change_key)
            else:
                view = view_change_list.ChangeListView(self, query)
            self.changeScreen(view)
        except gertty.view.DisplayError as e:
            return self.error(e.message)

    def searchDialog(self):
        dialog = SearchDialog(self)
        urwid.connect_signal(dialog, 'cancel',
            lambda button: self.backScreen())
        urwid.connect_signal(dialog, 'search',
            lambda button: self._searchDialog(dialog))
        self.popup(dialog, min_width=76, min_height=8)

    def _searchDialog(self, dialog):
        self.backScreen()
        query = dialog.entry.edit_text
        try:
            query = 'change:%s' % int(query)
        except ValueError:
            pass
        self.doSearch(query)

    def error(self, message):
        dialog = mywid.MessageDialog('Error', message)
        urwid.connect_signal(dialog, 'close',
                             lambda button: self.backScreen())
        self.popup(dialog, min_height=4)
        return None

    def unhandledInput(self, key):
        commands = self.config.keymap.getCommands(key)
        if keymap.PREV_SCREEN in commands:
            self.backScreen()
        elif keymap.HELP in commands:
            self.help()
        elif keymap.QUIT in commands:
            self.quit()
        elif keymap.CHANGE_SEARCH in commands:
            self.searchDialog()
        elif key in self.config.dashboards:
            d = self.config.dashboards[key]
            self.clearHistory()
            view = view_change_list.ChangeListView(self, d['query'], d['name'])
            self.changeScreen(view)

    def getRepo(self, project_name):
        local_path = os.path.join(self.config.git_root, project_name)
        local_root = os.path.abspath(self.config.git_root)
        assert os.path.commonprefix((local_root, local_path)) == local_root
        return gitrepo.Repo(self.config.url+'p/'+project_name,
                            local_path)

    def openURL(self, url):
        self.log.debug("Open URL %s" % url)
        webbrowser.open_new_tab(url)

def version():
    return "Gertty version: %s" % gertty.version.version_info.version_string()

class PrintKeymapAction(argparse.Action):
    def __call__(self, parser, namespace, values, option_string=None):
        for cmd in sorted(keymap.DEFAULT_KEYMAP.keys()):
            print cmd.replace(' ', '-')
        sys.exit(0)

class PrintPaletteAction(argparse.Action):
    def __call__(self, parser, namespace, values, option_string=None):
        for attr in sorted(palette.DEFAULT_PALETTE.keys()):
            print attr
        sys.exit(0)

def main():
    parser = argparse.ArgumentParser(
        description='Console client for Gerrit Code Review.')
    parser.add_argument('-d', dest='debug', action='store_true',
                        help='enable debug logging')
    parser.add_argument('--no-sync', dest='no_sync', action='store_true',
                        help='disable remote syncing')
    parser.add_argument('--fetch-missing-refs', dest='fetch_missing_refs',
                        action='store_true',
                        help='fetch any refs missing from local repos')
    parser.add_argument('--print-keymap', nargs=0, action=PrintKeymapAction,
                        help='print the keymap command names to stdout')
    parser.add_argument('--print-palette', nargs=0, action=PrintPaletteAction,
                        help='print the palette attribute names to stdout')
    parser.add_argument('--version', dest='version', action='version',
                        version=version(),
                        help='show zuul version')
    parser.add_argument('-p', dest='palette', default='default',
                        help='Color palette to use')
    parser.add_argument('-k', dest='keymap', default='default',
                        help='Keymap to use')
    parser.add_argument('server', nargs='?',
                        help='the server to use (as specified in config file)')
    args = parser.parse_args()
    g = App(args.server, args.palette, args.keymap, args.debug, args.no_sync,
            args.fetch_missing_refs)
    g.run()

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


<commit_before># Copyright (c) 2017, Neil Booth
#
# All rights reserved.
#
# See the file "LICENCE" for information about the copyright
# and warranty status of this software.

import asyncio
import os
import signal
import sys
import time
from functools import partial

from electrumx.lib.util import class_logger

class ServerBase(object):
    '''Base class server implementation.

    Derived classes are expected to:

    - set PYTHON_MIN_VERSION and SUPPRESS_MESSAGES as appropriate
    - implement the start_servers() coroutine, called from the run() method.
      Upon return the event loop runs until the shutdown signal is received.
    - implement the shutdown() coroutine
    '''

    SUPPRESS_MESSAGES = [
        'Fatal read error on socket transport',
        'Fatal write error on socket transport',
    ]

    PYTHON_MIN_VERSION = (3, 6)

    def __init__(self, env):
        '''Save the environment, perform basic sanity checks, and set the
        event loop policy.
        '''
        self.logger = class_logger(__name__, self.__class__.__name__)
        self.env = env

        # Sanity checks
        if sys.version_info < self.PYTHON_MIN_VERSION:
            mvs = '.'.join(str(part) for part in self.PYTHON_MIN_VERSION)
            raise RuntimeError('Python version >= {} is required'.format(mvs))

        if os.geteuid() == 0 and not env.allow_root:
            raise RuntimeError('RUNNING AS ROOT IS STRONGLY DISCOURAGED!\n'
                               'You shoud create an unprivileged user account '
                               'and use that.\n'
                               'To continue as root anyway, restart with '
                               'environment variable ALLOW_ROOT non-empty')

        # First asyncio operation must be to set the event loop policy
        # as this replaces the event loop
        asyncio.set_event_loop_policy(self.env.loop_policy)

        # Trigger this event to cleanly shutdown
        self.shutdown_event = asyncio.Event()

    async def start_servers(self):
        '''Override to perform initialization that requires the event loop,
        and start servers.'''
        pass

    async def shutdown(self):
        '''Override to perform the shutdown sequence, if any.'''
        pass

    async def _wait_for_shutdown_event(self):
        '''Wait for shutdown to be signalled, and log it.

        Derived classes may want to provide a shutdown() coroutine.'''
        # Shut down cleanly after waiting for shutdown to be signalled
        await self.shutdown_event.wait()
        self.logger.info('shutting down')

        # Wait for the shutdown sequence
        await self.shutdown()

        # Finally, work around an apparent asyncio bug that causes log
        # spew on shutdown for partially opened SSL sockets
        try:
            del asyncio.sslproto._SSLProtocolTransport.__del__
        except Exception:
            pass

        self.logger.info('shutdown complete')

    def on_signal(self, signame):
        '''Call on receipt of a signal to cleanly shutdown.'''
        self.logger.warning('received {} signal, initiating shutdown'
                            .format(signame))
        self.shutdown_event.set()

    def on_exception(self, loop, context):
        '''Suppress spurious messages it appears we cannot control.'''
        message = context.get('message')
        if message in self.SUPPRESS_MESSAGES:
            return
        if 'accept_connection2()' in repr(context.get('task')):
            return
        loop.default_exception_handler(context)

    def run(self):
        '''Run the server application:

        - record start time
        - set the event loop policy as specified by the environment
        - install SIGINT and SIGKILL handlers to trigger shutdown_event
        - set loop's exception handler to suppress unwanted messages
        - run the event loop until start_servers() completes
        - run the event loop until shutdown is signalled
        '''
        self.start_time = time.time()

        loop = asyncio.get_event_loop()

        for signame in ('SIGINT', 'SIGTERM'):
            loop.add_signal_handler(getattr(signal, signame),
                                    partial(self.on_signal, signame))
        loop.set_exception_handler(self.on_exception)

        loop.run_until_complete(self.start_servers())
        loop.run_until_complete(self._wait_for_shutdown_event())
        loop.close()
<commit_msg>Log Python interpreter version on startup<commit_after># Copyright (c) 2017, Neil Booth
#
# All rights reserved.
#
# See the file "LICENCE" for information about the copyright
# and warranty status of this software.

import asyncio
import os
import signal
import sys
import time
from functools import partial

from electrumx.lib.util import class_logger

class ServerBase(object):
    '''Base class server implementation.

    Derived classes are expected to:

    - set PYTHON_MIN_VERSION and SUPPRESS_MESSAGES as appropriate
    - implement the start_servers() coroutine, called from the run() method.
      Upon return the event loop runs until the shutdown signal is received.
    - implement the shutdown() coroutine
    '''

    SUPPRESS_MESSAGES = [
        'Fatal read error on socket transport',
        'Fatal write error on socket transport',
    ]

    PYTHON_MIN_VERSION = (3, 6)

    def __init__(self, env):
        '''Save the environment, perform basic sanity checks, and set the
        event loop policy.
        '''
        self.logger = class_logger(__name__, self.__class__.__name__)
        self.logger.info(f'Python version: {sys.version}')
        self.env = env

        # Sanity checks
        if sys.version_info < self.PYTHON_MIN_VERSION:
            mvs = '.'.join(str(part) for part in self.PYTHON_MIN_VERSION)
            raise RuntimeError('Python version >= {} is required'.format(mvs))

        if os.geteuid() == 0 and not env.allow_root:
            raise RuntimeError('RUNNING AS ROOT IS STRONGLY DISCOURAGED!\n'
                               'You shoud create an unprivileged user account '
                               'and use that.\n'
                               'To continue as root anyway, restart with '
                               'environment variable ALLOW_ROOT non-empty')

        # First asyncio operation must be to set the event loop policy
        # as this replaces the event loop
        asyncio.set_event_loop_policy(self.env.loop_policy)

        # Trigger this event to cleanly shutdown
        self.shutdown_event = asyncio.Event()

    async def start_servers(self):
        '''Override to perform initialization that requires the event loop,
        and start servers.'''
        pass

    async def shutdown(self):
        '''Override to perform the shutdown sequence, if any.'''
        pass

    async def _wait_for_shutdown_event(self):
        '''Wait for shutdown to be signalled, and log it.

        Derived classes may want to provide a shutdown() coroutine.'''
        # Shut down cleanly after waiting for shutdown to be signalled
        await self.shutdown_event.wait()
        self.logger.info('shutting down')

        # Wait for the shutdown sequence
        await self.shutdown()

        # Finally, work around an apparent asyncio bug that causes log
        # spew on shutdown for partially opened SSL sockets
        try:
            del asyncio.sslproto._SSLProtocolTransport.__del__
        except Exception:
            pass

        self.logger.info('shutdown complete')

    def on_signal(self, signame):
        '''Call on receipt of a signal to cleanly shutdown.'''
        self.logger.warning('received {} signal, initiating shutdown'
                            .format(signame))
        self.shutdown_event.set()

    def on_exception(self, loop, context):
        '''Suppress spurious messages it appears we cannot control.'''
        message = context.get('message')
        if message in self.SUPPRESS_MESSAGES:
            return
        if 'accept_connection2()' in repr(context.get('task')):
            return
        loop.default_exception_handler(context)

    def run(self):
        '''Run the server application:

        - record start time
        - set the event loop policy as specified by the environment
        - install SIGINT and SIGKILL handlers to trigger shutdown_event
        - set loop's exception handler to suppress unwanted messages
        - run the event loop until start_servers() completes
        - run the event loop until shutdown is signalled
        '''
        self.start_time = time.time()

        loop = asyncio.get_event_loop()

        for signame in ('SIGINT', 'SIGTERM'):
            loop.add_signal_handler(getattr(signal, signame),
                                    partial(self.on_signal, signame))
        loop.set_exception_handler(self.on_exception)

        loop.run_until_complete(self.start_servers())
        loop.run_until_complete(self._wait_for_shutdown_event())
        loop.close()
<|endoftext|>


<commit_before>""" Nike plus activity log
https://developer.nike.com

Output:
-- May --
Sun 05/26/13 : 5000 points 5.59 miles 1:02:20 (11'10/mi)
Fri 05/24/13 : 2000 points 4.01 miles 0:37:40 (9'24/mi)
Wed 05/22/13 : 3000 points 6.17 miles 1:01:12 (9'55/mi)
"""

from collections import namedtuple
import json
import urllib
import urllib2
import time

# Insert token here
ACCESS_TOKEN = ''

# FIXME: Could use descriptors here:
#   - Could hold: default value, api name, pretty name, and conversion func

# Key = our internal name
# Value = nike plus API name (None for custom, not represented in API)
name_to_api = {'calories': 'calories',
               'fuel': 'fuel',
               'distance': 'distance',
               'steps': 'steps',
               'start_time': 'startTime',
               'device': 'deviceType',
               'miles': None,
               'duration': 'duration',
               'pace': None}

NikePlusActivity = namedtuple('NikePlusActivity', name_to_api.keys())

km_to_mi = lambda distance: distance * 0.621371

def calculate_mile_pace(duration, miles):
    pace = ''
    sp = duration.split(':')
    if (len(sp) == 3):
        duration_seconds = int(sp[0]) * 60 * 60 + int(sp[1]) * 60 + int(sp[2])
        seconds_per_mile = duration_seconds / miles
        hours, remainder = divmod(seconds_per_mile, 3600)
        minutes, seconds = divmod(remainder, 60)
        pace = '(%.0f\'%02.0f/mi)' % (minutes, seconds)

    return pace

def decode_activity(activity):
    metrics = activity.get('metricSummary')

    api_values = {}
    for pretty_name, api_name in name_to_api.iteritems():
        if api_name is not None:
            # Values can be in 1 of 2 dicts, metric sub-dict or 'root' activity
            # dict
            try:
                api_values[pretty_name] = metrics[api_name]
            except KeyError:
                api_values[pretty_name] = activity.get(api_name, None)

    # Custom values/sanitizing

    # 2013-05-26T14:48:42Z
    api_values['start_time'] = time.strptime(api_values['start_time'],
                                            '%Y-%m-%dT%H:%M:%SZ')
    api_values['start_time'] = time.strftime('%a %m/%d/%y',
                                             api_values['start_time'])

    # remove milliseconds
    api_values['duration'] = api_values['duration'].partition('.')[0]

    api_values['miles'] = km_to_mi(api_values['distance'])
    api_values['distance'] = '%.2f' % round(api_values['miles'], 2)
    api_values['pace'] = calculate_mile_pace(api_values['duration'],
                                             api_values['miles'])

    activity = NikePlusActivity(**api_values)

    return activity

def activities():
    base_url = 'https://api.nike.com'
    url = '/me/sport/activities?access_token=%s' % ACCESS_TOKEN

    # weird required headers, blah.
    headers = {'appid':'fuelband', 'Accept':'application/json'}
    current_month = None

    while url:
        req = urllib2.Request('%s%s' % (base_url, url), None, headers)
        r = urllib2.urlopen(req)
        resp = json.loads(r.read())
        r.close()

        data = resp.get('data')
        if data is None:
            raise StopIteration

        for item in resp.get('data'):
            activity = decode_activity(item)
            yield activity

        # pagination
        url = None
        if resp.get('paging') and resp.get('paging').get('next'):
            url = resp.get('paging').get('next')

if __name__ == '__main__':
    for activity in activities():
        print activity
<commit_msg>Print to csv with header, add -q argument<commit_after>""" Nike plus activity log
https://developer.nike.com

Output:
-- May --
Sun 05/26/13 : 5000 points 5.59 miles 1:02:20 (11'10/mi)
Fri 05/24/13 : 2000 points 4.01 miles 0:37:40 (9'24/mi)
Wed 05/22/13 : 3000 points 6.17 miles 1:01:12 (9'55/mi)
"""

from collections import namedtuple
import json
import urllib
import urllib2
import time

# Insert token here
ACCESS_TOKEN = ''

# FIXME: Could use descriptors here:
#   - Could hold: default value, api name, pretty name, and conversion func

# Key = our internal name
# Value = nike plus API name (None for custom, not represented in API)
name_to_api = {'calories': 'calories',
               'fuel': 'fuel',
               'distance': 'distance',
               'steps': 'steps',
               'start_time': 'startTime',
               'device': 'deviceType',
               'miles': None,
               'duration': 'duration',
               'pace': None}

NikePlusActivity = namedtuple('NikePlusActivity', name_to_api.keys())

km_to_mi = lambda distance: distance * 0.621371

def calculate_mile_pace(duration, miles):
    pace = ''
    sp = duration.split(':')
    if (len(sp) == 3):
        duration_seconds = int(sp[0]) * 60 * 60 + int(sp[1]) * 60 + int(sp[2])
        seconds_per_mile = duration_seconds / miles
        hours, remainder = divmod(seconds_per_mile, 3600)
        minutes, seconds = divmod(remainder, 60)
        pace = '(%.0f\'%02.0f/mi)' % (minutes, seconds)

    return pace

def decode_activity(activity):
    metrics = activity.get('metricSummary')

    api_values = {}
    for pretty_name, api_name in name_to_api.iteritems():
        if api_name is not None:
            # Values can be in 1 of 2 dicts, metric sub-dict or 'root' activity
            # dict
            try:
                api_values[pretty_name] = metrics[api_name]
            except KeyError:
                api_values[pretty_name] = activity.get(api_name, None)

    # Custom values/sanitizing

    # 2013-05-26T14:48:42Z
    api_values['start_time'] = time.strptime(api_values['start_time'],
                                            '%Y-%m-%dT%H:%M:%SZ')
    api_values['start_time'] = time.strftime('%a %m/%d/%y',
                                             api_values['start_time'])

    # remove milliseconds
    api_values['duration'] = api_values['duration'].partition('.')[0]

    api_values['miles'] = km_to_mi(api_values['distance'])
    api_values['distance'] = '%.2f' % round(api_values['miles'], 2)
    api_values['pace'] = calculate_mile_pace(api_values['duration'],
                                             api_values['miles'])

    activity = NikePlusActivity(**api_values)

    return activity

def get_activities():
    base_url = 'https://api.nike.com'
    url = '/me/sport/activities?access_token=%s' % ACCESS_TOKEN

    # weird required headers, blah.
    headers = {'appid':'fuelband', 'Accept':'application/json'}
    current_month = None

    while url:
        req = urllib2.Request('%s%s' % (base_url, url), None, headers)
        r = urllib2.urlopen(req)
        resp = json.loads(r.read())
        r.close()

        data = resp.get('data')
        if data is None:
            raise StopIteration

        for item in resp.get('data'):
            activity = decode_activity(item)
            yield activity

        # pagination
        url = None
        if resp.get('paging') and resp.get('paging').get('next'):
            url = resp.get('paging').get('next')

if __name__ == '__main__':
    # FIXME: Add help, real argparse

    import sys
    quiet = '-q' in sys.argv[1:]

    activities = get_activities()

    # Print header
    activity = activities.next()
    print ','.join(activity._fields)

    for activity in activities:
        if not quiet:
            print activity
<|endoftext|>


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

"""Module for the GSoC project student survey.
"""

__authors__ = [
  '"Madhusudan.C.S" <madhusudancs@gmail.com>',
  ]

from soc.views import forms

from django.utils.translation import ugettext

from soc.views.helper.access_checker import isSet
from soc.views.readonly_template import SurveyRecordReadOnlyTemplate

from soc.modules.gsoc.models.project_survey import ProjectSurvey
from soc.modules.gsoc.models.project_survey_record import \
    GSoCProjectSurveyRecord
from soc.modules.gsoc.views.base import RequestHandler
from soc.modules.gsoc.views.base_templates import LoggedInMsg
from soc.modules.gsoc.views.helper import url_patterns

class GSoCStudentEvaluationEditForm(forms.SurveyEditForm):
  """Form to create/edit GSoC project survey for students.
  """

  class Meta:
    model = ProjectSurvey
    css_prefix = 'gsoc-student-eval-edit'
    exclude = ['scope', 'author', 'modified_by', 'survey_content',
               'scope_path', 'link_id', 'prefix', 'read_access',
               'write_access', 'taking_access', 'is_featured']

class GSoCStudentEvaluationTakeForm(forms.SurveyTakeForm):
  """Form for students to respond to the survey during evaluations.
  """

  class Meta:
    model = GSoCProjectSurveyRecord
    css_prefix = 'gsoc-student-eval-record'
    exclude = ['project', 'org', 'user', 'survey', 'created', 'modified']

class GSoCStudentEvaluationEditPage(RequestHandler):
  """View for creating/editing student evalution.
  """

  def djangoURLPatterns(self):
    return [
         url_patterns.url(r'eval/student/edit/%s$' % url_patterns.SURVEY,
             self, name='gsoc_edit_student_evaluation'),
    ]

  def checkAccess(self):
    self.check.isHost()
    self.mutator.studentEvaluationFromKwargs(raise_not_found=False)

  def templatePath(self):
    return 'v2/modules/gsoc/_evaluation.html'

  def context(self):
    if self.data.student_evaluation:
      form = GSoCStudentEvaluationEditForm(
          self.data.POST or None, instance=self.data.student_evaluation)
    else:
      form = GSoCStudentEvaluationEditForm(self.data.POST or None)

    page_name = ugettext('Edit - %s' % (self.data.student_evaluation.title)) \
        if self.data.student_evaluation else 'Create new student evaluation'
    context = {
        'page_name': page_name,
        'post_url': self.redirect.survey().urlOf(
            'gsoc_edit_student_evaluation'),
        'forms': [form],
        'error': bool(form.errors),
        }

    return context

  def evaluationFromForm(self):
    """Create/edit the student evaluation entity from form.

    Returns:
      a newly created or updated student evaluation entity or None.
    """
    if self.data.student_evaluation:
      form = GSoCStudentEvaluationEditForm(
          self.data.POST, instance=self.data.student_evaluation)
    else:
      form = GSoCStudentEvaluationEditForm(self.data.POST)

    if not form.is_valid():
      return None

    form.cleaned_data['modified_by'] = self.data.user

    if not self.data.student_evaluation:
      form.cleaned_data['link_id'] = self.data.kwargs.get('survey')
      form.cleaned_data['prefix'] = 'gsoc_program'
      form.cleaned_data['author'] = self.data.user
      form.cleaned_data['scope'] = self.data.program
      # kwargs which defines an evaluation
      fields = ['sponsor', 'program', 'survey']

      key_name = '/'.join(['gsoc_program'] +
                          [self.data.kwargs[field] for field in fields])

      entity = form.create(commit=True, key_name=key_name)
    else:
      entity = form.save(commit=True)

    return entity

  def post(self):
    evaluation = self.evaluationFromForm()
    if evaluation:
      r = self.redirect.survey()
      r.to('gsoc_edit_student_evaluation', validated=True)
    else:
      self.get()

class GSoCStudentEvaluationTakePage(RequestHandler):
  """View for students to submit their evaluation.
  """

  def djangoURLPatterns(self):
    return [
         url_patterns.url(r'eval/student/%s$' % url_patterns.SURVEY_RECORD,
             self, name='gsoc_take_student_evaluation'),
    ]

  def checkAccess(self):
    self.mutator.projectFromKwargs()
    self.mutator.studentEvaluationFromKwargs()
    self.mutator.studentEvaluationRecordFromKwargs()

    assert isSet(self.data.student_evaluation)
    self.check.isSurveyActive(self.data.student_evaluation)
    self.check.canUserTakeSurvey(self.data.student_evaluation)
    self.check.isStudentForSurvey()

  def templatePath(self):
    return 'v2/modules/gsoc/_evaluation_take.html'

  def context(self):
    if self.data.student_evaluation_record:
      form = GSoCStudentEvaluationTakeForm(
          self.data.student_evaluation,
          self.data.POST or None, instance=self.data.student_evaluation_record)
    else:
      form = GSoCStudentEvaluationTakeForm(
          self.data.student_evaluation, self.data.POST or None)

    context = {
        'page_name': '%s page' % (self.data.student_evaluation.title),
        'form_top_msg': LoggedInMsg(self.data, apply_link=False),
        'forms': [form],
        'error': bool(form.errors),
        }

    return context

  def recordEvaluationFromForm(self):
    """Create/edit a new student evaluation record based on the form input.

    Returns:
      a newly created or updated evaluation record entity or None
    """
    if self.data.student_evaluation_record:
      form = GSoCStudentEvaluationTakeForm(
          self.data.student_evaluation,
          self.data.POST, instance=self.data.student_evaluation_record)
    else:
      form = GSoCStudentEvaluationTakeForm(
          self.data.student_evaluation, self.data.POST)

    if not form.is_valid():
      return None

    if not self.data.student_evaluation_record:
      form.cleaned_data['project'] = self.data.project
      form.cleaned_data['org'] = self.data.project.org
      form.cleaned_data['user'] = self.data.user
      form.cleaned_data['survey'] = self.data.student_evaluation
      entity = form.create(commit=True)
    else:
      entity = form.save(commit=True)

    return entity

  def post(self):
    student_evaluation_record = self.recordEvaluationFromForm()
    if student_evaluation_record:
      r = self.redirect.survey_record(
          self.data.student_evaluation.link_id)
      r.to('gsoc_take_student_evaluation', validated=True)
    else:
      self.get()

class GSoCStudentEvaluationReadOnlyTemplate(SurveyRecordReadOnlyTemplate):
  """Template to construct readonly student evaluation record.
  """

  class Meta:
    model = GSoCProjectSurveyRecord
    css_prefix = 'gsoc-student-eval-show'

class GSoCStudentEvaluationShowPage(RequestHandler):
  """View to display the readonly page for student evaluation.
  """

  def djangoURLPatterns(self):
    return [
        url_patterns.url(r'eval/student/show/%s$' % url_patterns.SURVEY_RECORD,
            self, name='gsoc_show_student_evaluation'),
    ]

  def checkAccess(self):
    self.mutator.projectFromKwargs()
    self.mutator.studentEvaluationFromKwargs()
    self.mutator.studentEvaluationRecordFromKwargs()

    assert isSet(self.data.project)
    assert isSet(self.data.student_evaluation)

    self.check.isProfileActive()
    if self.data.orgAdminFor(self.data.project.org):
      self.data.role = 'org_admin'
      return

    self.check.isStudentForSurvey()
    self.data.role = 'student'

  def templatePath(self):
    return 'v2/modules/gsoc/_survey/show.html'

  def context(self):
    assert isSet(self.data.student_evaluation_record)

    record = self.data.student_evaluation_record
    student = self.data.url_profile

    context = {
        'page_name': 'Student evaluation - %s' % (student.name()),
        'student': student.name(),
        'organization': self.data.project.org.name,
        'project': self.data.project.title,
        'top_msg': LoggedInMsg(self.data, apply_link=False),
        'css_prefix': GSoCStudentEvaluationReadOnlyTemplate.Meta.css_prefix,
        }

    if record:
      context['record'] = GSoCStudentEvaluationReadOnlyTemplate(record)

    if self.data.role == 'student':
      context['update_link'] = self.data.redirect.survey_record(
          'midterm').urlOf('gsoc_take_student_evaluation')
    return context
<commit_msg>Do not show update link to the evaluation if the survey deadline has passed.<commit_after>#!/usr/bin/env python2.5
#
# Copyright 2011 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Module for the GSoC project student survey.
"""

__authors__ = [
  '"Madhusudan.C.S" <madhusudancs@gmail.com>',
  ]

from soc.views import forms

from django.utils.translation import ugettext

from soc.views.helper.access_checker import isSet
from soc.views.readonly_template import SurveyRecordReadOnlyTemplate

from soc.modules.gsoc.models.project_survey import ProjectSurvey
from soc.modules.gsoc.models.project_survey_record import \
    GSoCProjectSurveyRecord
from soc.modules.gsoc.views.base import RequestHandler
from soc.modules.gsoc.views.base_templates import LoggedInMsg
from soc.modules.gsoc.views.helper import url_patterns

class GSoCStudentEvaluationEditForm(forms.SurveyEditForm):
  """Form to create/edit GSoC project survey for students.
  """

  class Meta:
    model = ProjectSurvey
    css_prefix = 'gsoc-student-eval-edit'
    exclude = ['scope', 'author', 'modified_by', 'survey_content',
               'scope_path', 'link_id', 'prefix', 'read_access',
               'write_access', 'taking_access', 'is_featured']

class GSoCStudentEvaluationTakeForm(forms.SurveyTakeForm):
  """Form for students to respond to the survey during evaluations.
  """

  class Meta:
    model = GSoCProjectSurveyRecord
    css_prefix = 'gsoc-student-eval-record'
    exclude = ['project', 'org', 'user', 'survey', 'created', 'modified']

class GSoCStudentEvaluationEditPage(RequestHandler):
  """View for creating/editing student evalution.
  """

  def djangoURLPatterns(self):
    return [
         url_patterns.url(r'eval/student/edit/%s$' % url_patterns.SURVEY,
             self, name='gsoc_edit_student_evaluation'),
    ]

  def checkAccess(self):
    self.check.isHost()
    self.mutator.studentEvaluationFromKwargs(raise_not_found=False)

  def templatePath(self):
    return 'v2/modules/gsoc/_evaluation.html'

  def context(self):
    if self.data.student_evaluation:
      form = GSoCStudentEvaluationEditForm(
          self.data.POST or None, instance=self.data.student_evaluation)
    else:
      form = GSoCStudentEvaluationEditForm(self.data.POST or None)

    page_name = ugettext('Edit - %s' % (self.data.student_evaluation.title)) \
        if self.data.student_evaluation else 'Create new student evaluation'
    context = {
        'page_name': page_name,
        'post_url': self.redirect.survey().urlOf(
            'gsoc_edit_student_evaluation'),
        'forms': [form],
        'error': bool(form.errors),
        }

    return context

  def evaluationFromForm(self):
    """Create/edit the student evaluation entity from form.

    Returns:
      a newly created or updated student evaluation entity or None.
    """
    if self.data.student_evaluation:
      form = GSoCStudentEvaluationEditForm(
          self.data.POST, instance=self.data.student_evaluation)
    else:
      form = GSoCStudentEvaluationEditForm(self.data.POST)

    if not form.is_valid():
      return None

    form.cleaned_data['modified_by'] = self.data.user

    if not self.data.student_evaluation:
      form.cleaned_data['link_id'] = self.data.kwargs.get('survey')
      form.cleaned_data['prefix'] = 'gsoc_program'
      form.cleaned_data['author'] = self.data.user
      form.cleaned_data['scope'] = self.data.program
      # kwargs which defines an evaluation
      fields = ['sponsor', 'program', 'survey']

      key_name = '/'.join(['gsoc_program'] +
                          [self.data.kwargs[field] for field in fields])

      entity = form.create(commit=True, key_name=key_name)
    else:
      entity = form.save(commit=True)

    return entity

  def post(self):
    evaluation = self.evaluationFromForm()
    if evaluation:
      r = self.redirect.survey()
      r.to('gsoc_edit_student_evaluation', validated=True)
    else:
      self.get()

class GSoCStudentEvaluationTakePage(RequestHandler):
  """View for students to submit their evaluation.
  """

  def djangoURLPatterns(self):
    return [
         url_patterns.url(r'eval/student/%s$' % url_patterns.SURVEY_RECORD,
             self, name='gsoc_take_student_evaluation'),
    ]

  def checkAccess(self):
    self.mutator.projectFromKwargs()
    self.mutator.studentEvaluationFromKwargs()
    self.mutator.studentEvaluationRecordFromKwargs()

    assert isSet(self.data.student_evaluation)
    self.check.isSurveyActive(self.data.student_evaluation)
    self.check.canUserTakeSurvey(self.data.student_evaluation)
    self.check.isStudentForSurvey()

  def templatePath(self):
    return 'v2/modules/gsoc/_evaluation_take.html'

  def context(self):
    if self.data.student_evaluation_record:
      form = GSoCStudentEvaluationTakeForm(
          self.data.student_evaluation,
          self.data.POST or None, instance=self.data.student_evaluation_record)
    else:
      form = GSoCStudentEvaluationTakeForm(
          self.data.student_evaluation, self.data.POST or None)

    context = {
        'page_name': '%s page' % (self.data.student_evaluation.title),
        'form_top_msg': LoggedInMsg(self.data, apply_link=False),
        'forms': [form],
        'error': bool(form.errors),
        }

    return context

  def recordEvaluationFromForm(self):
    """Create/edit a new student evaluation record based on the form input.

    Returns:
      a newly created or updated evaluation record entity or None
    """
    if self.data.student_evaluation_record:
      form = GSoCStudentEvaluationTakeForm(
          self.data.student_evaluation,
          self.data.POST, instance=self.data.student_evaluation_record)
    else:
      form = GSoCStudentEvaluationTakeForm(
          self.data.student_evaluation, self.data.POST)

    if not form.is_valid():
      return None

    if not self.data.student_evaluation_record:
      form.cleaned_data['project'] = self.data.project
      form.cleaned_data['org'] = self.data.project.org
      form.cleaned_data['user'] = self.data.user
      form.cleaned_data['survey'] = self.data.student_evaluation
      entity = form.create(commit=True)
    else:
      entity = form.save(commit=True)

    return entity

  def post(self):
    student_evaluation_record = self.recordEvaluationFromForm()
    if student_evaluation_record:
      r = self.redirect.survey_record(
          self.data.student_evaluation.link_id)
      r.to('gsoc_take_student_evaluation', validated=True)
    else:
      self.get()

class GSoCStudentEvaluationReadOnlyTemplate(SurveyRecordReadOnlyTemplate):
  """Template to construct readonly student evaluation record.
  """

  class Meta:
    model = GSoCProjectSurveyRecord
    css_prefix = 'gsoc-student-eval-show'

class GSoCStudentEvaluationShowPage(RequestHandler):
  """View to display the readonly page for student evaluation.
  """

  def djangoURLPatterns(self):
    return [
        url_patterns.url(r'eval/student/show/%s$' % url_patterns.SURVEY_RECORD,
            self, name='gsoc_show_student_evaluation'),
    ]

  def checkAccess(self):
    self.mutator.projectFromKwargs()
    self.mutator.studentEvaluationFromKwargs()
    self.mutator.studentEvaluationRecordFromKwargs()

    assert isSet(self.data.project)
    assert isSet(self.data.student_evaluation)

    self.check.isProfileActive()
    if self.data.orgAdminFor(self.data.project.org):
      self.data.role = 'org_admin'
      return

    self.check.isStudentForSurvey()
    self.data.role = 'student'

  def templatePath(self):
    return 'v2/modules/gsoc/_survey/show.html'

  def context(self):
    assert isSet(self.data.program)
    assert isSet(self.data.timeline)
    assert isSet(self.data.student_evaluation_record)

    record = self.data.student_evaluation_record
    student = self.data.url_profile

    context = {
        'page_name': 'Student evaluation - %s' % (student.name()),
        'student': student.name(),
        'organization': self.data.project.org.name,
        'project': self.data.project.title,
        'top_msg': LoggedInMsg(self.data, apply_link=False),
        'css_prefix': GSoCStudentEvaluationReadOnlyTemplate.Meta.css_prefix,
        }

    if record:
      context['record'] = GSoCStudentEvaluationReadOnlyTemplate(record)

    if (self.data.timeline.surveyPeriod(self.data.student_evaluation) and
        self.data.role == 'student'):
      context['update_link'] = self.data.redirect.survey_record(
          self.data.student_evaluation.link_id).urlOf(
          'gsoc_take_student_evaluation')
    return context
<|endoftext|>


<commit_before>#!/usr/bin/env python3
#******************************************************************************
# (C) 2018, Stefan Korner, Austria                                            *
#                                                                             *
# The Space Python Library is free software; you can redistribute it and/or   *
# modify it under under the terms of the MIT License as published by the      *
# Massachusetts Institute of Technology.                                      *
#                                                                             *
# The Space Python Library is distributed in the hope that it will be useful, *
# but WITHOUT ANY WARRANTY; without even the implied warranty of              *
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MIT License    *
# for more details.                                                           *
#******************************************************************************
# Space Segment - Unit Tests                                                  *
#******************************************************************************
import testData
import CCSDS.PACKET
import SPACE.DEF, SPACE.TMGEN
import UTIL.SYS

#############
# constants #
#############
SYS_CONFIGURATION = [
  ["HOST", "127.0.0.1"],
  ["NCTRS_ADMIN_SERVER_PORT", "13006"],
  ["NCTRS_TC_SERVER_PORT", "13007"],
  ["NCTRS_TM_SERVER_PORT", "2502"],
  ["SPACECRAFT_ID", "758"],
  ["DEF_GROUND_STATION_ID", "10"],
  ["GROUND_STATION_NAME", "ESA G/S "],
  ["TC_ACK_ACCEPT_SUCC_MNEMO", "ACK1"],
  ["TC_ACK_ACCEPT_FAIL_MNEMO", "NAK1"],
  ["TC_ACK_EXESTA_SUCC_MNEMO", "ACK2"],
  ["TC_ACK_EXESTA_FAIL_MNEMO", "NAK2"],
  ["TC_ACK_EXEPRO_SUCC_MNEMO", "ACK3"],
  ["TC_ACK_EXEPRO_FAIL_MNEMO", "NAK3"],
  ["TC_ACK_EXECUT_SUCC_MNEMO", "ACK4"],
  ["TC_ACK_EXECUT_FAIL_MNEMO", "NAK4"],
  ["TC_ACK_APID_PARAM_BYTE_OFFSET", "18"],
  ["TC_ACK_SSC_PARAM_BYTE_OFFSET", "20"],
  ["TC_TT_TIME_FORMAT", "CUC4"],
  ["TC_TT_TIME_BYTE_OFFSET", "11"],
  ["TC_TT_PKT_BYTE_OFFSET", "17"],
  ["TM_CYCLIC_MNEMO", "TM_PKT1"],
  ["TM_CYCLIC_PERIOD_MS", "5000"],
  ["TM_TT_TIME_FORMAT", "CUC4"],
  ["TM_TT_TIME_BYTE_OFFSET", "10"],
  ["TM_RECORD_FORMAT", "NCTRS"],
  ["TM_REPLAY_KEY", "SPID"],
  ["OBT_MISSION_EPOCH_STR", UTIL.TCO.UNIX_MISSION_EPOCH_STR],
  ["OBT_LEAP_SECONDS", "0"],
  ["ERT_MISSION_EPOCH_STR", UTIL.TCO.UNIX_MISSION_EPOCH_STR],
  ["ERT_LEAP_SECONDS", "0"],
  ["SYS_COLOR_LOG", "1"],
  ["SYS_APP_MNEMO", "SIM"],
  ["SYS_APP_NAME", "Simulator"],
  ["SYS_APP_VERSION", "1.0"]]

#############
# functions #
#############
# -----------------------------------------------------------------------------
def test_DEFoperations():
  """function to test the DEF definition module"""
  SPACE.DEF.init()
  print("----- TM packet definitions -----")
  tmPktDefs = SPACE.IF.s_definitions.getTMpktDefs()
  for tmPktDef in tmPktDefs:
    print(tmPktDef.pktSPID, " ", end='')
  print("")
  print("----- TM parameter definitions -----")
  tmParamDefs = SPACE.IF.s_definitions.getTMparamDefs()
  for tmParamDef in tmParamDefs:
    print(tmParamDef.paramName, " ", end='')
  print("")
  # force a re-creation of the persistent definitions
  # (for the next test run)
  SPACE.IF.s_definitions.createDefinitions()
  return True
# -----------------------------------------------------------------------------
def test_TMGENoperations():
  """function to test the TMGEN telemetry generator"""
  SPACE.TMGEN.init()
  # create TM packet without TM parameters
  tmPacket = SPACE.IF.s_tmPacketGenerator.getTMpacket(
    spid=testData.TM_PACKET_03_SPID,
    parameterValues=[],
    dataField=None,
    segmentationFlags=CCSDS.PACKET.UNSEGMENTED,
    obtUTC=0.0,
    reuse=True)
  print("tmPacket =", tmPacket)
  if UTIL.DU.array2str(tmPacket.getBufferString()) != "\n" + \
    "0000 0C D2 C0 00 00 26 10 03 19 00 00 00 00 00 00 00 .....&..........\n" + \
    "0010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................\n" + \
    "0020 00 00 00 00 00 00 00 00 00 00 00 A9 07          .............":
    print("unexpected TM packet encoding")
    return False
  if tmPacket.versionNumber != testData.TM_PACKET_03_versionNumber:
    print("tmPacket versionNumber wrong:", tmPacket.versionNumber, "- should be", testData.TM_PACKET_03_versionNumber)
    return False
  if tmPacket.packetType != testData.TM_PACKET_03_packetType:
    print("tmPacket packetType wrong:", tmPacket.packetType, "- should be", testData.TM_PACKET_03_packetType)
    return False
  if tmPacket.dataFieldHeaderFlag != testData.TM_PACKET_03_dataFieldHeaderFlag:
    print("tmPacket dataFieldHeaderFlag wrong:", tmPacket.dataFieldHeaderFlag, "- should be", testData.TM_PACKET_03_dataFieldHeaderFlag)
    return False
  if tmPacket.applicationProcessId != testData.TM_PACKET_03_applicationProcessId:
    print("tmPacket applicationProcessId wrong:", tmPacket.applicationProcessId, "- should be", testData.TM_PACKET_03_applicationProcessId)
    return False
  if tmPacket.segmentationFlags != testData.TM_PACKET_03_segmentationFlags:
    print("tmPacket segmentationFlags wrong:", tmPacket.segmentationFlags, "- should be", testData.TM_PACKET_03_segmentationFlags)
    return False
  if tmPacket.sequenceControlCount != testData.TM_PACKET_03_sequenceControlCount:
    print("tmPacket sequenceControlCount wrong:", tmPacket.sequenceControlCount, "- should be", testData.TM_PACKET_03_sequenceControlCount)
    return False
  if tmPacket.packetLength != testData.TM_PACKET_03_packetLength:
    print("tmPacket packetLength wrong:", tmPacket.packetLength, "- should be", testData.TM_PACKET_03_packetLength)
    return False
  if tmPacket.pusVersionNumber != testData.TM_PACKET_03_pusVersionNumber:
    print("tmPacket pusVersionNumber wrong:", tmPacket.pusVersionNumber, "- should be", testData.TM_PACKET_03_pusVersionNumber)
    return False
  if tmPacket.serviceType != testData.TM_PACKET_03_serviceType:
    print("tmPacket serviceType wrong:", tmPacket.serviceType, "- should be", testData.TM_PACKET_03_serviceType)
    return False
  if tmPacket.serviceSubType != testData.TM_PACKET_03_serviceSubType:
    print("tmPacket serviceSubType wrong:", tmPacket.serviceSubType, "- should be", testData.TM_PACKET_03_serviceSubType)
    return False
  if not tmPacket.checkPacketLength():
    print("tmPacket has inconsistent packetLength")
    return False
  if not tmPacket.checkChecksum():
    print("tmPacket has invalid checksum")
    return False
  # create TM packet with initialized TM parameters
  parameterValuesList = []
  parameterValuesList.append(["PAR1", 1])
  parameterValuesList.append(["PAR3", 1])
  parameterValuesList.append(["PAR5", 1])
  parameterValuesList.append(["PAR7", 1])
  parameterValuesList.append(["PAR9", 0x12345678])
  parameterValuesList.append(["PAR11", 10.0])
  parameterValuesList.append(["PAR12", 10.0])
  tmPacket = SPACE.IF.s_tmPacketGenerator.getTMpacket(
    spid=testData.TM_PACKET_03_SPID,
    parameterValues=parameterValuesList,
    dataField=None,
    segmentationFlags=CCSDS.PACKET.UNSEGMENTED,
    obtUTC=0.0,
    reuse=True)
  print("tmPacket =", tmPacket)
  if UTIL.DU.array2str(tmPacket.getBufferString()) != "\n" + \
    "0000 0C D2 C0 01 00 26 10 03 19 00 00 00 00 00 00 00 .....&..........\n" + \
    "0010 00 00 AA 12 34 56 78 00 00 00 00 00 00 00 00 41 ....4Vx........A\n" + \
    "0020 20 00 00 40 24 00 00 00 00 00 00 BC 77           ..@$.......w":
    print("unexpected TM packet encoding")
    return False
  return True

########
# main #
########
if __name__ == "__main__":
  # initialise the system configuration
  UTIL.SYS.s_configuration.setDefaults(SYS_CONFIGURATION)
  print("***** test_DEFoperations() start")
  retVal = test_DEFoperations()
  print("***** test_DEFoperations() done:", retVal)
  print("***** test_TMGENoperations() start")
  retVal = test_TMGENoperations()
  print("***** test_TMGENoperations() done:", retVal)
<commit_msg>improve print<commit_after>#!/usr/bin/env python3
#******************************************************************************
# (C) 2018, Stefan Korner, Austria                                            *
#                                                                             *
# The Space Python Library is free software; you can redistribute it and/or   *
# modify it under under the terms of the MIT License as published by the      *
# Massachusetts Institute of Technology.                                      *
#                                                                             *
# The Space Python Library is distributed in the hope that it will be useful, *
# but WITHOUT ANY WARRANTY; without even the implied warranty of              *
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MIT License    *
# for more details.                                                           *
#******************************************************************************
# Space Segment - Unit Tests                                                  *
#******************************************************************************
import testData
import CCSDS.PACKET
import SPACE.DEF, SPACE.TMGEN
import UTIL.SYS

#############
# constants #
#############
SYS_CONFIGURATION = [
  ["HOST", "127.0.0.1"],
  ["NCTRS_ADMIN_SERVER_PORT", "13006"],
  ["NCTRS_TC_SERVER_PORT", "13007"],
  ["NCTRS_TM_SERVER_PORT", "2502"],
  ["SPACECRAFT_ID", "758"],
  ["DEF_GROUND_STATION_ID", "10"],
  ["GROUND_STATION_NAME", "ESA G/S "],
  ["TC_ACK_ACCEPT_SUCC_MNEMO", "ACK1"],
  ["TC_ACK_ACCEPT_FAIL_MNEMO", "NAK1"],
  ["TC_ACK_EXESTA_SUCC_MNEMO", "ACK2"],
  ["TC_ACK_EXESTA_FAIL_MNEMO", "NAK2"],
  ["TC_ACK_EXEPRO_SUCC_MNEMO", "ACK3"],
  ["TC_ACK_EXEPRO_FAIL_MNEMO", "NAK3"],
  ["TC_ACK_EXECUT_SUCC_MNEMO", "ACK4"],
  ["TC_ACK_EXECUT_FAIL_MNEMO", "NAK4"],
  ["TC_ACK_APID_PARAM_BYTE_OFFSET", "18"],
  ["TC_ACK_SSC_PARAM_BYTE_OFFSET", "20"],
  ["TC_TT_TIME_FORMAT", "CUC4"],
  ["TC_TT_TIME_BYTE_OFFSET", "11"],
  ["TC_TT_PKT_BYTE_OFFSET", "17"],
  ["TM_CYCLIC_MNEMO", "TM_PKT1"],
  ["TM_CYCLIC_PERIOD_MS", "5000"],
  ["TM_TT_TIME_FORMAT", "CUC4"],
  ["TM_TT_TIME_BYTE_OFFSET", "10"],
  ["TM_RECORD_FORMAT", "NCTRS"],
  ["TM_REPLAY_KEY", "SPID"],
  ["OBT_MISSION_EPOCH_STR", UTIL.TCO.UNIX_MISSION_EPOCH_STR],
  ["OBT_LEAP_SECONDS", "0"],
  ["ERT_MISSION_EPOCH_STR", UTIL.TCO.UNIX_MISSION_EPOCH_STR],
  ["ERT_LEAP_SECONDS", "0"],
  ["SYS_COLOR_LOG", "1"],
  ["SYS_APP_MNEMO", "SIM"],
  ["SYS_APP_NAME", "Simulator"],
  ["SYS_APP_VERSION", "1.0"]]

#############
# functions #
#############
# -----------------------------------------------------------------------------
def test_DEFoperations():
  """function to test the DEF definition module"""
  SPACE.DEF.init()
  print("----- TM packet definitions -----")
  tmPktDefs = SPACE.IF.s_definitions.getTMpktDefs()
  for tmPktDef in tmPktDefs:
    print(tmPktDef.pktSPID, end=' ')
  print("")
  print("----- TM parameter definitions -----")
  tmParamDefs = SPACE.IF.s_definitions.getTMparamDefs()
  for tmParamDef in tmParamDefs:
    print(tmParamDef.paramName, end=' ')
  print("")
  # force a re-creation of the persistent definitions
  # (for the next test run)
  SPACE.IF.s_definitions.createDefinitions()
  return True
# -----------------------------------------------------------------------------
def test_TMGENoperations():
  """function to test the TMGEN telemetry generator"""
  SPACE.TMGEN.init()
  # create TM packet without TM parameters
  tmPacket = SPACE.IF.s_tmPacketGenerator.getTMpacket(
    spid=testData.TM_PACKET_03_SPID,
    parameterValues=[],
    dataField=None,
    segmentationFlags=CCSDS.PACKET.UNSEGMENTED,
    obtUTC=0.0,
    reuse=True)
  print("tmPacket =", tmPacket)
  if UTIL.DU.array2str(tmPacket.getBufferString()) != "\n" + \
    "0000 0C D2 C0 00 00 26 10 03 19 00 00 00 00 00 00 00 .....&..........\n" + \
    "0010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................\n" + \
    "0020 00 00 00 00 00 00 00 00 00 00 00 A9 07          .............":
    print("unexpected TM packet encoding")
    return False
  if tmPacket.versionNumber != testData.TM_PACKET_03_versionNumber:
    print("tmPacket versionNumber wrong:", tmPacket.versionNumber, "- should be", testData.TM_PACKET_03_versionNumber)
    return False
  if tmPacket.packetType != testData.TM_PACKET_03_packetType:
    print("tmPacket packetType wrong:", tmPacket.packetType, "- should be", testData.TM_PACKET_03_packetType)
    return False
  if tmPacket.dataFieldHeaderFlag != testData.TM_PACKET_03_dataFieldHeaderFlag:
    print("tmPacket dataFieldHeaderFlag wrong:", tmPacket.dataFieldHeaderFlag, "- should be", testData.TM_PACKET_03_dataFieldHeaderFlag)
    return False
  if tmPacket.applicationProcessId != testData.TM_PACKET_03_applicationProcessId:
    print("tmPacket applicationProcessId wrong:", tmPacket.applicationProcessId, "- should be", testData.TM_PACKET_03_applicationProcessId)
    return False
  if tmPacket.segmentationFlags != testData.TM_PACKET_03_segmentationFlags:
    print("tmPacket segmentationFlags wrong:", tmPacket.segmentationFlags, "- should be", testData.TM_PACKET_03_segmentationFlags)
    return False
  if tmPacket.sequenceControlCount != testData.TM_PACKET_03_sequenceControlCount:
    print("tmPacket sequenceControlCount wrong:", tmPacket.sequenceControlCount, "- should be", testData.TM_PACKET_03_sequenceControlCount)
    return False
  if tmPacket.packetLength != testData.TM_PACKET_03_packetLength:
    print("tmPacket packetLength wrong:", tmPacket.packetLength, "- should be", testData.TM_PACKET_03_packetLength)
    return False
  if tmPacket.pusVersionNumber != testData.TM_PACKET_03_pusVersionNumber:
    print("tmPacket pusVersionNumber wrong:", tmPacket.pusVersionNumber, "- should be", testData.TM_PACKET_03_pusVersionNumber)
    return False
  if tmPacket.serviceType != testData.TM_PACKET_03_serviceType:
    print("tmPacket serviceType wrong:", tmPacket.serviceType, "- should be", testData.TM_PACKET_03_serviceType)
    return False
  if tmPacket.serviceSubType != testData.TM_PACKET_03_serviceSubType:
    print("tmPacket serviceSubType wrong:", tmPacket.serviceSubType, "- should be", testData.TM_PACKET_03_serviceSubType)
    return False
  if not tmPacket.checkPacketLength():
    print("tmPacket has inconsistent packetLength")
    return False
  if not tmPacket.checkChecksum():
    print("tmPacket has invalid checksum")
    return False
  # create TM packet with initialized TM parameters
  parameterValuesList = []
  parameterValuesList.append(["PAR1", 1])
  parameterValuesList.append(["PAR3", 1])
  parameterValuesList.append(["PAR5", 1])
  parameterValuesList.append(["PAR7", 1])
  parameterValuesList.append(["PAR9", 0x12345678])
  parameterValuesList.append(["PAR11", 10.0])
  parameterValuesList.append(["PAR12", 10.0])
  tmPacket = SPACE.IF.s_tmPacketGenerator.getTMpacket(
    spid=testData.TM_PACKET_03_SPID,
    parameterValues=parameterValuesList,
    dataField=None,
    segmentationFlags=CCSDS.PACKET.UNSEGMENTED,
    obtUTC=0.0,
    reuse=True)
  print("tmPacket =", tmPacket)
  if UTIL.DU.array2str(tmPacket.getBufferString()) != "\n" + \
    "0000 0C D2 C0 01 00 26 10 03 19 00 00 00 00 00 00 00 .....&..........\n" + \
    "0010 00 00 AA 12 34 56 78 00 00 00 00 00 00 00 00 41 ....4Vx........A\n" + \
    "0020 20 00 00 40 24 00 00 00 00 00 00 BC 77           ..@$.......w":
    print("unexpected TM packet encoding")
    return False
  return True

########
# main #
########
if __name__ == "__main__":
  # initialise the system configuration
  UTIL.SYS.s_configuration.setDefaults(SYS_CONFIGURATION)
  print("***** test_DEFoperations() start")
  retVal = test_DEFoperations()
  print("***** test_DEFoperations() done:", retVal)
  print("***** test_TMGENoperations() start")
  retVal = test_TMGENoperations()
  print("***** test_TMGENoperations() done:", retVal)
<|endoftext|>


<commit_before>"""Remove legal_entity_congressional from DetachedAwardFinancialAssistance table

Revision ID: ff7c3328a4b1
Revises: 9acf8cc4e454
Create Date: 2017-07-17 10:29:42.577175

"""

# revision identifiers, used by Alembic.
revision = 'ff7c3328a4b1'
down_revision = '9acf8cc4e454'
branch_labels = None
depends_on = None

from alembic import op
import sqlalchemy as sa

def upgrade(engine_name):
    globals()["upgrade_%s" % engine_name]()

def downgrade(engine_name):
    globals()["downgrade_%s" % engine_name]()

def upgrade_data_broker():
    ### commands auto generated by Alembic - please adjust! ###
    op.drop_column('detached_award_financial_assistance', 'legal_entity_congressional')
    ### end Alembic commands ###

def downgrade_data_broker():
    ### commands auto generated by Alembic - please adjust! ###
    op.add_column('detached_award_financial_assistance', sa.Column('legal_entity_congressional', sa.TEXT(), autoincrement=False, nullable=True))
    ### end Alembic commands ###

<commit_msg>Updating alembic migration<commit_after>"""Remove legal_entity_congressional from DetachedAwardFinancialAssistance table

Revision ID: ff7c3328a4b1
Revises: 5350b3e22c39
Create Date: 2017-07-17 10:29:42.577175

"""

# revision identifiers, used by Alembic.
revision = 'ff7c3328a4b1'
down_revision = '5350b3e22c39'
branch_labels = None
depends_on = None

from alembic import op
import sqlalchemy as sa

def upgrade(engine_name):
    globals()["upgrade_%s" % engine_name]()

def downgrade(engine_name):
    globals()["downgrade_%s" % engine_name]()

def upgrade_data_broker():
    ### commands auto generated by Alembic - please adjust! ###
    op.drop_column('detached_award_financial_assistance', 'legal_entity_congressional')
    ### end Alembic commands ###

def downgrade_data_broker():
    ### commands auto generated by Alembic - please adjust! ###
    op.add_column('detached_award_financial_assistance', sa.Column('legal_entity_congressional', sa.TEXT(), autoincrement=False, nullable=True))
    ### end Alembic commands ###

<|endoftext|>


<commit_before>import itertools
from collections import namedtuple

from django.core.management import BaseCommand, CommandError

from corehq.apps.change_feed import data_sources, topics
from corehq.apps.change_feed.producer import producer
from corehq.apps.hqadmin.management.commands.stale_data_in_es import DataRow, HEADER_ROW
from corehq.form_processor.backends.sql.dbaccessors import ShardAccessor, doc_type_to_state
from corehq.form_processor.models import CommCareCaseSQL
from corehq.form_processor.utils import should_use_sql_backend
from dimagi.utils.chunked import chunked

from casexml.apps.case.models import CommCareCase
from corehq.util.couch import bulk_get_revs
from corehq.apps.hqcase.management.commands.backfill_couch_forms_and_cases import (
    publish_change, create_case_change_meta
)
from pillowtop.feed.interface import ChangeMeta

DocumentRecord = namedtuple('DocumentRecord', ['doc_id', 'doc_type', 'doc_subtype', 'domain'])

CASE_DOC_TYPES = {'CommCareCase'}
FORM_DOC_TYPES = set(doc_type_to_state.keys())

class Command(BaseCommand):
    """
    Republish doc changes. Meant to be used in conjunction with stale_data_in_es command

        $ ./manage.py republish_doc_changes changes.tsv
    """

    def add_arguments(self, parser):
        parser.add_argument('stale_data_in_es_file')
        parser.add_argument('--delimiter', default='\t', choices=('\t', ','))

    def handle(self, stale_data_in_es_file, delimiter, *args, **options):
        data_rows = _get_data_rows(stale_data_in_es_file, delimiter=delimiter)
        document_records = _get_document_records(data_rows)
        form_records = []
        case_records = []
        for record in document_records:
            if record.doc_type in CASE_DOC_TYPES:
                case_records.append(record)
            elif record.doc_type in FORM_DOC_TYPES:
                form_records.append(record)
            else:
                assert False, f'Bad doc type {record.doc_type} should have been caught already below.'
        _publish_cases(case_records)
        _publish_forms(form_records)

def _get_data_rows(stale_data_in_es_file, delimiter):
    with open(stale_data_in_es_file, 'r') as f:
        for line in f.readlines():
            data_row = DataRow(*line.rstrip('\n').split(delimiter))
            # Skip the header row anywhere in the file.
            # The "anywhere in the file" part is useful
            # if you cat multiple stale_data_in_es_file files together.
            if data_row != HEADER_ROW:
                yield data_row

def _get_document_records(data_rows):
    for data_row in data_rows:
        doc_id, doc_type, doc_subtype, domain = \
            data_row.doc_id, data_row.doc_type, data_row.doc_subtype, data_row.domain
        if doc_type not in set.union(CASE_DOC_TYPES, FORM_DOC_TYPES):
            raise CommandError(f"Found bad doc type {doc_type}. "
                               "Did you use the right command to create the data?")
        yield DocumentRecord(doc_id, doc_type, doc_subtype, domain)

def _publish_cases(case_records):
    for domain, records in itertools.groupby(case_records, lambda r: r.domain):
        if should_use_sql_backend(domain):
            _publish_cases_for_sql(domain, records)
        else:
            _publish_cases_for_couch(domain, [c.doc_id for c in records])

def _publish_forms(form_records):
    for domain, records in itertools.groupby(form_records, lambda r: r.domain):
        if should_use_sql_backend(domain):
            _publish_forms_for_sql(domain, records)
        else:
            raise CommandError("Republishing forms for couch domains is not supported yet!")

def _publish_cases_for_couch(domain, case_ids):
    for ids in chunked(case_ids, 500):
        doc_id_rev_list = bulk_get_revs(CommCareCase.get_db(), ids)
        for doc_id, doc_rev in doc_id_rev_list:
            publish_change(
                create_case_change_meta(domain, doc_id, doc_rev)
            )

def _publish_cases_for_sql(domain, case_records):
    records_with_types = filter(lambda r: r.doc_subtype, case_records)
    records_with_no_types = filter(lambda r: not r.doc_subtype, case_records)
    # if we already had a type just publish as-is
    for record in records_with_types:
        producer.send_change(
            topics.CASE_SQL,
            _change_meta_for_sql_case(domain, record.doc_id, record.doc_subtype)
        )

    # else lookup the type from the database
    for record_chunk in chunked(records_with_no_types, 10000):
        # databases will contain a mapping of shard database ids to case_ids in that DB
        id_chunk = [r.doc_id for r in record_chunk]
        databases = ShardAccessor.get_docs_by_database(id_chunk)
        for db, doc_ids in databases.items():
            results = CommCareCaseSQL.objects.using(db).filter(
                case_id__in=doc_ids,
            ).values_list('case_id', 'type')
            # make sure we found the same number of IDs
            assert len(results) == len(doc_ids)
            for case_id, case_type in results:
                producer.send_change(
                    topics.CASE_SQL,
                    _change_meta_for_sql_case(domain, case_id, case_type)
                )

def _change_meta_for_sql_case(domain, case_id, case_type):
    doc_type, = CASE_DOC_TYPES
    return ChangeMeta(
        document_id=case_id,
        data_source_type=data_sources.SOURCE_SQL,
        data_source_name=data_sources.CASE_SQL,
        document_type=doc_type,
        document_subtype=case_type,
        domain=domain,
        is_deletion=False,
    )

def _publish_forms_for_sql(domain, form_records):
    for record in form_records:
        producer.send_change(
            topics.FORM_SQL,
            _change_meta_for_sql_form_record(domain, record)
        )

def _change_meta_for_sql_form_record(domain, form_record):
    return ChangeMeta(
        document_id=form_record.doc_id,
        data_source_type=data_sources.SOURCE_SQL,
        data_source_name=data_sources.FORM_SQL,
        document_type=form_record.doc_type,
        document_subtype=form_record.doc_subtype,
        domain=domain,
        is_deletion=False,
    )
<commit_msg>Pass list (not iterator) into _publish_cases_for_sql<commit_after>import itertools
from collections import namedtuple

from django.core.management import BaseCommand, CommandError

from corehq.apps.change_feed import data_sources, topics
from corehq.apps.change_feed.producer import producer
from corehq.apps.hqadmin.management.commands.stale_data_in_es import DataRow, HEADER_ROW
from corehq.form_processor.backends.sql.dbaccessors import ShardAccessor, doc_type_to_state
from corehq.form_processor.models import CommCareCaseSQL
from corehq.form_processor.utils import should_use_sql_backend
from dimagi.utils.chunked import chunked

from casexml.apps.case.models import CommCareCase
from corehq.util.couch import bulk_get_revs
from corehq.apps.hqcase.management.commands.backfill_couch_forms_and_cases import (
    publish_change, create_case_change_meta
)
from pillowtop.feed.interface import ChangeMeta

DocumentRecord = namedtuple('DocumentRecord', ['doc_id', 'doc_type', 'doc_subtype', 'domain'])

CASE_DOC_TYPES = {'CommCareCase'}
FORM_DOC_TYPES = set(doc_type_to_state.keys())

class Command(BaseCommand):
    """
    Republish doc changes. Meant to be used in conjunction with stale_data_in_es command

        $ ./manage.py republish_doc_changes changes.tsv
    """

    def add_arguments(self, parser):
        parser.add_argument('stale_data_in_es_file')
        parser.add_argument('--delimiter', default='\t', choices=('\t', ','))

    def handle(self, stale_data_in_es_file, delimiter, *args, **options):
        data_rows = _get_data_rows(stale_data_in_es_file, delimiter=delimiter)
        document_records = _get_document_records(data_rows)
        form_records = []
        case_records = []
        for record in document_records:
            if record.doc_type in CASE_DOC_TYPES:
                case_records.append(record)
            elif record.doc_type in FORM_DOC_TYPES:
                form_records.append(record)
            else:
                assert False, f'Bad doc type {record.doc_type} should have been caught already below.'
        _publish_cases(case_records)
        _publish_forms(form_records)

def _get_data_rows(stale_data_in_es_file, delimiter):
    with open(stale_data_in_es_file, 'r') as f:
        for line in f.readlines():
            data_row = DataRow(*line.rstrip('\n').split(delimiter))
            # Skip the header row anywhere in the file.
            # The "anywhere in the file" part is useful
            # if you cat multiple stale_data_in_es_file files together.
            if data_row != HEADER_ROW:
                yield data_row

def _get_document_records(data_rows):
    for data_row in data_rows:
        doc_id, doc_type, doc_subtype, domain = \
            data_row.doc_id, data_row.doc_type, data_row.doc_subtype, data_row.domain
        if doc_type not in set.union(CASE_DOC_TYPES, FORM_DOC_TYPES):
            raise CommandError(f"Found bad doc type {doc_type}. "
                               "Did you use the right command to create the data?")
        yield DocumentRecord(doc_id, doc_type, doc_subtype, domain)

def _publish_cases(case_records):
    for domain, records in itertools.groupby(case_records, lambda r: r.domain):
        if should_use_sql_backend(domain):
            _publish_cases_for_sql(domain, list(records))
        else:
            _publish_cases_for_couch(domain, [c.doc_id for c in records])

def _publish_forms(form_records):
    for domain, records in itertools.groupby(form_records, lambda r: r.domain):
        if should_use_sql_backend(domain):
            _publish_forms_for_sql(domain, records)
        else:
            raise CommandError("Republishing forms for couch domains is not supported yet!")

def _publish_cases_for_couch(domain, case_ids):
    for ids in chunked(case_ids, 500):
        doc_id_rev_list = bulk_get_revs(CommCareCase.get_db(), ids)
        for doc_id, doc_rev in doc_id_rev_list:
            publish_change(
                create_case_change_meta(domain, doc_id, doc_rev)
            )

def _publish_cases_for_sql(domain, case_records):
    records_with_types = filter(lambda r: r.doc_subtype, case_records)
    records_with_no_types = filter(lambda r: not r.doc_subtype, case_records)
    # if we already had a type just publish as-is
    for record in records_with_types:
        producer.send_change(
            topics.CASE_SQL,
            _change_meta_for_sql_case(domain, record.doc_id, record.doc_subtype)
        )

    # else lookup the type from the database
    for record_chunk in chunked(records_with_no_types, 10000):
        # databases will contain a mapping of shard database ids to case_ids in that DB
        id_chunk = [r.doc_id for r in record_chunk]
        databases = ShardAccessor.get_docs_by_database(id_chunk)
        for db, doc_ids in databases.items():
            results = CommCareCaseSQL.objects.using(db).filter(
                case_id__in=doc_ids,
            ).values_list('case_id', 'type')
            # make sure we found the same number of IDs
            assert len(results) == len(doc_ids)
            for case_id, case_type in results:
                producer.send_change(
                    topics.CASE_SQL,
                    _change_meta_for_sql_case(domain, case_id, case_type)
                )

def _change_meta_for_sql_case(domain, case_id, case_type):
    doc_type, = CASE_DOC_TYPES
    return ChangeMeta(
        document_id=case_id,
        data_source_type=data_sources.SOURCE_SQL,
        data_source_name=data_sources.CASE_SQL,
        document_type=doc_type,
        document_subtype=case_type,
        domain=domain,
        is_deletion=False,
    )

def _publish_forms_for_sql(domain, form_records):
    for record in form_records:
        producer.send_change(
            topics.FORM_SQL,
            _change_meta_for_sql_form_record(domain, record)
        )

def _change_meta_for_sql_form_record(domain, form_record):
    return ChangeMeta(
        document_id=form_record.doc_id,
        data_source_type=data_sources.SOURCE_SQL,
        data_source_name=data_sources.FORM_SQL,
        document_type=form_record.doc_type,
        document_subtype=form_record.doc_subtype,
        domain=domain,
        is_deletion=False,
    )
<|endoftext|>