<commit_before>#!/usr/bin/env python
"""Stream XML Writer module"""

# Copyright (c) 2009 Filip Salomonsson
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.

__author__ = "Filip Salomonsson <filip.salomonsson@gmail.com>"

INDENT = "  "

def escape_attribute(value, encoding):
    value = value.replace("&", "&")
    value = value.replace("<", "<")
    value = value.replace("\"", """)
    return value.encode(encoding, "xmlcharrefreplace")

def escape_cdata(data, encoding):
    data = data.replace("&", "&")
    data = data.replace("<", "<")
    data = data.replace(">", ">")
    return data.encode(encoding, "xmlcharrefreplace")

def sorter_factory(attrib_order):
    for tag, names in attrib_order.iteritems():
        attrib_order[tag] = dict((name, n) for (n, name) in enumerate(names))
    def asort(pairs, tag):
        def key(a):
            name, value = a
            keys = attrib_order.get(tag, {})
            if name in keys:
                return keys.get(name), name
            else:
                return keys.get(None, len(keys)), name
        return sorted(pairs, key=key)
    return asort

class XMLSyntaxError(Exception):
    pass

class XMLWriter(object):
    """Stream XML writer"""
    def __init__(self, file, encoding="us-ascii",
                 pretty_print=False, sort=True):
        self.write = file.write
        self.encoding = encoding
        self._pretty_print = pretty_print
        self._sort = sort
        if isinstance(sort, dict):
            self._sort = sorter_factory(sort)
        self._tags = []
        self._start_tag_open = False
        self.declaration()

    def start(self, tag, attributes=None, **kwargs):
        self._close_start()
        if self._pretty_print and self._tags and not self._wrote_data:
            self.write("\n" + INDENT * len(self._tags))
        self.write("<" + tag)
        if attributes or kwargs:
            if attributes is None: attributes={}
            attributes = attributes.items() + kwargs.items()
            if self._sort:
                if callable(self._sort):
                    attributes = self._sort(attributes, tag)
                else:
                    attributes = sorted(attributes)
                for name, value in attributes:
                    self.write(" " + name + "=\""
                               + escape_attribute(value, self.encoding)
                               + "\"")
        self._start_tag_open = True
        self._wrote_data = False
        self._tags.append(tag)

    def end(self, tag=None):
        open_tag = self._tags.pop()
        if tag is not None and open_tag != tag:
            raise XMLSyntaxError("Start and end tag mismatch: %s and /%s."
                                 % (open_tag, tag))
        if self._start_tag_open:
            self.write(" />")
            self._start_tag_open = False
        else:
            if self._pretty_print and not self._wrote_data:
                self.write("\n" + INDENT * len(self._tags))
            self.write("</" + open_tag + ">")
        self._wrote_data = False

    def data(self, data):
        self._close_start()
        self.write(escape_cdata(data, self.encoding))
        self._wrote_data = True

    def element(self, tag, attributes=None, data=None, **kwargs):
        self.start(tag, attributes, **kwargs)
        if data:
            self.data(data)
        self.end(tag)

    def _close_start(self):
        if self._start_tag_open:
            self.write(">")
        self._start_tag_open = False

    def declaration(self):
        if self.encoding not in ("us-ascii", "utf-8"):
            self.write("<?xml version='1.0' encoding='" + self.encoding + "'?>")

    def close(self):
        while self._tags:
            self.end()
<commit_msg>Added __version__<commit_after>#!/usr/bin/env python
"""Stream XML Writer module"""

# Copyright (c) 2009 Filip Salomonsson
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.

__author__ = "Filip Salomonsson <filip.salomonsson@gmail.com>"
__version__ = "1.0 pre-alpha"

INDENT = "  "

def escape_attribute(value, encoding):
    value = value.replace("&", "&")
    value = value.replace("<", "<")
    value = value.replace("\"", """)
    return value.encode(encoding, "xmlcharrefreplace")

def escape_cdata(data, encoding):
    data = data.replace("&", "&")
    data = data.replace("<", "<")
    data = data.replace(">", ">")
    return data.encode(encoding, "xmlcharrefreplace")

def sorter_factory(attrib_order):
    for tag, names in attrib_order.iteritems():
        attrib_order[tag] = dict((name, n) for (n, name) in enumerate(names))
    def asort(pairs, tag):
        def key(a):
            name, value = a
            keys = attrib_order.get(tag, {})
            if name in keys:
                return keys.get(name), name
            else:
                return keys.get(None, len(keys)), name
        return sorted(pairs, key=key)
    return asort

class XMLSyntaxError(Exception):
    pass

class XMLWriter(object):
    """Stream XML writer"""
    def __init__(self, file, encoding="us-ascii",
                 pretty_print=False, sort=True):
        self.write = file.write
        self.encoding = encoding
        self._pretty_print = pretty_print
        self._sort = sort
        if isinstance(sort, dict):
            self._sort = sorter_factory(sort)
        self._tags = []
        self._start_tag_open = False
        self.declaration()

    def start(self, tag, attributes=None, **kwargs):
        self._close_start()
        if self._pretty_print and self._tags and not self._wrote_data:
            self.write("\n" + INDENT * len(self._tags))
        self.write("<" + tag)
        if attributes or kwargs:
            if attributes is None: attributes={}
            attributes = attributes.items() + kwargs.items()
            if self._sort:
                if callable(self._sort):
                    attributes = self._sort(attributes, tag)
                else:
                    attributes = sorted(attributes)
                for name, value in attributes:
                    self.write(" " + name + "=\""
                               + escape_attribute(value, self.encoding)
                               + "\"")
        self._start_tag_open = True
        self._wrote_data = False
        self._tags.append(tag)

    def end(self, tag=None):
        open_tag = self._tags.pop()
        if tag is not None and open_tag != tag:
            raise XMLSyntaxError("Start and end tag mismatch: %s and /%s."
                                 % (open_tag, tag))
        if self._start_tag_open:
            self.write(" />")
            self._start_tag_open = False
        else:
            if self._pretty_print and not self._wrote_data:
                self.write("\n" + INDENT * len(self._tags))
            self.write("</" + open_tag + ">")
        self._wrote_data = False

    def data(self, data):
        self._close_start()
        self.write(escape_cdata(data, self.encoding))
        self._wrote_data = True

    def element(self, tag, attributes=None, data=None, **kwargs):
        self.start(tag, attributes, **kwargs)
        if data:
            self.data(data)
        self.end(tag)

    def _close_start(self):
        if self._start_tag_open:
            self.write(">")
        self._start_tag_open = False

    def declaration(self):
        if self.encoding not in ("us-ascii", "utf-8"):
            self.write("<?xml version='1.0' encoding='" + self.encoding + "'?>")

    def close(self):
        while self._tags:
            self.end()
<|endoftext|>


<commit_before>"""
check if a binary tree is a bst
"""

<commit_msg>Update 4.4_checkbst.py<commit_after>"""
check if a binary tree is a bst
"""

def tree_max(node):
    maxleft  = float('-inf') if not node.left  else tree_max(node.left)
    maxright = float('-inf') if not node.right else tree_max(node.right)
    return max(node.value, maxleft, maxright)

def tree_min(node):
    minleft  = float('-inf') if not node.right else tree_min(node.left)
    minright = float('-inf') if not node.left else tree_min(node.right)
    return min(node.value, minleft, minright)

def verify(node):
    if tree_max(node.left) <= node.value and node.value <= tree_min(node.right):
       if verify(node.left) and verify(node.right):
           return True
       else:
           return False
    else:
        return False
<|endoftext|>


<commit_before>import argparse
import os
import math
import time
import servo_process as sp

class Arm:
    def __init__(self):
        self.init_servos()
        # 'scooping', ''
        self.spoon_status = ''
        self.status = ''

    def init_servos(self):
        self.sp = sp.ServoProcess()
        self.sp.start()

    def run(self):
        i = 0
        # self.ease_spoon(180)
        # self.sp.spoon.rotate(180, 2000)
        # self.sp.spoon.sleep(2000)
        # self.sp.spoon.rotate(90, 2000)
        # self.sp.leg.rotate(180, 2000)
        # self.sp.leg.rotate(90, 2000)
        # self.leg_servo.rotate(180, 2000)
        # self.reset_position()
        while(1):
            # pass
            self.scoop()
            self.update_spoon_status()

            if self.spoon_status == 'finished_scooping':
                self.reset_position();
            #
            # if self.status == 'reset':
            #     print 'SHUTTING DOWN'
            #     break
            #
            # self.update_status()

            # if self.spoon_status == 'finished_scooping':
            #     self.reset_position()
            #     break
            # print self.sp.leg.finished()
            # time.sleep(1)
            # self.sp.spoon.stop()
            # self.scoop()
            # # time.sleep(.5)
            # # self.spoon_servo.rotate_instant(180)
            # self.update_spoon_status()

            # time.sleep(5)
            # self.spoon_servo.stop()
            # is_alive()
            # self.ease_spoon(0)

            # self.spoon_servo.terminate()
            # time.sleep(5)
            # self.ease_spoon(180)
            # i += 1

    def scoop(self):
        if self.spoon_status in ['scooping', 'finished_scooping']: return
        print 'SCOOPING'
        self.spoon_status = 'scooping'

        self.sp.spoon.rotate(0, 2000)
        # self.sp.leg.sleep(2000)
        #
        # self.sp.leg.rotate(0, 2000)
        # self.sp.spoon.sleep(2000)
        #
        # self.sp.spoon.rotate(90, 2000)
        # self.sp.leg.sleep(2000)
        #
        # self.sp.leg.rotate(20, 2000)

    def update_spoon_status(self):
        if self.spoon_status == 'scooping' and self.sp.spoon.is_finished() and self.sp.leg.is_finished():
            self.spoon_status = 'finished_scooping'

    def update_status(self):
        if self.status == 'resetting' and self.sp.spoon.is_finished() and self.sp.leg.is_finished():
            print 'RESET'
            self.status = 'reset'

    def reset_position(self):
        # TODO: Gradually decrease angle until error, then servos are 0 position
        if self.status in ['resetting', 'reset']: return
        print 'RESETTING'
        self.status = 'resetting'
        self.sp.spoon.rotate(0, 2000)
        self.sp.leg.rotate(0, 2000)

a = Arm()
a.run()
<commit_msg>Update<commit_after>import argparse
import os
import math
import time
import servo_process as sp

