log2iso.py 4.57 KB
import sys
import re
from argparse import ArgumentParser
from time import time
from ISO8583.ISOErrors import BitNotSet
from sqlalchemy import func
from sqlalchemy.ext.declarative import declarative_base
from zope.sqlalchemy import register
from opensipkd.waktu import create_datetime
from opensipkd.string import FixLength
from ..models import Common
from ..log_models import LogFile
from .common import (
    BaseApp,
    my_registry,
    append_csv,
    create_session,
    InvalidSource,
    )


PATTERN = r'^([\d]*)-([\d]*)-([\d]*) ([\d]*):([\d]*):([\d]*)\.([\d]*) '\
          r'(.*) \[SENT RAW BUFFER\] : (.*)'
REGEX = re.compile(PATTERN)

Base = declarative_base()


def get_parser():
    pars = ArgumentParser()
    pars.add_argument('conf')
    pars.add_argument('--update-from-id', type=int)
    pars.add_argument('--debug-sql', action='store_true')
    return pars


def get_option(argv):
    pars = get_parser()
    return pars.parse_args(argv)


class App(BaseApp):
    def __init__(self, argv):
        super().__init__(argv)
        if not self.pid:
            return

        class Log(self.models.Log, Common):
            pass

        self.report_orm = Log
        if self.conf['models'].find('bphtb') > -1:
            self.conf_name = 'bphtb log file last id'
            self.pajak = 'bphtb'
        else:
            self.conf_name = 'pbb log file last id'
            self.pajak = 'pbb'
        register(self.prod_session)

    def get_option(self, argv):  # Override
        return get_option(argv)

    def get_session_for_save(self):  # Override
        return self.prod_session

    def get_filter_query(self, q):
        return q.filter(LogFile.id > self.last_id)

    def get_count(self):  # Override
        q = self.prod_session.query(func.count())
        q = self.get_filter_query(q)
        return q.scalar()

    def get_payment_query(self):  # Override
        q = self.prod_session.query(LogFile)
        q = self.get_filter_query(q)
        return q.order_by(LogFile.id)

    def get_prefix_log(self):  # Override
        return f'ID {self.log_id}'

    def create_data(self, log_file):  # Override
        self.log_id = log_file.id
        match = REGEX.search(log_file.line)
        if not match:
            raise InvalidSource(f'ID {log_file.id} pola tidak dipahami')
        year, month, day, hour, minute, sec, msec, _, raw = \
            match.groups()
        waktu = create_datetime(
                int(year), int(month), int(day), int(hour),
                int(minute), int(sec), int(msec)*1000)
        iso = self.service.Doc()
        try:
            iso.setIsoContent(raw[4:].encode('utf-8'))
        except ValueError as e:
            raise InvalidSource('ID {log_file.id} tidak dipahami: {str(e)}')
        try:
            if iso.getBit(3) != self.service.PAYMENT_CODE:
                raise InvalidSource(f'ID {log_file.id} bukan payment')
        except BitNotSet:
            raise InvalidSource(f'ID {log_file.id} bit 3 tidak ada')
        d = dict(id=log_file.id, mti=iso.getMTI(), created=waktu)
        for bit in iso.get_bit_definition():
            try:
                value = iso.getBit(bit)
            except BitNotSet:
                continue
            field = 'bit_{}'.format(str(bit).zfill(3))
            d[field] = value
        if self.pajak == 'bphtb':
            try:
                profile = FixLength(self.service.INVOICE_PROFILE)
                profile.set_raw(iso.getBit(47))
                d['bit_047_data'] = profile.to_dict()
                profile = FixLength(self.service.INVOICE_PROFILE2)
                profile.set_raw(iso.getBit(48))
                d['bit_048_data'] = profile.to_dict()
            except BitNotSet:
                pass
        else:
            profile = FixLength(self.service.INVOICE_PROFILE)
            profile.set_raw(iso.getBit(62))
            d['bit_062_data'] = profile.to_dict()
        return d

    def get_last_time(self):  # Override
        return str(self.last_pay.id)

    def run_payment(self):  # Override
        self.last_pay = None
        self.offset = 0
        if self.option.update_from_id is None:
            self.last = self.get_last_id(self.conf_name)
            self.last_id = self.last.as_int()
        else:
            self.last = None
            self.last_id = self.option.update_from_id - 1
        self.count = self.get_count()
        self.start_time = time()
        while True:
            found = self.update_from_date()
            if not found:
                break
            if self.last_pay and self.last:
                self.update_last()


def main(argv=sys.argv[1:]):
    app = App(argv)
    app.run()