common.py 6.81 KB
import sys
import os
import logging
import csv
from datetime import (
    date,
    datetime,
    )
from argparse import ArgumentParser
from configparser import ConfigParser
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
import transaction
from zope.sqlalchemy import register
from opensipkd.waktu import dmyhms
from iso8583_web.models.conf import Conf as BaseConf
from opensipkd.iso8583.bjb.scripts.common import get_module_object


log_format = '%(asctime)s %(levelname)s %(message)s'
formatter = logging.Formatter(log_format)

BIT_18_NAMES = {
    '6010': 'TELLER',
    '6011': 'ATM',
    '6012': 'POS',
    '6013': 'PHONE BANKING',
    '6014': 'INTERNETBANKING',
    '6015': 'KIOSK',
    '6016': 'AUTODEBET',
    '6017': 'MOBILBANKING',
    '7012': 'PT.POS'}

my_registry = dict()


def get_file(filename):
    base_dir = os.path.split(__file__)[0]
    fullpath = os.path.join(base_dir, 'data', filename)
    return open(fullpath)


def append_csv(table, filename, keys):
    DBSession = my_registry['db_session']
    with get_file(filename) as f:
        reader = csv.DictReader(f)
        filter_ = dict()
        for cf in reader:
            for key in keys:
                filter_[key] = cf[key]
            q = DBSession.query(table).filter_by(**filter_)
            found = q.first()
            if found:
                continue
            row = table()
            for fieldname in cf:
                val = cf[fieldname]
                if not val:
                    continue
                setattr(row, fieldname, val)
            DBSession.add(row)


def clean_raw(raw):
    if raw[:2] == '\\x':
        raw = raw[2:]
    if raw[:4] == '3032':
        return bytes.fromhex(raw)
    return raw.encode('utf8')


def get_iso(raw, iso_class, debug=False):
    raw = clean_raw(raw)
    iso = iso_class(debug=debug)
    iso.setIsoContent(raw)
    return iso


def get_keys(iso):
    return dict(
        nomor_bayar=iso.get_invoice_id().strip(),
        stan=iso.get_stan().strip(),
        ntb=iso.get_ntb().strip(),
        channel=get_channel_name(iso))


def get_channel_name(iso):
    channel_id = iso.get_channel().strip()
    if channel_id in BIT_18_NAMES:
        return BIT_18_NAMES[channel_id]
    if channel_id != '6025':
        return ''
    bit_041 = iso.getBit(41).strip()
    bit_042 = iso.getBit(42).strip()
    bit_043 = iso.getBit(43).strip()
    if bit_042 in ('TOKOPEDIA', 'BUKALAPAK', 'MASAGO'):
        return bit_042
    if bit_042 == 'NG':
        return bit_041
    if bit_043 in ('INDOMARET', 'ALFAMART'):
        return bit_043
    return 'LAINNYA'


def create_log(log_file):
    file_handler = logging.FileHandler(log_file)
    file_handler.setFormatter(formatter)
    console_handler = logging.StreamHandler(sys.stdout)
    console_handler.setFormatter(formatter)
    log = logging.getLogger(sys.argv[0])
    log.setLevel(logging.DEBUG)
    log.addHandler(file_handler)
    log.addHandler(console_handler)
    return log


def get_parser():
    pars = ArgumentParser()
    pars.add_argument('conf')
    pars.add_argument('--debug', action='store_true')
    pars.add_argument('--debug-sql', action='store_true')
    return pars


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


class Conf(BaseConf):
    def as_datetime(self):
        return datetime.strptime(self.nilai, '%d-%m-%Y %H:%M:%S')


def is_live(pid):
    try:
        os.kill(pid, 0)
    except OSError:
        return
    return True


def read_pid_file(filename):
    try:
        f = open(filename)
        s = f.read()
        f.close()
        s = s.split()
        s = s[0]
        return int(s)
    except IOError:
        return
    except ValueError:
        return
    except IndexError:
        return


def write_pid_file(filename):
    pid = os.getpid()
    f = open(filename, 'w')
    f.write(str(pid))
    f.close()
    return pid


def make_pid_file(filename):
    pid = read_pid_file(filename)
    if pid and is_live(pid):
        print(f'PID saya {pid} masih aktif.')
        return
    return write_pid_file(filename)


class App:
    iso_class = None  # Override, please
    report_orm = None  # Override, please
    iso_reversal_orm = None  # Override, please

    def __init__(self, argv):
        self.option = get_option(argv)
        conf = ConfigParser()
        conf.read(self.option.conf)
        cf = conf['main']
        self.pid = make_pid_file(cf['pid_file'])
        if not self.pid:
            return
        self.log = create_log(cf['log_file'])
        module_name = cf['module']
        module = get_module_object(module_name)
        module_conf = dict(cf)
        module.init(module_conf)
        iso = self.iso_class()
        self.prod_session = self.get_db_session()
        self.prod_session.bind.echo = self.option.debug_sql
        engine = create_engine(cf['report_db_url'], echo=self.option.debug_sql)
        factory = sessionmaker(bind=engine)
        self.rpt_session = factory()
        register(self.rpt_session)
        self.base_q_report = self.rpt_session.query(self.report_orm)

    def get_db_session(self):  # Override, please
        pass

    def get_last_id(self, nama):
        q = self.rpt_session.query(Conf).filter_by(nama=nama)
        return q.first()

    def get_report(self, iso):
        d = get_keys(iso)
        q = self.base_q_report.filter_by(stan=d['stan'], ntb=d['ntb'])
        return q.first()

    def run_payment(self):  # Override, please
        pass

    def run_reversal(self, conf_name):  # Override, please
        last = self.get_last_id(conf_name)
        q = self.prod_session.query(self.iso_reversal_orm).filter(
                self.iso_reversal_orm.tgl > last.as_datetime())
        for row in q.order_by(self.iso_reversal_orm.tgl):
            iso = get_iso(row.iso_request, self.iso_class, self.option.debug)
            rpt = self.get_report(iso)
            if not rpt or rpt.tgl_batal:
                continue
            rpt.tgl_batal = row.tgl
            last.nilai = s_tgl = dmyhms(row.tgl)
            d = get_keys(iso)
            self.log.info(
                f'Tgl batal {s_tgl}, Nomor bayar {d["nomor_bayar"]}, '
                f'STAN {d["stan"]}, NTB {d["ntb"]}, Channel {d["channel"]}')
            with transaction.manager:
                self.rpt_session.add(rpt)
                self.rpt_session.add(last)

    def run(self):
        self.run_payment()
        self.run_reversal()


def init_db(metadata_rpt, metadata_conf, argv=sys.argv[1:]):
    conf_file = argv[0]
    conf = ConfigParser()
    conf.read(conf_file)
    db_url = conf.get('main', 'report_db_url')
    engine = create_engine(db_url, echo=True)
    metadata_rpt.create_all(engine)
    metadata_conf.create_all(engine)
    factory = sessionmaker(bind=engine)
    my_registry['db_session'] = db_session = factory()
    register(db_session)
    with transaction.manager:
        append_csv(Conf, 'conf.csv', ['nama'])