class Arm:
    def __init__(self):
        self.init_servos()
        # 'scooping', ''
        self.spoon_status = ''
        self.status = ''

    def init_servos(self):
        self.sp = sp.ServoProcess()
        self.sp.start()

    def run(self):
        i = 0
        # self.ease_spoon(180)
        # self.sp.spoon.rotate(180, 2000)
        # self.sp.spoon.sleep(2000)
        # self.sp.spoon.rotate(90, 2000)
        # self.sp.leg.rotate(180, 2000)
        # self.sp.leg.rotate(90, 2000)
        # self.leg_servo.rotate(180, 2000)
        # self.reset_position()
        while(1):
            # pass
            self.scoop()
            self.update_spoon_status()

            if self.spoon_status == 'finished_scooping':
                self.reset_position();
            #
            # if self.status == 'reset':
            #     print 'SHUTTING DOWN'
            #     break
            #
            # self.update_status()

            # if self.spoon_status == 'finished_scooping':
            #     self.reset_position()
            #     break
            # print self.sp.leg.finished()
            # time.sleep(1)
            # self.sp.spoon.stop()
            # self.scoop()
            # # time.sleep(.5)
            # # self.spoon_servo.rotate_instant(180)
            # self.update_spoon_status()

            # time.sleep(5)
            # self.spoon_servo.stop()
            # is_alive()
            # self.ease_spoon(0)

            # self.spoon_servo.terminate()
            # time.sleep(5)
            # self.ease_spoon(180)
            # i += 1

    def scoop(self):
        if self.spoon_status in ['scooping', 'finished_scooping']: return
        print 'SCOOPING'
        self.spoon_status = 'scooping'

        self.sp.spoon.rotate(0, 2000)
        # self.sp.leg.sleep(2000)
        #
        # self.sp.leg.rotate(0, 2000)
        # self.sp.spoon.sleep(2000)
        #
        # self.sp.spoon.rotate(90, 2000)
        # self.sp.leg.sleep(2000)
        #
        self.sp.leg.rotate(20, 2000)

    def update_spoon_status(self):
        if self.spoon_status == 'scooping' and self.sp.spoon.is_finished() and self.sp.leg.is_finished():
            self.spoon_status = 'finished_scooping'

    def update_status(self):
        if self.status == 'resetting' and self.sp.spoon.is_finished() and self.sp.leg.is_finished():
            print 'RESET'
            self.status = 'reset'

    def reset_position(self):
        # TODO: Gradually decrease angle until error, then servos are 0 position
        if self.status in ['resetting', 'reset']: return
        print 'RESETTING'
        self.status = 'resetting'
        self.sp.spoon.rotate(0, 2000)
        self.sp.leg.rotate(0, 2000)

a = Arm()
a.run()
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
###############################################################################
#
#    Copyright (C) 2001-2014 Micronaet SRL (<http://www.micronaet.it>).
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU Affero General Public License as published
#    by the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU Affero General Public License for more details.
#
#    You should have received a copy of the GNU Affero General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
###############################################################################
import os
import sys
import win32service
import win32serviceutil
import win32event
import servicemanager
import time

import webservice
import xmlrpclib
import ConfigParser

from datetime import datetime

class PySvc(win32serviceutil.ServiceFramework):
    ''' Class for manage Microsoft Service
    '''
    # -------------------------------------------------------------------------
    #                                  INSTANCE DATA:
    # -------------------------------------------------------------------------
    # Service name (use with net start / stop
    _svc_name_ = 'Micronaet Listener Service'

    # Service name in Service Control Manager (SCM)
    _svc_display_name_ = 'Micronaet Web Service'

    # Help text in SCM
    _svc_description_ = 'This service open a XMLRPC listner for remote call'

    # -------------------------------------------------------------------------
    #                                  PRIVATE METHOD:
    # -------------------------------------------------------------------------
    def _create_setup_file(self, ):
        ''' Create config file if not present in data path
        '''
        if not os.path.isfile(self._setup_file):
            setup_file = open(self._setup_file, 'w')
            setup_file.write('[XMLRPC]%shost: 0.0.0.0%sport: 7000' % (
                self._return,
                self._return,
                ))
            setup_file.close()
            self._log_data('Create empty config file: %s%s' % (
                self._setup_file, self._return))
        return True

    def _log_data(self, event, mode='INFO', registry='activity', close=False):
        ''' Log data on file:
        '''
        # ---------------------------------------------------------------------
        # Chose log registry file:
        # ---------------------------------------------------------------------
        if registry == 'activity':
            if not self._file_activity:
                # Open log file for service session:
                self._file_activity = open(self._log_activity, 'a+')
            current_log = self._file_activity
        else: # service registry
            if not self._file_service:
                # Open log file for service session:
                self._file_service = open(self._log_service, 'a+')
            current_log = self._file_service

        # ---------------------------------------------------------------------
        # Write event:
        # ---------------------------------------------------------------------
        current_log.write('%s. [%s] %s%s' % (
            datetime.now(),
            mode.upper(),
            event,
            self._return,
            ))
        current_log.flush()

        # ---------------------------------------------------------------------
        # Closing check:
        # ---------------------------------------------------------------------
        if close:
            try:
                current_log.close()
            except:
                pass # TODO Log closing error?
        return True

    def _init(self, ):
        ''' Initial setup (launched both init and start service)
        '''
        # ---------------------------------------------------------------------
        # Parameters:
        # ---------------------------------------------------------------------
        # Return:
        self._return = '\r\n'

        # Root folder:
        self._root_path = 'C:\\Micronaet\\Micronaet Listener Service' # TODO
        #self._root_path = os.path.join(
        #    'C:\\',
        #    'Micronaet',
        #    self._svc_name_,
        #    ))

        print 'Root folder: %s' % self._root_path

        # Configuration:
        self._setup_file = os.path.join(self._root_path, 'service.cfg')
        print 'Config file: %s' % self._setup_file

        self._webserver_command = 'start %s "%s"' % (
            os.path.join(
                os.path.dirname(os.path.realpath(__file__)),
                'webservice.py',
                ),
            self._setup_file,
            )
        print 'Web Server: %s' % self._webserver_command

        # Log:
        self._log_path = os.path.join(self._root_path, 'log')
        self._log_service = os.path.join(self._log_path, 'service.log')
        self._log_activity = os.path.join(self._log_path, 'activity.log')
        print 'Log file Service: %s Activity: %s' % (
            self._log_service,
            self._log_activity,
            )

        # Handle file:
        self._file_service = False
        self._file_activity = False

        # Millisecond for waiting:
        self._wait_ms = 5000

        # Create path if not present:
        try:
            os.system('mkdir "%s"' % self._log_path) # create also root folder
        except:
            print 'Error creating folder structure: %s' % self._log_path

        # ---------------------------------------------------------------------
        # Config file:
        # ---------------------------------------------------------------------
        # Create empty if not present:
        self._create_setup_file()

        # ---------------------------------------------------------------------
        # Log install event:
        # ---------------------------------------------------------------------
        self._log_data('Instance %s service%s    Log path: %s%sConfig: %s' % (
            self._svc_name_, self._return, self._log_path,
            self._return, self._setup_file),
            registry='service',
            )
        self._log_data('Web Server command: %s' % self._webserver_command)

    # -------------------------------------------------------------------------
    #                            CONSTRUCTOR:
    # -------------------------------------------------------------------------
    def __init__(self, args):
        ''' Constructor, init method
        '''
        # Setup instance:
        self._init()

        # ---------------------------------------------------------------------
        # Windows intallation:
        # ---------------------------------------------------------------------
        # Launch ancherstor init procedure:
        win32serviceutil.ServiceFramework.__init__(self, args)

        # create an event to listen for stop requests on
        self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)

    # -------------------------------------------------------------------------
    #                            PUBLIC METHODS:
    # -------------------------------------------------------------------------
    def SvcDoRun(self):
        ''' Start service method
        '''
        # Reload instance:
        self._init()

        response = None

        # Log operation:
        self._log_data('Start Listner Service', registry='service')

        # ---------------------------------------------------------------------
        #                     START WEBSERVICE:
        # ---------------------------------------------------------------------
        # Launch indipendent task:
        os.system(self._webserver_command)

        # ---------------------------------------------------------------------
        #                        RUNNING LOOP:
        # ---------------------------------------------------------------------
        # If the stop event hasn't been fired keep looping
        while response != win32event.WAIT_OBJECT_0:
            # Stop for X millisecond and listen for stop event
            response = win32event.WaitForSingleObject(
                self.hWaitStop,
                self._wait_ms,
                )

    def SvcStop(self):
        ''' Shutting down service:
        '''
        # ---------------------------------------------------------------------
        #                          STOP OPERATION:
        # ---------------------------------------------------------------------
        # Terminate web server:
        error = ''
        try:
            # A. Read config file:
            error = 'Error reading config file: %s' % self._setup_file
            config = ConfigParser.ConfigParser()
            config.read([self._setup_file])
            xmlrpc_host = config.get('XMLRPC', 'host')
            xmlrpc_port = eval(config.get('XMLRPC', 'port'))

            # B. Connecting remote server:
            error = 'Error connecting RPC: %s)' % address,
            address = 'http://%s:%s/RPC2' % (xmlrpc_host, xmlrpc_port)
            sock = xmlrpclib.ServerProxy(address, allow_none=True)

            # C. Shutdown command:
            error = 'Error stopping listener (setup: %s)' % address,
            sock.remote_shutdown()
            time.sleep(1)

            self._log_data(
                'Stop Listner Service %s' % address,
                registry='service',
                close=True,
                )

            # D. Shutdown command:
            error = 'Error closing log file'
            self._file_service.close()
            self._file_activity.close()
        except:
            self._log_data(
                error,
                mode='error',
                registry='service',
                close=True,
                )

        try:
            # Service stop part:
            # Tell the SCM shutting down event:
            self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)

            # Raise the stop event
            win32event.SetEvent(self.hWaitStop)
        except:
            pass # Service unavailable

if __name__ == '__main__':
    win32serviceutil.HandleCommandLine(PySvc)
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
<commit_msg>generic bugfix (fast message)<commit_after># -*- coding: utf-8 -*-
###############################################################################
#
#    Copyright (C) 2001-2014 Micronaet SRL (<http://www.micronaet.it>).
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU Affero General Public License as published
#    by the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU Affero General Public License for more details.
#
#    You should have received a copy of the GNU Affero General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
###############################################################################
import os
import sys
import win32service
import win32serviceutil
import win32event
import servicemanager
import time

import webservice
import xmlrpclib
import ConfigParser

from datetime import datetime

class PySvc(win32serviceutil.ServiceFramework):
    ''' Class for manage Microsoft Service
    '''
    # -------------------------------------------------------------------------
    #                                  INSTANCE DATA:
    # -------------------------------------------------------------------------
    # Service name (use with net start / stop
    _svc_name_ = 'Micronaet Listener Service'

    # Service name in Service Control Manager (SCM)
    _svc_display_name_ = 'Micronaet Web Service'

    # Help text in SCM
    _svc_description_ = 'This service open a XMLRPC listner for remote call'

    # -------------------------------------------------------------------------
    #                                  PRIVATE METHOD:
    # -------------------------------------------------------------------------
    def _create_setup_file(self, ):
        ''' Create config file if not present in data path
        '''
        if not os.path.isfile(self._setup_file):
            setup_file = open(self._setup_file, 'w')
            setup_file.write('[XMLRPC]%shost: 0.0.0.0%sport: 7000' % (
                self._return,
                self._return,
                ))
            setup_file.close()
            self._log_data('Create empty config file: %s%s' % (
                self._setup_file, self._return))
        return True

    def _log_data(self, event, mode='INFO', registry='activity', close=False):
        ''' Log data on file:
        '''
        # ---------------------------------------------------------------------
        # Chose log registry file:
        # ---------------------------------------------------------------------
        if registry == 'activity':
            if not self._file_activity:
                # Open log file for service session:
                self._file_activity = open(self._log_activity, 'a+')
            current_log = self._file_activity
        else: # service registry
            if not self._file_service:
                # Open log file for service session:
                self._file_service = open(self._log_service, 'a+')
            current_log = self._file_service

        # ---------------------------------------------------------------------
        # Write event:
        # ---------------------------------------------------------------------
        current_log.write('%s. [%s] %s%s' % (
            datetime.now(),
            mode.upper(),
            event,
            self._return,
            ))
        current_log.flush()

        # ---------------------------------------------------------------------
        # Closing check:
        # ---------------------------------------------------------------------
        if close:
            try:
                current_log.close()
            except:
                pass # TODO Log closing error?
        return True

    def _init(self, ):
        ''' Initial setup (launched both init and start service)
        '''
        # ---------------------------------------------------------------------
        # Parameters:
        # ---------------------------------------------------------------------
        # Return:
        self._return = '\r\n'

        # Root folder:
        self._root_path = 'C:\\Micronaet\\Micronaet Listener Service' # TODO
        #self._root_path = os.path.join(
        #    'C:\\',
        #    'Micronaet',
        #    self._svc_name_,
        #    ))

        print 'Root folder: %s' % self._root_path

        # Configuration:
        self._setup_file = os.path.join(self._root_path, 'service.cfg')
        print 'Config file: %s' % self._setup_file

        self._webserver_command = 'start %s "%s"' % (
            os.path.join(
                os.path.dirname(os.path.realpath(__file__)),
                'webservice.py',
                ),
            self._setup_file,
            )
        print 'Web Server: %s' % self._webserver_command

        # Log:
        self._log_path = os.path.join(self._root_path, 'log')
        self._log_service = os.path.join(self._log_path, 'service.log')
        self._log_activity = os.path.join(self._log_path, 'activity.log')
        print 'Log file Service: %s Activity: %s' % (
            self._log_service,
            self._log_activity,
            )

        # Handle file:
        self._file_service = False
        self._file_activity = False

        # Millisecond for waiting:
        self._wait_ms = 5000

        # Create path if not present:
        try:
            os.system('mkdir "%s"' % self._log_path) # create also root folder
        except:
            print 'Error creating folder structure: %s' % self._log_path

        # ---------------------------------------------------------------------
        # Config file:
        # ---------------------------------------------------------------------
        # Create empty if not present:
        self._create_setup_file()

        # ---------------------------------------------------------------------
        # Log install event:
        # ---------------------------------------------------------------------
        self._log_data('Instance %s service%s    Log path: %s%sConfig: %s' % (
            self._svc_name_, self._return, self._log_path,
            self._return, self._setup_file),
            registry='service',
            )
        self._log_data('Web Server command: %s' % self._webserver_command)

    # -------------------------------------------------------------------------
    #                            CONSTRUCTOR:
    # -------------------------------------------------------------------------
    def __init__(self, args):
        ''' Constructor, init method
        '''
        # Setup instance:
        self._init()

        # ---------------------------------------------------------------------
        # Windows intallation:
        # ---------------------------------------------------------------------
        # Launch ancherstor init procedure:
        win32serviceutil.ServiceFramework.__init__(self, args)

        # create an event to listen for stop requests on
        self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)

    # -------------------------------------------------------------------------
    #                            PUBLIC METHODS:
    # -------------------------------------------------------------------------
    def SvcDoRun(self):
        ''' Start service method
        '''
        # Reload instance:
        self._init()

        response = None

        # Log operation:
        self._log_data('Start Listner Service', registry='service')

        # ---------------------------------------------------------------------
        #                     START WEBSERVICE:
        # ---------------------------------------------------------------------
        # Launch indipendent task:
        os.system(self._webserver_command)

        # ---------------------------------------------------------------------
        #                        RUNNING LOOP:
        # ---------------------------------------------------------------------
        # If the stop event hasn't been fired keep looping
        while response != win32event.WAIT_OBJECT_0:
            # Stop for X millisecond and listen for stop event
            response = win32event.WaitForSingleObject(
                self.hWaitStop,
                self._wait_ms,
                )

    def SvcStop(self):
        ''' Shutting down service:
        '''
        # ---------------------------------------------------------------------
        #                          STOP OPERATION:
        # ---------------------------------------------------------------------
        # Terminate web server:
        error = ''
        try:
            # A. Read config file:
            error = 'Error reading config file: %s' % self._setup_file
            config = ConfigParser.ConfigParser()
            config.read([self._setup_file])
            xmlrpc_host = config.get('XMLRPC', 'host')
            xmlrpc_port = eval(config.get('XMLRPC', 'port'))

            # B. Connecting remote server:
            error = 'Error connecting RPC: %s)' % address,
            address = 'http://%s:%s/RPC2' % (xmlrpc_host, xmlrpc_port)
            sock = xmlrpclib.ServerProxy(address, allow_none=True)

            # C. Shutdown command:
            error = 'Error stopping listener (setup: %s)' % address,
            sock.remote_shutdown()
            time.sleep(1)

            self._log_data(
                'Stop Listner Service %s' % address,
                registry='service',
                close=True,
                )

            # D. Shutdown command:
            error = 'Error closing log file'
            self._file_service.close()
            self._file_activity.close()
        except:
            self._log_data(
                '%s\n    [%s]' % (error, sys.exc_info()),
                mode='error',
                registry='service',
                close=True,
                )

        try:
            # Service stop part:
            # Tell the SCM shutting down event:
            self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)

            # Raise the stop event
            win32event.SetEvent(self.hWaitStop)
        except:
            pass # Service unavailable

if __name__ == '__main__':
    win32serviceutil.HandleCommandLine(PySvc)
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
"""
bmp.py - module for constructing simple BMP graphics files

 Permission is hereby granted, free of charge, to any person obtaining
 a copy of this software and associated documentation files (the
 "Software"), to deal in the Software without restriction, including
 without limitation the rights to use, copy, modify, merge, publish,
 distribute, sublicense, and/or sell copies of the Software, and to
 permit persons to whom the Software is furnished to do so, subject to
 the following conditions:

 The above copyright notice and this permission notice shall be
 included in all copies or substantial portions of the Software.

 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

"""
__version__ = "0.3"
__about =  "bmp module, version %s, written by Paul McGuire, October, 2003, updated by Margus Laak, September, 2009" % __version__

from math import ceil, hypot

def shortToString(i):
  hi = (i & 0xff00) >> 8
  lo = i & 0x00ff
  return chr(lo) + chr(hi)

def longToString(i):
  hi = (long(i) & 0x7fff0000) >> 16
  lo = long(i) & 0x0000ffff
  return shortToString(lo) + shortToString(hi)

def long24ToString(i):
  return chr(i & 0xff) + chr(i >> 8 & 0xff) + chr(i >> 16 & 0xff)

def stringToLong(input_string, offset):
  return ord(input_string[offset+3]) << 24 | ord(input_string[offset+2]) << 16 | ord(input_string[offset+1]) << 8 | ord(input_string[offset])

def stringToLong24(input_string, offset):
  return ord(input_string[offset+2]) << 16 | ord(input_string[offset+1]) << 8 | ord(input_string[offset])

class Color(object):
  """class for specifying colors while drawing BitMap elements"""
  __slots__ = [ 'red', 'grn', 'blu' ]
  __shade = 32

  def __init__( self, r=0, g=0, b=0 ):
    self.red = r
    self.grn = g
    self.blu = b

  def __setattr__(self, name, value):
    if hasattr(self, name):
      raise AttributeError, "Color is immutable"
    else:
      object.__setattr__(self, name, value)

  def __str__( self ):
    return "R:%d G:%d B:%d" % (self.red, self.grn, self.blu )

  def __hash__( self ):
    return ( ( long(self.blu) ) +
              ( long(self.grn) <<  8 ) +
              ( long(self.red) << 16 ) )

  def __eq__( self, other ):
    return (self is other) or (self.toLong == other.toLong)

  def lighten( self ):
    return Color(
      min( self.red + Color.__shade, 255),
      min( self.grn + Color.__shade, 255),
      min( self.blu + Color.__shade, 255)
      )

  def darken( self ):
    return Color(
      max( self.red - Color.__shade, 0),
      max( self.grn - Color.__shade, 0),
      max( self.blu - Color.__shade, 0)
      )

  def toLong( self ):
    return self.__hash__()

  def fromLong( l ):
    b = l & 0xff
    l = l >> 8
    g = l & 0xff
    l = l >> 8
    r = l & 0xff
    return Color( r, g, b )
  fromLong = staticmethod(fromLong)

# define class constants for common colors
Color.BLACK    = Color(   0,   0,   0 )
Color.RED      = Color( 255,   0,   0 )
Color.GREEN    = Color(   0, 255,   0 )
Color.BLUE     = Color(   0,   0, 255 )
Color.CYAN     = Color(   0, 255, 255 )
Color.MAGENTA  = Color( 255,   0, 255 )
Color.YELLOW   = Color( 255, 255,   0 )
Color.WHITE    = Color( 255, 255, 255 )
Color.DKRED    = Color( 128,   0,   0 )
Color.DKGREEN  = Color(   0, 128,   0 )
Color.DKBLUE   = Color(   0,   0, 128 )
Color.TEAL     = Color(   0, 128, 128 )
Color.PURPLE   = Color( 128,   0, 128 )
Color.BROWN    = Color( 128, 128,   0 )
Color.GRAY     = Color( 128, 128, 128 )

class BitMap(object):
  """class for drawing and saving simple Windows bitmap files"""

  LINE_SOLID  = 0
  LINE_DASHED = 1
  LINE_DOTTED = 2
  LINE_DOT_DASH=3
  _DASH_LEN = 12.0
  _DOT_LEN = 6.0
  _DOT_DASH_LEN = _DOT_LEN + _DASH_LEN

  def __init__( self, width, height,
                 bkgd = Color.WHITE, frgd = Color.BLACK ):
    self.wd = int( ceil(width) )
    self.ht = int( ceil(height) )
    self.bgcolor = 0
    self.fgcolor = 1
    self.palette = []
    self.palette.append( bkgd.toLong() )
    self.palette.append( frgd.toLong() )
    self.currentPen = self.fgcolor

    tmparray = [ self.bgcolor ] * self.wd
    self.bitarray = [ tmparray[:] for i in range( self.ht ) ]
    self.currentPen = 1

  def plotPoint( self, x, y ):
    if ( 0 <= x < self.wd and 0 <= y < self.ht ):
      x = int(x)
      y = int(y)
      self.bitarray[y][x] = self.currentPen

  def _saveBitMapNoCompression( self ):
    line_padding = (4 - (self.wd % 4)) % 4

    # write bitmap header
    _bitmap = "BM"
    _bitmap += longToString( 54 + self.ht*(self.wd*3 + line_padding) )   # DWORD size in bytes of the file
    _bitmap += longToString( 0 )    # DWORD 0
    _bitmap += longToString( 54  )
    _bitmap += longToString( 40 )    # DWORD header size = 40
    _bitmap += longToString( self.wd )    # DWORD image width
    _bitmap += longToString( self.ht )    # DWORD image height
    _bitmap += shortToString( 1 )    # WORD planes = 1
    _bitmap += shortToString( 24 )    # WORD bits per pixel = 8
    _bitmap += longToString( 0 )    # DWORD compression = 0
    _bitmap += longToString( self.ht * (self.wd * 3 + line_padding) )    # DWORD sizeimage = size in bytes of the bitmap = width * height
    _bitmap += longToString( 0 )    # DWORD horiz pixels per meter (?)
    _bitmap += longToString( 0 )    # DWORD ver pixels per meter (?)
    _bitmap += longToString( 0 )    # DWORD number of colors used = 256
    _bitmap += longToString( 0 )    # DWORD number of "import colors = len( self.palette )

    # write pixels
    self.bitarray.reverse()
    for row in self.bitarray:
      for pixel in row:
        c = self.palette[pixel]
        _bitmap += long24ToString(c)
      for i in range(line_padding):
        _bitmap += chr( 0 )

    return _bitmap

  def saveFile( self, filename):
    _b = self._saveBitMapNoCompression( )

    f = file(filename, 'wb')
    f.write(_b)
    f.close()

def save_qrcode(qr, filename):
    matrix = qr.get_matrix()
    k = len(matrix)
    bitmap = BitMap( (k+2)*8, (k+2)*8 )
    bitmap.bitarray = []
    for r in range(k+2):
        tmparray = [ 0 ] * (k+2)*8

        if 0 < r < k+1:
            for c in range(k):
                if matrix[r-1][c]:
                    tmparray[ (1+c)*8:(2+c)*8] = [1]*8

        for i in range(8):
            bitmap.bitarray.append( tmparray[:] )

    bitmap.saveFile( filename )

if __name__ == "__main__":

  bmp = BitMap( 10, 10 )
  bmp.plotPoint( 5, 5 )
  bmp.plotPoint( 0, 0 )
  bmp.saveFile( "test.bmp" )
<commit_msg>Use string joins instead of concatenation for efficiency.<commit_after># -*- coding: utf-8 -*-
"""
bmp.py - module for constructing simple BMP graphics files

 Permission is hereby granted, free of charge, to any person obtaining
 a copy of this software and associated documentation files (the
 "Software"), to deal in the Software without restriction, including
 without limitation the rights to use, copy, modify, merge, publish,
 distribute, sublicense, and/or sell copies of the Software, and to
 permit persons to whom the Software is furnished to do so, subject to
 the following conditions:

 The above copyright notice and this permission notice shall be
 included in all copies or substantial portions of the Software.

 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

"""
__version__ = "0.3"
__about =  "bmp module, version %s, written by Paul McGuire, October, 2003, updated by Margus Laak, September, 2009" % __version__

from math import ceil, hypot

def shortToString(i):
  hi = (i & 0xff00) >> 8
  lo = i & 0x00ff
  return chr(lo) + chr(hi)

def longToString(i):
  hi = (long(i) & 0x7fff0000) >> 16
  lo = long(i) & 0x0000ffff
  return shortToString(lo) + shortToString(hi)

def long24ToString(i):
  return chr(i & 0xff) + chr(i >> 8 & 0xff) + chr(i >> 16 & 0xff)

def stringToLong(input_string, offset):
  return ord(input_string[offset+3]) << 24 | ord(input_string[offset+2]) << 16 | ord(input_string[offset+1]) << 8 | ord(input_string[offset])

def stringToLong24(input_string, offset):
  return ord(input_string[offset+2]) << 16 | ord(input_string[offset+1]) << 8 | ord(input_string[offset])

class Color(object):
  """class for specifying colors while drawing BitMap elements"""
  __slots__ = [ 'red', 'grn', 'blu' ]
  __shade = 32

  def __init__( self, r=0, g=0, b=0 ):
    self.red = r
    self.grn = g
    self.blu = b

  def __setattr__(self, name, value):
    if hasattr(self, name):
      raise AttributeError, "Color is immutable"
    else:
      object.__setattr__(self, name, value)

  def __str__( self ):
    return "R:%d G:%d B:%d" % (self.red, self.grn, self.blu )

  def __hash__( self ):
    return ( ( long(self.blu) ) +
              ( long(self.grn) <<  8 ) +
              ( long(self.red) << 16 ) )

  def __eq__( self, other ):
    return (self is other) or (self.toLong == other.toLong)

  def lighten( self ):
    return Color(
      min( self.red + Color.__shade, 255),
      min( self.grn + Color.__shade, 255),
      min( self.blu + Color.__shade, 255)
      )

  def darken( self ):
    return Color(
      max( self.red - Color.__shade, 0),
      max( self.grn - Color.__shade, 0),
      max( self.blu - Color.__shade, 0)
      )

  def toLong( self ):
    return self.__hash__()

  def fromLong( l ):
    b = l & 0xff
    l = l >> 8
    g = l & 0xff
    l = l >> 8
    r = l & 0xff
    return Color( r, g, b )
  fromLong = staticmethod(fromLong)

# define class constants for common colors
Color.BLACK    = Color(   0,   0,   0 )
Color.RED      = Color( 255,   0,   0 )
Color.GREEN    = Color(   0, 255,   0 )
Color.BLUE     = Color(   0,   0, 255 )
Color.CYAN     = Color(   0, 255, 255 )
Color.MAGENTA  = Color( 255,   0, 255 )
Color.YELLOW   = Color( 255, 255,   0 )
Color.WHITE    = Color( 255, 255, 255 )
Color.DKRED    = Color( 128,   0,   0 )
Color.DKGREEN  = Color(   0, 128,   0 )
Color.DKBLUE   = Color(   0,   0, 128 )
Color.TEAL     = Color(   0, 128, 128 )
Color.PURPLE   = Color( 128,   0, 128 )
Color.BROWN    = Color( 128, 128,   0 )
Color.GRAY     = Color( 128, 128, 128 )

class BitMap(object):
  """class for drawing and saving simple Windows bitmap files"""

  LINE_SOLID  = 0
  LINE_DASHED = 1
  LINE_DOTTED = 2
  LINE_DOT_DASH=3
  _DASH_LEN = 12.0
  _DOT_LEN = 6.0
  _DOT_DASH_LEN = _DOT_LEN + _DASH_LEN

  def __init__( self, width, height,
                 bkgd = Color.WHITE, frgd = Color.BLACK ):
    self.wd = int( ceil(width) )
    self.ht = int( ceil(height) )
    self.bgcolor = 0
    self.fgcolor = 1
    self.palette = []
    self.palette.append( bkgd.toLong() )
    self.palette.append( frgd.toLong() )
    self.currentPen = self.fgcolor

    tmparray = [ self.bgcolor ] * self.wd
    self.bitarray = [ tmparray[:] for i in range( self.ht ) ]
    self.currentPen = 1

  def plotPoint( self, x, y ):
    if ( 0 <= x < self.wd and 0 <= y < self.ht ):
      x = int(x)
      y = int(y)
      self.bitarray[y][x] = self.currentPen

  def _saveBitMapNoCompression( self ):
    line_padding = (4 - (self.wd % 4)) % 4

    # write bitmap header
    _bitmap = "BM"
    _bitmap += longToString( 54 + self.ht*(self.wd*3 + line_padding) )   # DWORD size in bytes of the file
    _bitmap += longToString( 0 )    # DWORD 0
    _bitmap += longToString( 54  )
    _bitmap += longToString( 40 )    # DWORD header size = 40
    _bitmap += longToString( self.wd )    # DWORD image width
    _bitmap += longToString( self.ht )    # DWORD image height
    _bitmap += shortToString( 1 )    # WORD planes = 1
    _bitmap += shortToString( 24 )    # WORD bits per pixel = 8
    _bitmap += longToString( 0 )    # DWORD compression = 0
    _bitmap += longToString( self.ht * (self.wd * 3 + line_padding) )    # DWORD sizeimage = size in bytes of the bitmap = width * height
    _bitmap += longToString( 0 )    # DWORD horiz pixels per meter (?)
    _bitmap += longToString( 0 )    # DWORD ver pixels per meter (?)
    _bitmap += longToString( 0 )    # DWORD number of colors used = 256
    _bitmap += longToString( 0 )    # DWORD number of "import colors = len( self.palette )

    # write pixels
    self.bitarray.reverse()
    rows = []
    for row in self.bitarray:
      for pixel in row:
        c = self.palette[pixel]
        rows.append(long24ToString(c))
      rows.append(chr(0) * line_padding)
    _bitmap += ''.join(rows)

    return _bitmap

  def saveFile( self, filename):
    _b = self._saveBitMapNoCompression( )

    f = file(filename, 'wb')
    f.write(_b)
    f.close()

def save_qrcode(qr, filename):
    matrix = qr.get_matrix()
    k = len(matrix)
    bitmap = BitMap( (k+2)*8, (k+2)*8 )
    bitmap.bitarray = []
    for r in range(k+2):
        tmparray = [ 0 ] * (k+2)*8

        if 0 < r < k+1:
            for c in range(k):
                if matrix[r-1][c]:
                    tmparray[ (1+c)*8:(2+c)*8] = [1]*8

        for i in range(8):
            bitmap.bitarray.append( tmparray[:] )

    bitmap.saveFile( filename )

if __name__ == "__main__":

  bmp = BitMap( 10, 10 )
  bmp.plotPoint( 5, 5 )
  bmp.plotPoint( 0, 0 )
  bmp.saveFile( "test.bmp" )
<|endoftext|>


<commit_before>#!/usr/bin/python3
import sys
import time
from md01 import *
import curses

def curses_loop(tels, target):
    stdscr = curses.initscr()
    loop = True
    cw = 12 # column width
    stdscr.nodelay(True) # Don't wait for user input
    while loop:
        # Sleep 0.5s between reading and sending commands
        time.sleep(0.5)
        # Display info on screen...
        stdscr.clear()
        stdscr.addstr(0, 0, "UTC now   | ", curses.A_BOLD)
        stdscr.addstr(1, 0, "TELESCOPE | ", curses.A_BOLD)
        stdscr.addstr(2, 0, "Cur. Alt. | ", curses.A_BOLD)
        stdscr.addstr(3, 0, "Cur. Az.  | ", curses.A_BOLD)
        stdscr.addstr(4, 0, "Tar. Alt. | ", curses.A_BOLD)
        stdscr.addstr(5, 0, "Tar. Az.  | ", curses.A_BOLD)
        stdscr.addstr(6, 0, "TRACKING: | ", curses.A_BOLD)
        ## Some space
        stdscr.addstr(10, 0, "Available commands: ", curses.A_BOLD)
        stdscr.addstr(11, 0, "s - stop telescopes")
        stdscr.addstr(12, 0, "q - stop telescopes and quit program")
        stdscr.addstr(13, 0, "g - Track new Galactic (GLON/GLAT) position")
        stdscr.addstr(14, 0, "h - Track new Horizontal (ALT/AZ) direction ")
        stdscr.addstr(15, 0, "i - Track the Sun")
        # Set datetime info
        utcnow = time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime())
        stdscr.addstr(0, cw, utcnow)
        # Print tel names and current position
        for i, tel in enumerate(tels):
            stdscr.addstr(1, cw * (1+i), tel.site.name, curses.A_UNDERLINE)
            (cal, caz) = tel.get_current_alaz()
            stdscr.addstr(2, cw * (1+i), "{}".format(cal))
            stdscr.addstr(3, cw * (1+i), "{}".format(caz))
        # Sleep 0.5s between reading and sending commands
        time.sleep(0.5)

        # Set target info
        if target[0]=="":
            stdscr.addstr(6, cw, "N/A")
            tal = ""
            taz = ""
            for i, tel in enumerate(tels):
                # Print target alt/az
                stdscr.addstr(4, cw * (1+i), "{}".format(tal))
                stdscr.addstr(5, cw * (1+i), "{}".format(taz))
        elif target[0]=="HOR":
            stdscr.addstr(6, cw, "HORIZONTAL")
            tal = target[1]
            taz = target[2]
            for i, tel in enumerate(tels):
                # Print target alt/az
                stdscr.addstr(4, cw * (1+i), "{}".format(tal))
                stdscr.addstr(5, cw * (1+i), "{}".format(taz))
        elif target[0]=="GAL":
            glon = target[1]
            glat = target[2]
            stdscr.addstr(6, cw, "GALACTIC LON={}, LAT={}".format(glon, glat))
            for i, tel in enumerate(tels):
                # Don't get current pos, instead get desired
                tal,taz = tel.get_desired_alaz(target)
                # Move all telescopes
                tel.move(tal, taz)
                # Print target alt/az from now or last loop
                stdscr.addstr(4, cw * (1+i), "{}".format(tal))
                stdscr.addstr(5, cw * (1+i), "{}".format(taz))
        elif target[0]=="SUN":
            glon = target[1]
            glat = target[2]
            stdscr.addstr(6, cw, "The Sun")
            for i, tel in enumerate(tels):
                # Don't get current pos, instead get desired
                tal,taz = tel.get_desired_alaz(target)
                # Move all telescopes
                tel.move(tal, taz)
                # Print target alt/az from now or last loop
                stdscr.addstr(4, cw * (1+i), "{}".format(tal))
                stdscr.addstr(5, cw * (1+i), "{}".format(taz))

        # Handle user input
        try:
            key = stdscr.getkey()
            if key.lower()=="s":
                # Stop all tels
                for i, tel in enumerate(tels):
                    tel.stop()
                target = ["", "", ""]
            elif key.lower()=="q":
                # Stop all tels
                for i, tel in enumerate(tels):
                    tel.stop()
                stdscr.clear()
                stdscr.refresh()
                loop = False
            elif key.lower()=="g" or key.lower()=="h" or key.lower()=="i":
                # Quit this loop to get new target from user
                stdscr.clear()
                stdscr.refresh()
                loop = False
        except:
            pass
        stdscr.refresh()
    curses.endwin()

    # Return user input to control loop
    return key.lower()

def control_loop(tels):
    # Enable curses loop
    cloop = True
    # Initialize target to empty string
    target = ["","",""]
    while cloop:
        # Run curses loop awaiting user input
        ans = curses_loop(tels, target)
        # We got user input, deal with it...
        if ans == "g":
            glon = float(input("Please enter target galactic longitude in decimal degrees e.g. 80.5 :"))
            glat = float(input("Please enter target galactic latitude in decimal degrees e.g. 0.0 :"))
            conf = input("Are you sure you want to track glon={} glat={} deg ? [Yes/No]".format(glon,glat))
            if conf.lower()=="yes":
                target = ["GAL", glon, glat]
                # Stop all tels
                for i, tel in enumerate(tels):
                    tel.stop()
                time.sleep(1.0)
                # Movement done by curses_loop in this case
        if ans == "h":
            az = float(input("Please enter target azimuth in decimal degrees e.g. 148.5 :"))
            al = float(input("Please enter target altitude in decimal degrees e.g. 43.5 :"))
            conf = input("Are you sure you want to track alt={} az={} deg ? [Yes/No]".format(al, az))
            if conf.lower()=="yes":
                target = ["HOR", al,az]
                # Stop all tels
                for i, tel in enumerate(tels):
                    tel.stop()
                time.sleep(1.0)
                # Move all telescopes, started here to simplify code (but yes, ugly place)
                for i, tel in enumerate(tels):
                    tel.move(al, az)
        if ans == "i":
            conf = input("Are you sure you want to track the Sun ? [Yes/No]")
            if conf.lower()=="yes":
                target = ["SUN", "", ""]
                # Stop all tels
                for i, tel in enumerate(tels):
                    tel.stop()
                time.sleep(1.0)
                # Movement done by curses_loop in this case
        elif ans == "q":
            # Quit program
            cloop = False

if __name__ == '__main__':
    # Assume list of config files given as script arguments on command line,
    # so we run "python3 script.py tel1.conf tel2.conf ..."
    # Each config file reflects a telescope to be controlled
    configs = sys.argv[1:]
    tels = []

    # Create one MD01 object for each config file (each telescope)
    for cf in configs:
        tels.append(MD01(cf))
    # Start control loop
    control_loop(tels)
<commit_msg>Update main.py<commit_after>#!/usr/bin/python3
import sys
import time
from md01 import *
import curses

def curses_loop(tels, target):
    stdscr = curses.initscr()
    loop = True
    cw = 12 # column width
    stdscr.nodelay(True) # Don't wait for user input
    while loop:
        # Sleep 0.5s between reading and sending commands
        time.sleep(0.5)
        # Display info on screen...
        stdscr.clear()
        stdscr.addstr(0, 0, "UTC now   | ", curses.A_BOLD)
        stdscr.addstr(1, 0, "TELESCOPE | ", curses.A_BOLD)
        stdscr.addstr(2, 0, "Cur. Alt. | ", curses.A_BOLD)
        stdscr.addstr(3, 0, "Cur. Az.  | ", curses.A_BOLD)
        stdscr.addstr(4, 0, "Tar. Alt. | ", curses.A_BOLD)
        stdscr.addstr(5, 0, "Tar. Az.  | ", curses.A_BOLD)
        stdscr.addstr(6, 0, "TRACKING: | ", curses.A_BOLD)
        ## Some space
        stdscr.addstr(10, 0, "Available commands: ", curses.A_BOLD)
        stdscr.addstr(11, 0, "s - stop telescopes")
        stdscr.addstr(12, 0, "q - stop telescopes and quit program")
        stdscr.addstr(13, 0, "g - Track new Galactic (GLON/GLAT) position")
        stdscr.addstr(14, 0, "h - Track new Horizontal (ALT/AZ) direction ")
        stdscr.addstr(15, 0, "i - Track the Sun")
        # Set datetime info
        utcnow = time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime())
        stdscr.addstr(0, cw, utcnow)
        # Print tel names and current position
        for i, tel in enumerate(tels):
            stdscr.addstr(1, cw * (1+i), tel.site.name, curses.A_UNDERLINE)
            (cal, caz) = tel.get_current_alaz()
            stdscr.addstr(2, cw * (1+i), "{}".format(cal))
            stdscr.addstr(3, cw * (1+i), "{}".format(caz))
        # Sleep 0.5s between reading and sending commands
        time.sleep(0.5)

        # Set target info
        if target[0]=="":
            stdscr.addstr(6, cw, "N/A")
            tal = ""
            taz = ""
            for i, tel in enumerate(tels):
                # Print target alt/az
                stdscr.addstr(4, cw * (1+i), "{}".format(tal))
                stdscr.addstr(5, cw * (1+i), "{}".format(taz))
        elif target[0]=="HOR":
            stdscr.addstr(6, cw, "HORIZONTAL")
            tal = target[1]
            taz = target[2]
            for i, tel in enumerate(tels):
                # Print target alt/az
                stdscr.addstr(4, cw * (1+i), "{}".format(tal))
                stdscr.addstr(5, cw * (1+i), "{}".format(taz))
        elif target[0]=="GAL":
            glon = target[1]
            glat = target[2]
            stdscr.addstr(6, cw, "GALACTIC LON={}, LAT={}".format(glon, glat))
            for i, tel in enumerate(tels):
                # Don't get current pos, instead get desired
                tal,taz = tel.get_desired_alaz(target)
                # Move all telescopes
                tel.move(tal, taz)
                # Print target alt/az from now or last loop
                stdscr.addstr(4, cw * (1+i), "{}".format(tal))
                stdscr.addstr(5, cw * (1+i), "{}".format(taz))
        elif target[0]=="SUN":
            stdscr.addstr(6, cw, "The Sun")
            for i, tel in enumerate(tels):
                # Don't get current pos, instead get desired
                tal,taz = tel.get_desired_alaz(target)
                # Move all telescopes
                tel.move(tal, taz)
                # Print target alt/az from now or last loop
                stdscr.addstr(4, cw * (1+i), "{}".format(tal))
                stdscr.addstr(5, cw * (1+i), "{}".format(taz))

        # Handle user input
        try:
            key = stdscr.getkey().lower()
            if key=="s":
                # Stop all tels
                for i, tel in enumerate(tels):
                    tel.stop()
                target = ["", "", ""]
            elif key=="q":
                # Stop all tels
                for i, tel in enumerate(tels):
                    tel.stop()
                stdscr.clear()
                stdscr.refresh()
                loop = False
            elif key=="g" or key=="h" or key=="i":
                # Quit this loop to get new target from user
                stdscr.clear()
                stdscr.refresh()
                loop = False
        except:
            pass
        stdscr.refresh()
    curses.endwin()

    # Return user input to control loop
    return key

def control_loop(tels):
    # Enable curses loop
    cloop = True
    # Initialize target to empty string
    target = ["","",""]
    while cloop:
        # Run curses loop awaiting user input
        ans = curses_loop(tels, target)
        # We got user input, deal with it...
        if ans == "g":
            glon = float(input("Please enter target galactic longitude in decimal degrees e.g. 80.5 :"))
            glat = float(input("Please enter target galactic latitude in decimal degrees e.g. 0.0 :"))
            conf = input("Are you sure you want to track glon={} glat={} deg ? [Yes/No]".format(glon,glat))
            if conf.lower()=="yes":
                target = ["GAL", glon, glat]
                # Stop all tels
                for i, tel in enumerate(tels):
                    tel.stop()
                time.sleep(1.0)
                # Movement done by curses_loop in this case
        if ans == "h":
            az = float(input("Please enter target azimuth in decimal degrees e.g. 148.5 :"))
            al = float(input("Please enter target altitude in decimal degrees e.g. 43.5 :"))
            conf = input("Are you sure you want to track alt={} az={} deg ? [Yes/No]".format(al, az))
            if conf.lower()=="yes":
                target = ["HOR", al,az]
                # Stop all tels
                for i, tel in enumerate(tels):
                    tel.stop()
                time.sleep(1.0)
                # Move all telescopes, started here to simplify code (but yes, ugly place)
                for i, tel in enumerate(tels):
                    tel.move(al, az)
        if ans == "i":
            conf = input("Are you sure you want to track the Sun ? [Yes/No]")
            if conf.lower()=="yes":
                target = ["SUN", "", ""]
                # Stop all tels
                for i, tel in enumerate(tels):
                    tel.stop()
                time.sleep(1.0)
                # Movement done by curses_loop in this case
        elif ans == "q":
            # Quit program
            cloop = False

if __name__ == '__main__':
    # Assume list of config files given as script arguments on command line,
    # so we run "python3 script.py tel1.conf tel2.conf ..."
    # Each config file reflects a telescope to be controlled
    configs = sys.argv[1:]
    tels = []

    # Create one MD01 object for each config file (each telescope)
    for cf in configs:
        tels.append(MD01(cf))
    # Start control loop
    control_loop(tels)
<|endoftext|>


<commit_before>from __future__ import absolute_import
from decimal import Decimal

from django.conf import settings
from django.db import transaction
from django.utils.translation import ugettext as _

import stripe

from corehq.apps.accounting.models import (
    BillingAccount,
    CreditLine,
    Invoice,
    PaymentRecord,
    PreOrPostPay,
    StripePaymentMethod,
    LastPayment
)
from corehq.apps.accounting.utils import (
    fmt_dollar_amount,
    log_accounting_error,
    log_accounting_info,
)
from corehq.const import USER_DATE_FORMAT

stripe.api_key = settings.STRIPE_PRIVATE_KEY

class BaseStripePaymentHandler(object):
    """Handler for paying via Stripe's API
    """
    receipt_email_template = None
    receipt_email_template_plaintext = None

    def __init__(self, payment_method, domain):
        self.payment_method = payment_method
        self.domain = domain

    @property
    def cost_item_name(self):
        """Returns a name for the cost item that's used in the logging messages.
        """
        raise NotImplementedError("you must implement cost_item_name")

    def create_charge(self, amount, card=None, customer=None):
        """Process the HTTPRequest used to make this payment

        returns a dict to be used as the json response for the request.
        """
        raise NotImplementedError("you must implement process_request")

    def get_charge_amount(self, request):
        """Returns a Decimal of the amount to be charged.
        """
        raise NotImplementedError("you must implement get_charge_amount")

    def update_credits(self, payment_record):
        """Updates any relevant Credit lines
        """
        raise NotImplementedError("you must implement update_credits")

    @staticmethod
    def get_amount_in_cents(amount):
        amt_cents = amount * Decimal('100')
        return int(amt_cents.quantize(Decimal(10)))

    def update_payment_information(self, account):
        account.last_payment_method = LastPayment.CC_ONE_TIME
        account.pre_or_post_pay = PreOrPostPay.POSTPAY
        account.save()

    def process_request(self, request):
        customer = None
        amount = self.get_charge_amount(request)
        card = request.POST.get('stripeToken')
        remove_card = request.POST.get('removeCard')
        is_saved_card = request.POST.get('selectedCardType') == 'saved'
        save_card = request.POST.get('saveCard') and not is_saved_card
        autopay = request.POST.get('autopayCard')
        billing_account = BillingAccount.get_account_by_domain(self.domain)
        generic_error = {
            'error': {
                'message': _(
                    "Something went wrong while processing your payment. "
                    "We're working quickly to resolve the issue. No charges "
                    "were issued. Please try again in a few hours."
                ),
            },
        }
        try:
            with transaction.atomic():
                if remove_card:
                    self.payment_method.remove_card(card)
                    return {'success': True, 'removedCard': card, }
                if save_card:
                    card = self.payment_method.create_card(card, billing_account, self.domain, autopay=autopay)
                if save_card or is_saved_card:
                    customer = self.payment_method.customer

                payment_record = PaymentRecord.create_record(
                    self.payment_method, 'temp', amount
                )
                self.update_credits(payment_record)

                charge = self.create_charge(amount, card=card, customer=customer)

            payment_record.transaction_id = charge.id
            payment_record.save()
            self.update_payment_information(billing_account)
        except stripe.error.CardError as e:
            # card was declined
            return e.json_body
        except (
            stripe.error.AuthenticationError,
            stripe.error.InvalidRequestError,
            stripe.error.APIConnectionError,
            stripe.error.StripeError,
        ) as e:
            log_accounting_error(
                "A payment for %(cost_item)s failed due "
                "to a Stripe %(error_class)s: %(error_msg)s" % {
                    'error_class': e.__class__.__name__,
                    'cost_item': self.cost_item_name,
                    'error_msg': e.json_body['error']
                },
                show_stack_trace=True,
            )
            return generic_error
        except Exception as e:
            log_accounting_error(
                "A payment for %(cost_item)s failed due to: %(error_msg)s" % {
                    'cost_item': self.cost_item_name,
                    'error_msg': e,
                },
                show_stack_trace=True,
            )
            return generic_error

        try:
            self.send_email(payment_record)
        except Exception:
            log_accounting_error(
                "Failed to send out an email receipt for "
                "payment related to PaymentRecord No. %s. "
                "Everything else succeeded."
                % payment_record.id,
                show_stack_trace=True,
            )

        return {
            'success': True,
            'card': card,
            'wasSaved': save_card,
            'changedBalance': amount,
        }

    def get_email_context(self):
        return {
            'invoicing_contact_email': settings.INVOICING_CONTACT_EMAIL,
        }

    def send_email(self, payment_record):
        additional_context = self.get_email_context()
        from corehq.apps.accounting.tasks import send_purchase_receipt
        send_purchase_receipt.delay(
            payment_record, self.domain, self.receipt_email_template,
            self.receipt_email_template_plaintext, additional_context
        )

class InvoiceStripePaymentHandler(BaseStripePaymentHandler):
    receipt_email_template = 'accounting/invoice_receipt_email.html'
    receipt_email_template_plaintext = 'accounting/invoice_receipt_email_plaintext.txt'

    def __init__(self, payment_method, domain, invoice):
        super(InvoiceStripePaymentHandler, self).__init__(payment_method, domain)
        self.invoice = invoice

    @property
    def cost_item_name(self):
        return _("Invoice #%s") % self.invoice.id

    def create_charge(self, amount, card=None, customer=None):
        return stripe.Charge.create(
            card=card,
            customer=customer,
            amount=self.get_amount_in_cents(amount),
            currency=settings.DEFAULT_CURRENCY,
            description="Payment for Invoice %s" % self.invoice.invoice_number,
        )

    def get_charge_amount(self, request):
        """Returns a Decimal of the amount to be charged.
        """
        if request.POST['paymentAmount'] == 'full':
            return self.invoice.balance.quantize(Decimal(10) ** -2)
        return Decimal(request.POST['customPaymentAmount'])

    def update_credits(self, payment_record):
        # record the credit to the account
        CreditLine.add_credit(
            payment_record.amount, account=self.invoice.subscription.account,
            payment_record=payment_record,
        )
        CreditLine.add_credit(
            -payment_record.amount,
            account=self.invoice.subscription.account,
            invoice=self.invoice,
        )
        self.invoice.update_balance()
        self.invoice.save()

    def get_email_context(self):
        context = super(InvoiceStripePaymentHandler, self).get_email_context()
        context.update({
            'balance': fmt_dollar_amount(self.invoice.balance),
            'is_paid': self.invoice.is_paid,
            'date_due': self.invoice.date_due.strftime(USER_DATE_FORMAT) if self.invoice.date_due else 'None',
            'invoice_num': self.invoice.invoice_number,
        })
        return context

class BulkStripePaymentHandler(BaseStripePaymentHandler):
    receipt_email_template = 'accounting/bulk_payment_receipt_email.html'
    receipt_email_template_plaintext = 'accounting/bulk_payment_receipt_email_plaintext.txt'

    def __init__(self, payment_method, domain):
        super(BulkStripePaymentHandler, self).__init__(payment_method, domain)

    @property
    def cost_item_name(self):
        return _('Bulk Payment for project space %s' % self.domain)

    def create_charge(self, amount, card=None, customer=None):
        return stripe.Charge.create(
            card=card,
            customer=customer,
            amount=self.get_amount_in_cents(amount),
            currency=settings.DEFAULT_CURRENCY,
            description=self.cost_item_name,
        )

    @property
    def invoices(self):
        return Invoice.objects.filter(
            subscription__subscriber__domain=self.domain,
            is_hidden=False,
        )

    @property
    def balance(self):
        return sum(invoice.balance for invoice in self.invoices)

    def get_charge_amount(self, request):
        if request.POST['paymentAmount'] == 'full':
            return self.balance
        return Decimal(request.POST['customPaymentAmount'])

    def update_credits(self, payment_record):
        amount = payment_record.amount
        for invoice in self.invoices:
            deduct_amount = min(amount, invoice.balance)
            amount -= deduct_amount
            if deduct_amount > 0:
                # TODO - refactor duplicated functionality
                CreditLine.add_credit(
                    deduct_amount, account=invoice.subscription.account,
                    payment_record=payment_record,
                )
                CreditLine.add_credit(
                    -deduct_amount,
                    account=invoice.subscription.account,
                    invoice=invoice,
                )
                invoice.update_balance()
                invoice.save()
        if amount:
            account = BillingAccount.get_or_create_account_by_domain(self.domain)
            CreditLine.add_credit(
                amount, account=account,
                payment_record=payment_record,
            )

    def get_email_context(self):
        context = super(BulkStripePaymentHandler, self).get_email_context()
        context.update({
            'is_paid': all(invoice.is_paid for invoice in self.invoices),
            'domain': self.domain,
            'balance': self.balance,
        })
        return context

class CreditStripePaymentHandler(BaseStripePaymentHandler):
    receipt_email_template = 'accounting/credit_receipt_email.html'
    receipt_email_template_plaintext = 'accounting/credit_receipt_email_plaintext.txt'

    def __init__(self, payment_method, domain, account, subscription=None, post_data=None):
        super(CreditStripePaymentHandler, self).__init__(payment_method, domain)
        if Decimal(post_data.get('general_credit', 0)) > 0:
            self.general_credits = {
                'type': 'general_credit',
                'amount': Decimal(post_data.get('general_credit', 0))
            }
        else:
            self.general_credits = None
        self.post_data = post_data
        self.account = account
        self.subscription = subscription
        self.credit_lines = []

    @property
    def cost_item_name(self):
        return str(self.subscription)

    def get_charge_amount(self, request):
        return Decimal(request.POST['amount'])

    def create_charge(self, amount, card=None, customer=None):
        return stripe.Charge.create(
            card=card,
            customer=customer,
            amount=self.get_amount_in_cents(amount),
            currency=settings.DEFAULT_CURRENCY,
            description="Payment for %s" % self.cost_item_name,
        )

    def update_payment_information(self, account):
        account.last_payment_method = LastPayment.CC_ONE_TIME
        account.pre_or_post_pay = PreOrPostPay.PREPAY
        account.save()

    def update_credits(self, payment_record):
        if self.general_credits:
            amount = self.general_credits['amount']
            if amount >= 0.5:
                self.credit_lines.append(CreditLine.add_credit(
                    amount,
                    account=self.account,
                    subscription=self.subscription,
                    payment_record=payment_record,
                ))
            else:
                log_accounting_error(
                    "{account} tried to make a payment for Credits for less than $0.5."
                    "You should follow up with them."
                    .format(
                        account=self.account,
                    )
                )

    def process_request(self, request):
        response = super(CreditStripePaymentHandler, self).process_request(request)
        if self.credit_lines:
            response.update({
                'balances': [{'type': cline.product_type if cline.product_type else cline.feature_type,
                              'balance': fmt_dollar_amount(cline.balance)}
                             for cline in self.credit_lines]
            })
        return response

class AutoPayInvoicePaymentHandler(object):

    def pay_autopayable_invoices(self, date_due):
        """ Pays the full balance of all autopayable invoices on date_due """
        autopayable_invoices = Invoice.autopayable_invoices(date_due)
        for invoice in autopayable_invoices:
            try:
                self._pay_invoice(invoice)
            except Exception as e:
                log_accounting_error("Error autopaying invoice %d: %s" % (invoice.id, e.message))

    def _pay_invoice(self, invoice):
        log_accounting_info("[Autopay] Autopaying invoice {}".format(invoice.id))
        amount = invoice.balance.quantize(Decimal(10) ** -2)
        if not amount:
            return

        auto_payer = invoice.subscription.account.auto_pay_user
        payment_method = StripePaymentMethod.objects.get(web_user=auto_payer)
        autopay_card = payment_method.get_autopay_card(invoice.subscription.account)
        if autopay_card is None:
            return

        try:
            with transaction.atomic():
                payment_record = PaymentRecord.create_record(payment_method, 'temp_transaction_id', amount)
                invoice.pay_invoice(payment_record)
                invoice.subscription.account.last_payment_method = LastPayment.CC_AUTO
                invoice.account.save()
                transaction_id = payment_method.create_charge(
                    autopay_card,
                    amount_in_dollars=amount,
                    description='Auto-payment for Invoice %s' % invoice.invoice_number,
                )
        except stripe.error.CardError:
            self._handle_card_declined(invoice, payment_method)
        except payment_method.STRIPE_GENERIC_ERROR as e:
            self._handle_card_errors(invoice, e)
        else:
            payment_record.transaction_id = transaction_id
            payment_record.save()
            self._send_payment_receipt(invoice, payment_record)

    def _send_payment_receipt(self, invoice, payment_record):
        from corehq.apps.accounting.tasks import send_purchase_receipt
        receipt_email_template = 'accounting/invoice_receipt_email.html'
        receipt_email_template_plaintext = 'accounting/invoice_receipt_email_plaintext.txt'
        try:
            domain = invoice.subscription.subscriber.domain
            context = {
                'invoicing_contact_email': settings.INVOICING_CONTACT_EMAIL,
                'balance': fmt_dollar_amount(invoice.balance),
                'is_paid': invoice.is_paid,
                'date_due': invoice.date_due.strftime(USER_DATE_FORMAT) if invoice.date_due else 'None',
                'invoice_num': invoice.invoice_number,
            }
            send_purchase_receipt.delay(
                payment_record, domain, receipt_email_template, receipt_email_template_plaintext, context,
            )
        except:
            self._handle_email_failure(payment_record)

    @staticmethod
    def _handle_card_declined(invoice, payment_method):
        from corehq.apps.accounting.tasks import send_autopay_failed
        log_accounting_error(
            "[Autopay] An automatic payment failed for invoice: {} "
            "because the card was declined. This invoice will not be automatically paid. "
            "Not necessarily actionable, but be aware that this happened."
            .format(invoice.id)
        )
        send_autopay_failed.delay(invoice, payment_method)

    @staticmethod
    def _handle_card_errors(invoice, e):
        log_accounting_error(
            "[Autopay] An automatic payment failed for invoice: {invoice} "
            "because the of {error}. This invoice will not be automatically paid."
            .format(invoice=invoice.id, error=e)
        )

    @staticmethod
    def _handle_email_failure(payment_record):
        log_accounting_error(
            "[Autopay] During an automatic payment, sending a payment receipt failed"
            " for Payment Record: {}. Everything else succeeded"
            .format(payment_record.id)
        )
<commit_msg>include more info about autopay errors<commit_after>from __future__ import absolute_import
from decimal import Decimal

from django.conf import settings
from django.db import transaction
from django.utils.translation import ugettext as _

import stripe

from corehq.apps.accounting.models import (
    BillingAccount,
    CreditLine,
    Invoice,
    PaymentRecord,
    PreOrPostPay,
    StripePaymentMethod,
    LastPayment
)
from corehq.apps.accounting.utils import (
    fmt_dollar_amount,
    log_accounting_error,
    log_accounting_info,
)
from corehq.const import USER_DATE_FORMAT

stripe.api_key = settings.STRIPE_PRIVATE_KEY

class BaseStripePaymentHandler(object):
    """Handler for paying via Stripe's API
    """
    receipt_email_template = None
    receipt_email_template_plaintext = None

    def __init__(self, payment_method, domain):
        self.payment_method = payment_method
        self.domain = domain

    @property
    def cost_item_name(self):
        """Returns a name for the cost item that's used in the logging messages.
        """
        raise NotImplementedError("you must implement cost_item_name")

    def create_charge(self, amount, card=None, customer=None):
        """Process the HTTPRequest used to make this payment

        returns a dict to be used as the json response for the request.
        """
        raise NotImplementedError("you must implement process_request")

    def get_charge_amount(self, request):
        """Returns a Decimal of the amount to be charged.
        """
        raise NotImplementedError("you must implement get_charge_amount")

    def update_credits(self, payment_record):
        """Updates any relevant Credit lines
        """
        raise NotImplementedError("you must implement update_credits")

    @staticmethod
    def get_amount_in_cents(amount):
        amt_cents = amount * Decimal('100')
        return int(amt_cents.quantize(Decimal(10)))

    def update_payment_information(self, account):
        account.last_payment_method = LastPayment.CC_ONE_TIME
        account.pre_or_post_pay = PreOrPostPay.POSTPAY
        account.save()

    def process_request(self, request):
        customer = None
        amount = self.get_charge_amount(request)
        card = request.POST.get('stripeToken')
        remove_card = request.POST.get('removeCard')
        is_saved_card = request.POST.get('selectedCardType') == 'saved'
        save_card = request.POST.get('saveCard') and not is_saved_card
        autopay = request.POST.get('autopayCard')
        billing_account = BillingAccount.get_account_by_domain(self.domain)
        generic_error = {
            'error': {
                'message': _(
                    "Something went wrong while processing your payment. "
                    "We're working quickly to resolve the issue. No charges "
                    "were issued. Please try again in a few hours."
                ),
            },
        }
        try:
            with transaction.atomic():
                if remove_card:
                    self.payment_method.remove_card(card)
                    return {'success': True, 'removedCard': card, }
                if save_card:
                    card = self.payment_method.create_card(card, billing_account, self.domain, autopay=autopay)
                if save_card or is_saved_card:
                    customer = self.payment_method.customer

                payment_record = PaymentRecord.create_record(
                    self.payment_method, 'temp', amount
                )
                self.update_credits(payment_record)

                charge = self.create_charge(amount, card=card, customer=customer)

            payment_record.transaction_id = charge.id
            payment_record.save()
            self.update_payment_information(billing_account)
        except stripe.error.CardError as e:
            # card was declined
            return e.json_body
        except (
            stripe.error.AuthenticationError,
            stripe.error.InvalidRequestError,
            stripe.error.APIConnectionError,
            stripe.error.StripeError,
        ) as e:
            log_accounting_error(
                "A payment for %(cost_item)s failed due "
                "to a Stripe %(error_class)s: %(error_msg)s" % {
                    'error_class': e.__class__.__name__,
                    'cost_item': self.cost_item_name,
                    'error_msg': e.json_body['error']
                },
                show_stack_trace=True,
            )
            return generic_error
        except Exception as e:
            log_accounting_error(
                "A payment for %(cost_item)s failed due to: %(error_msg)s" % {
                    'cost_item': self.cost_item_name,
                    'error_msg': e,
                },
                show_stack_trace=True,
            )
            return generic_error

        try:
            self.send_email(payment_record)
        except Exception:
            log_accounting_error(
                "Failed to send out an email receipt for "
                "payment related to PaymentRecord No. %s. "
                "Everything else succeeded."
                % payment_record.id,
                show_stack_trace=True,
            )

        return {
            'success': True,
            'card': card,
            'wasSaved': save_card,
            'changedBalance': amount,
        }

    def get_email_context(self):
        return {
            'invoicing_contact_email': settings.INVOICING_CONTACT_EMAIL,
        }

    def send_email(self, payment_record):
        additional_context = self.get_email_context()
        from corehq.apps.accounting.tasks import send_purchase_receipt
        send_purchase_receipt.delay(
            payment_record, self.domain, self.receipt_email_template,
            self.receipt_email_template_plaintext, additional_context
        )

class InvoiceStripePaymentHandler(BaseStripePaymentHandler):
    receipt_email_template = 'accounting/invoice_receipt_email.html'
    receipt_email_template_plaintext = 'accounting/invoice_receipt_email_plaintext.txt'

    def __init__(self, payment_method, domain, invoice):
        super(InvoiceStripePaymentHandler, self).__init__(payment_method, domain)
        self.invoice = invoice

    @property
    def cost_item_name(self):
        return _("Invoice #%s") % self.invoice.id

    def create_charge(self, amount, card=None, customer=None):
        return stripe.Charge.create(
            card=card,
            customer=customer,
            amount=self.get_amount_in_cents(amount),
            currency=settings.DEFAULT_CURRENCY,
            description="Payment for Invoice %s" % self.invoice.invoice_number,
        )

    def get_charge_amount(self, request):
        """Returns a Decimal of the amount to be charged.
        """
        if request.POST['paymentAmount'] == 'full':
            return self.invoice.balance.quantize(Decimal(10) ** -2)
        return Decimal(request.POST['customPaymentAmount'])

    def update_credits(self, payment_record):
        # record the credit to the account
        CreditLine.add_credit(
            payment_record.amount, account=self.invoice.subscription.account,
            payment_record=payment_record,
        )
        CreditLine.add_credit(
            -payment_record.amount,
            account=self.invoice.subscription.account,
            invoice=self.invoice,
        )
        self.invoice.update_balance()
        self.invoice.save()

    def get_email_context(self):
        context = super(InvoiceStripePaymentHandler, self).get_email_context()
        context.update({
            'balance': fmt_dollar_amount(self.invoice.balance),
            'is_paid': self.invoice.is_paid,
            'date_due': self.invoice.date_due.strftime(USER_DATE_FORMAT) if self.invoice.date_due else 'None',
            'invoice_num': self.invoice.invoice_number,
        })
        return context

class BulkStripePaymentHandler(BaseStripePaymentHandler):
    receipt_email_template = 'accounting/bulk_payment_receipt_email.html'
    receipt_email_template_plaintext = 'accounting/bulk_payment_receipt_email_plaintext.txt'

    def __init__(self, payment_method, domain):
        super(BulkStripePaymentHandler, self).__init__(payment_method, domain)

    @property
    def cost_item_name(self):
        return _('Bulk Payment for project space %s' % self.domain)

    def create_charge(self, amount, card=None, customer=None):
        return stripe.Charge.create(
            card=card,
            customer=customer,
            amount=self.get_amount_in_cents(amount),
            currency=settings.DEFAULT_CURRENCY,
            description=self.cost_item_name,
        )

    @property
    def invoices(self):
        return Invoice.objects.filter(
            subscription__subscriber__domain=self.domain,
            is_hidden=False,
        )

    @property
    def balance(self):
        return sum(invoice.balance for invoice in self.invoices)

    def get_charge_amount(self, request):
        if request.POST['paymentAmount'] == 'full':
            return self.balance
        return Decimal(request.POST['customPaymentAmount'])

    def update_credits(self, payment_record):
        amount = payment_record.amount
        for invoice in self.invoices:
            deduct_amount = min(amount, invoice.balance)
            amount -= deduct_amount
            if deduct_amount > 0:
                # TODO - refactor duplicated functionality
                CreditLine.add_credit(
                    deduct_amount, account=invoice.subscription.account,
                    payment_record=payment_record,
                )
                CreditLine.add_credit(
                    -deduct_amount,
                    account=invoice.subscription.account,
                    invoice=invoice,
                )
                invoice.update_balance()
                invoice.save()
        if amount:
            account = BillingAccount.get_or_create_account_by_domain(self.domain)
            CreditLine.add_credit(
                amount, account=account,
                payment_record=payment_record,
            )

    def get_email_context(self):
        context = super(BulkStripePaymentHandler, self).get_email_context()
        context.update({
            'is_paid': all(invoice.is_paid for invoice in self.invoices),
            'domain': self.domain,
            'balance': self.balance,
        })
        return context

class CreditStripePaymentHandler(BaseStripePaymentHandler):
    receipt_email_template = 'accounting/credit_receipt_email.html'
    receipt_email_template_plaintext = 'accounting/credit_receipt_email_plaintext.txt'

    def __init__(self, payment_method, domain, account, subscription=None, post_data=None):
        super(CreditStripePaymentHandler, self).__init__(payment_method, domain)
        if Decimal(post_data.get('general_credit', 0)) > 0:
            self.general_credits = {
                'type': 'general_credit',
                'amount': Decimal(post_data.get('general_credit', 0))
            }
        else:
            self.general_credits = None
        self.post_data = post_data
        self.account = account
        self.subscription = subscription
        self.credit_lines = []

    @property
    def cost_item_name(self):
        return str(self.subscription)

    def get_charge_amount(self, request):
        return Decimal(request.POST['amount'])

    def create_charge(self, amount, card=None, customer=None):
        return stripe.Charge.create(
            card=card,
            customer=customer,
            amount=self.get_amount_in_cents(amount),
            currency=settings.DEFAULT_CURRENCY,
            description="Payment for %s" % self.cost_item_name,
        )

    def update_payment_information(self, account):
        account.last_payment_method = LastPayment.CC_ONE_TIME
        account.pre_or_post_pay = PreOrPostPay.PREPAY
        account.save()

    def update_credits(self, payment_record):
        if self.general_credits:
            amount = self.general_credits['amount']
            if amount >= 0.5:
                self.credit_lines.append(CreditLine.add_credit(
                    amount,
                    account=self.account,
                    subscription=self.subscription,
                    payment_record=payment_record,
                ))
            else:
                log_accounting_error(
                    "{account} tried to make a payment for Credits for less than $0.5."
                    "You should follow up with them."
                    .format(
                        account=self.account,
                    )
                )

    def process_request(self, request):
        response = super(CreditStripePaymentHandler, self).process_request(request)
        if self.credit_lines:
            response.update({
                'balances': [{'type': cline.product_type if cline.product_type else cline.feature_type,
                              'balance': fmt_dollar_amount(cline.balance)}
                             for cline in self.credit_lines]
            })
        return response

class AutoPayInvoicePaymentHandler(object):

    def pay_autopayable_invoices(self, date_due):
        """ Pays the full balance of all autopayable invoices on date_due """
        autopayable_invoices = Invoice.autopayable_invoices(date_due)
        for invoice in autopayable_invoices:
            try:
                self._pay_invoice(invoice)
            except Exception as e:
                log_accounting_error("Error autopaying invoice %d: %s" % (invoice.id, e.message))

    def _pay_invoice(self, invoice):
        log_accounting_info("[Autopay] Autopaying invoice {}".format(invoice.id))
        amount = invoice.balance.quantize(Decimal(10) ** -2)
        if not amount:
            return

        auto_payer = invoice.subscription.account.auto_pay_user
        payment_method = StripePaymentMethod.objects.get(web_user=auto_payer)
        autopay_card = payment_method.get_autopay_card(invoice.subscription.account)
        if autopay_card is None:
            return

        try:
            with transaction.atomic():
                payment_record = PaymentRecord.create_record(payment_method, 'temp_transaction_id', amount)
                invoice.pay_invoice(payment_record)
                invoice.subscription.account.last_payment_method = LastPayment.CC_AUTO
                invoice.account.save()
                transaction_id = payment_method.create_charge(
                    autopay_card,
                    amount_in_dollars=amount,
                    description='Auto-payment for Invoice %s' % invoice.invoice_number,
                )
        except stripe.error.CardError as e:
            self._handle_card_declined(invoice, payment_method, e)
        except payment_method.STRIPE_GENERIC_ERROR as e:
            self._handle_card_errors(invoice, e)
        else:
            payment_record.transaction_id = transaction_id
            payment_record.save()
            self._send_payment_receipt(invoice, payment_record)

    def _send_payment_receipt(self, invoice, payment_record):
        from corehq.apps.accounting.tasks import send_purchase_receipt
        receipt_email_template = 'accounting/invoice_receipt_email.html'
        receipt_email_template_plaintext = 'accounting/invoice_receipt_email_plaintext.txt'
        try:
            domain = invoice.subscription.subscriber.domain
            context = {
                'invoicing_contact_email': settings.INVOICING_CONTACT_EMAIL,
                'balance': fmt_dollar_amount(invoice.balance),
                'is_paid': invoice.is_paid,
                'date_due': invoice.date_due.strftime(USER_DATE_FORMAT) if invoice.date_due else 'None',
                'invoice_num': invoice.invoice_number,
            }
            send_purchase_receipt.delay(
                payment_record, domain, receipt_email_template, receipt_email_template_plaintext, context,
            )
        except:
            self._handle_email_failure(payment_record)

    @staticmethod
    def _handle_card_declined(invoice, payment_method, e):
        from corehq.apps.accounting.tasks import send_autopay_failed

        # https://stripe.com/docs/api/python#error_handling
        body = e.json_body
        err = body.get('error', {})

        log_accounting_error(
            "[Autopay] An automatic payment failed for invoice: {} "
            "because the card was declined. This invoice will not be automatically paid. "
            "Not necessarily actionable, but be aware that this happened. "
            "error = {}"
            .format(invoice.id, err)
        )
        send_autopay_failed.delay(invoice, payment_method)

    @staticmethod
    def _handle_card_errors(invoice, e):
        log_accounting_error(
            "[Autopay] An automatic payment failed for invoice: {invoice} "
            "because the of {error}. This invoice will not be automatically paid."
            .format(invoice=invoice.id, error=e)
        )

    @staticmethod
    def _handle_email_failure(payment_record):
        log_accounting_error(
            "[Autopay] During an automatic payment, sending a payment receipt failed"
            " for Payment Record: {}. Everything else succeeded"
            .format(payment_record.id)
        )
<|endoftext|>