Commit 1555f0a1 by Owo Sugiana

Class yang tidak bergantung pada command line argument

1 parent 396ca087
3.0 2023-03-27
--------------
- Class yang tidak bergantung pada command line argument
2.13 2023-03-18 2.13 2023-03-18
--------------- ---------------
- Tambah Fortuna - Tambah Fortuna
......
...@@ -36,17 +36,8 @@ diperlukan user ``report``:: ...@@ -36,17 +36,8 @@ diperlukan user ``report``::
GRANT SELECT ON pad_payment TO report; GRANT SELECT ON pad_payment TO report;
GRANT SELECT ON pad_reversal TO report; GRANT SELECT ON pad_reversal TO report;
Lalu buat file konfigurasi bernama ``pad-report.ini``:: File konfigurasinya lihat di ``pad.ini``. Jika ada database VA / QRIS maka
tambahkan::
[main]
models = opensipkd.pad.models.default
service = opensipkd.pad.services.default
db_url = postgresql://report:password@localhost:5432/simpad
report_db_url = postgresql://report:password@localhost:5432/payment_report
pid_file = /home/sugiana/tmp/pad-report.pid
log_file = /home/sugiana/log/pad-report.log
Jika ada database VA / QRIS maka tambahkan::
va_db_url = postgresql://report:password@localhost:5432/va_bjb va_db_url = postgresql://report:password@localhost:5432/va_bjb
...@@ -57,7 +48,7 @@ Buat tabel yang dibutuhkan di database ``payment_report``:: ...@@ -57,7 +48,7 @@ Buat tabel yang dibutuhkan di database ``payment_report``::
Kemudian buat file ``/etc/cron.d/payment-report`` berikut ini:: Kemudian buat file ``/etc/cron.d/payment-report`` berikut ini::
MAILTO="" MAILTO=""
*/5 * * * * sugiana cd /home/sugiana && env/bin/pad_report pad-report.ini */5 * * * * sugiana cd /home/sugiana && env/bin/payment_report pad-report.ini
Lalu pantau log-nya. Jika sudah ada penampakan lihat hasilnya di tabel ``pad_report``. Lalu pantau log-nya. Jika sudah ada penampakan lihat hasilnya di tabel ``pad_report``.
...@@ -78,22 +69,14 @@ Berikan hak akses yang diperlukan user ``report``:: ...@@ -78,22 +69,14 @@ Berikan hak akses yang diperlukan user ``report``::
GRANT SELECT ON bphtb.bphtb_ppat TO report; GRANT SELECT ON bphtb.bphtb_ppat TO report;
GRANT SELECT ON log_iso TO report; GRANT SELECT ON log_iso TO report;
Lalu buat file konfigurasi bernama ``bphtb-report.ini``:: File konfigurasi lihat di ``bphtb.ini``. Jika ada database VA / QRIS maka
tambahkan::
[main]
models = opensipkd.bphtb.models.default
db_url = postgresql://report:password@localhost:5434/bphtb
report_db_url = postgresql://report:password@localhost:5434/payment_report
pid_file = /home/sugiana/tmp/bphtb-report.pid
log_file = /home/sugiana/log/bphtb-report.log
Jika ada database VA / QRIS maka tambahkan::
va_db_url = postgresql://report:password@localhost:5432/va_bjb va_db_url = postgresql://report:password@localhost:5432/va_bjb
Kemudian di file ``/etc/cron.d/payment-report`` tambahkan:: Kemudian di file ``/etc/cron.d/payment-report`` tambahkan::
*/5 * * * * sugiana cd /home/sugiana && env/bin/bphtb_report bphtb-report.ini */5 * * * * sugiana cd /home/sugiana && env/bin/payment_report bphtb-report.ini
Lalu pantau log-nya. Jika sudah ada penampakan lihat hasilnya di tabel ``bphtb_report``. Lalu pantau log-nya. Jika sudah ada penampakan lihat hasilnya di tabel ``bphtb_report``.
...@@ -114,37 +97,14 @@ Berikan hak akses yang diperlukan user ``report``:: ...@@ -114,37 +97,14 @@ Berikan hak akses yang diperlukan user ``report``::
GRANT SELECT ON log_iso TO report; GRANT SELECT ON log_iso TO report;
CREATE INDEX log_iso_payment ON log_iso (mti, bit_003, bit_039, bit_061); CREATE INDEX log_iso_payment ON log_iso (mti, bit_003, bit_039, bit_061);
Lalu buat file konfigurasi bernama ``pbb-report.ini``:: File konfigurasi lihat di ``pbb.ini``. Jika ada database VA / QRIS maka
tambahkan::
[main]
models = sismiop.models.default
db_url = postgresql://report:password@localhost:5434/pbb
report_db_url = postgresql://report:password@localhost:5434/payment_report
pid_file = /home/sugiana/tmp/pbb-report.pid
log_file = /home/sugiana/log/pbb-report.log
Jika ada database VA / QRIS maka tambahkan::
va_db_url = postgresql://report:password@localhost:5432/va_bjb va_db_url = postgresql://report:password@localhost:5432/va_bjb
Kemudian di file ``/etc/cron.d/payment-report`` tambahkan:: Kemudian di file ``/etc/cron.d/payment-report`` tambahkan::
*/5 * * * * sugiana cd /home/sugiana && env/bin/pbb_report pbb-report.ini */5 * * * * sugiana cd /home/sugiana && env/bin/payment_report pbb-report.ini
Lalu pantau log-nya. Jika sudah ada penampakan lihat hasilnya di tabel ``pbb_report``. Lalu pantau log-nya. Jika sudah ada penampakan lihat hasilnya di tabel ``pbb_report``.
Database Sybase
---------------
Bila sumber data berasal dari Sybase maka kita bisa gunakan driver FreeTDS::
$ sudo apt-get install freetds-dev freetds-bin unixodbc-dev tdsodbc
$ sudo dpkg-reconfigure tdsodbc
Nanti ada pertanyaan apakah FreeTDS akan dipasang sebagai driver ODBC. Jawab
Ya. Kemudian pasang modul Python-nya::
$ ~/env/bin/pip install -r sybase-requirements.txt
Selanjutnya lihat ``pbb-odbc.ini``.
[main] [main]
module = payment_report.bphtb.default
models = opensipkd.bphtb.models.default models = opensipkd.bphtb.models.default
db_url = postgresql://user:pass@localhost/db db_url = postgresql://user:pass@localhost/db
#va_db_url = postgresql://user:pass@localhost/db #va_db_url = postgresql://user:pass@localhost/db
report_db_url = postgresql://user:pass@localhost/db report_db_url = postgresql://user:pass@localhost/db
pid_file = /home/sugiana/tmp/bphtb-report.pid pid_file = /home/sugiana/tmp/bphtb-report.pid
log_file = /home/sugiana/log/bphtb-report.log log_file = /home/sugiana/log/bphtb-report.log
[formatter_generic]
format = %(asctime)s %(levelname)s %(name)s %(message)s
[formatter_simple]
format = %(asctime)s %(levelname)s %(message)s
[handler_console]
class = StreamHandler
stream = sys.stdout
formatter = generic
[handler_file]
class = FileHandler
filename = /home/sugiana/log/bphtb-report.log
formatter = simple
[logger_root]
handlers = console, file
level = INFO
[main] [main]
moduel = payment_report.pad.default
models = opensipkd.pad.models.default models = opensipkd.pad.models.default
service = opensipkd.pad.services.default service = opensipkd.pad.services.default
db_url = postgresql://user:pass@localhost/db db_url = postgresql://user:pass@localhost/db
...@@ -6,3 +7,23 @@ db_url = postgresql://user:pass@localhost/db ...@@ -6,3 +7,23 @@ db_url = postgresql://user:pass@localhost/db
report_db_url = postgresql://user:pass@localhost/db report_db_url = postgresql://user:pass@localhost/db
pid_file = /home/sugiana/tmp/pad-report.pid pid_file = /home/sugiana/tmp/pad-report.pid
log_file = /home/sugiana/log/pad-report.log log_file = /home/sugiana/log/pad-report.log
[formatter_generic]
format = %(asctime)s %(levelname)s %(name)s %(message)s
[formatter_simple]
format = %(asctime)s %(levelname)s %(message)s
[handler_console]
class = StreamHandler
stream = sys.stdout
formatter = generic
[handler_file]
class = FileHandler
filename = /home/sugiana/log/pad-report.log
formatter = simple
[logger_root]
handlers = console, file
level = INFO
import sys
import pdb
from datetime import ( from datetime import (
datetime, datetime,
timedelta,
date, date,
) )
from ISO8583.ISOErrors import BitNotSet from ISO8583.ISOErrors import BitNotSet
...@@ -27,6 +24,7 @@ from sqlalchemy.exc import ( ...@@ -27,6 +24,7 @@ from sqlalchemy.exc import (
) )
from opensipkd.string import FixLength from opensipkd.string import FixLength
from opensipkd.waktu import dmyhms from opensipkd.waktu import dmyhms
from opensipkd.bphtb.models.default import Invoice
from opensipkd.bphtb.models.customer import CustomerMixin from opensipkd.bphtb.models.customer import CustomerMixin
from opensipkd.bphtb.models.perolehan import PerolehanMixin from opensipkd.bphtb.models.perolehan import PerolehanMixin
from opensipkd.bphtb.models.invoice import InvoiceMixin from opensipkd.bphtb.models.invoice import InvoiceMixin
...@@ -39,7 +37,6 @@ from opensipkd.bphtb.models.default import ( ...@@ -39,7 +37,6 @@ from opensipkd.bphtb.models.default import (
) )
from opensipkd.bphtb.structure import NOP from opensipkd.bphtb.structure import NOP
from opensipkd.bphtb.services.default.structure import INVOICE_ID from opensipkd.bphtb.services.default.structure import INVOICE_ID
from iso8583_web.models.meta import Base as BaseConf
from opensipkd.iso8583.bjb.bphtb import Doc from opensipkd.iso8583.bjb.bphtb import Doc
from opensipkd.iso8583.bjb.bphtb.structure import PAYMENT_CODE from opensipkd.iso8583.bjb.bphtb.structure import PAYMENT_CODE
from opensipkd.iso8583.bjb.models import Log as JsonLog from opensipkd.iso8583.bjb.models import Log as JsonLog
...@@ -48,13 +45,12 @@ from ..models import ( ...@@ -48,13 +45,12 @@ from ..models import (
Base, Base,
Bphtb, Bphtb,
) )
from .common import ( from ..common import (
get_iso, get_iso,
get_channel_name_by_row, get_channel_name_by_row,
get_channel_name, get_channel_name,
get_channel_info_by_iso, get_channel_info_by_iso,
BaseApp, BaseApp,
init_db as base_init_db,
one_day, one_day,
InvalidSource, InvalidSource,
BANK_NAMES, BANK_NAMES,
...@@ -172,8 +168,6 @@ class App(BaseApp): ...@@ -172,8 +168,6 @@ class App(BaseApp):
def __init__(self, argv): def __init__(self, argv):
super().__init__(argv) super().__init__(argv)
if not self.pid:
return
self.base_q_func = self.prod_session.query(func.count()) self.base_q_func = self.prod_session.query(func.count())
self.is_sukabumi_kota = False self.is_sukabumi_kota = False
self.is_cartenz = False self.is_cartenz = False
...@@ -241,14 +235,12 @@ class App(BaseApp): ...@@ -241,14 +235,12 @@ class App(BaseApp):
self.base_q_iso_payment = self.prod_session.query(IsoPayment) self.base_q_iso_payment = self.prod_session.query(IsoPayment)
try: try:
self.base_q_iso_payment.first() self.base_q_iso_payment.first()
except ProgrammingError: except (ProgrammingError, OperationalError):
self.prod_session.rollback() self.prod_session.rollback()
self.base_q_iso_payment = self.prod_session.query( self.base_q_iso_payment = self.prod_session.query(
AlternativeIsoPayment) AlternativeIsoPayment)
def set_default_models(self): def set_default_models(self):
if self.models:
Invoice = self.models.Invoice
self.base_q_inv = self.prod_session.query(Invoice) self.base_q_inv = self.prod_session.query(Invoice)
self.base_q_inv.first() self.base_q_inv.first()
self.Customer = Customer self.Customer = Customer
...@@ -458,7 +450,8 @@ class App(BaseApp): ...@@ -458,7 +450,8 @@ class App(BaseApp):
op_alamat=inv.alamat_op, npop=inv.npop, bumi_luas=inv.luas_tanah, op_alamat=inv.alamat_op, npop=inv.npop, bumi_luas=inv.luas_tanah,
bng_luas=inv.luas_bangunan, nilai_bphtb=pay.jumlah_yg_dibayar, bng_luas=inv.luas_bangunan, nilai_bphtb=pay.jumlah_yg_dibayar,
jenis_perolehan=str(inv.jns_perolehan_hak), ppat=inv.nama_notaris, jenis_perolehan=str(inv.jns_perolehan_hak), ppat=inv.nama_notaris,
channel_id=channel_id, channel_nama=channel_nama) channel_id=channel_id, channel_nama=channel_nama,
status_pembayaran=inv.status_pembayaran)
def create_data_cartenz(self, pay): def create_data_cartenz(self, pay):
self.invoice_id = cartenz_invoice_id(pay) self.invoice_id = cartenz_invoice_id(pay)
...@@ -537,13 +530,3 @@ class App(BaseApp): ...@@ -537,13 +530,3 @@ class App(BaseApp):
nilai_bphtb=pay.bayar, jenis_perolehan=perolehan.nama, nilai_bphtb=pay.bayar, jenis_perolehan=perolehan.nama,
ppat=cust.nama.strip(), channel_id=channel_id, ppat=cust.nama.strip(), channel_id=channel_id,
channel_nama=channel_nama) channel_nama=channel_nama)
def main(argv=sys.argv[1:]):
app = App(argv)
if app.pid:
app.run()
def init_db(argv=sys.argv[1:]):
base_init_db(Base.metadata, BaseConf.metadata, argv)
...@@ -3,6 +3,7 @@ import os ...@@ -3,6 +3,7 @@ import os
import logging import logging
import csv import csv
from time import time from time import time
from logging import getLogger
from datetime import ( from datetime import (
date, date,
datetime, datetime,
...@@ -23,7 +24,7 @@ import transaction ...@@ -23,7 +24,7 @@ import transaction
from zope.sqlalchemy import register from zope.sqlalchemy import register
from opensipkd.views.models import Conf as BaseConf from opensipkd.views.models import Conf as BaseConf
from opensipkd.iso8583.bjb.scripts.common import get_module_object from opensipkd.iso8583.bjb.scripts.common import get_module_object
from ..models import Base from .models import Base
from .tools import ( from .tools import (
plain_values, plain_values,
update, update,
...@@ -102,7 +103,7 @@ def humanize_time(secs): ...@@ -102,7 +103,7 @@ def humanize_time(secs):
def get_file(filename): def get_file(filename):
base_dir = os.path.split(__file__)[0] base_dir = os.path.split(__file__)[0]
fullpath = os.path.join(base_dir, 'data', filename) fullpath = os.path.join(base_dir, 'scripts', 'data', filename)
return open(fullpath) return open(fullpath)
...@@ -206,32 +207,6 @@ def get_keys(iso): ...@@ -206,32 +207,6 @@ def get_keys(iso):
return d return d
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('--update-from-date')
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): class Conf(BaseConf):
def as_datetime(self): def as_datetime(self):
pola = '%d-%m-%Y %H:%M:%S' pola = '%d-%m-%Y %H:%M:%S'
...@@ -240,46 +215,6 @@ class Conf(BaseConf): ...@@ -240,46 +215,6 @@ class Conf(BaseConf):
return datetime.strptime(self.nilai, pola) return datetime.strptime(self.nilai, pola)
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)
def create_session(db_url, debug=False): def create_session(db_url, debug=False):
engine = create_engine(db_url, echo=debug) engine = create_engine(db_url, echo=debug)
factory = sessionmaker(bind=engine) factory = sessionmaker(bind=engine)
...@@ -291,29 +226,20 @@ class BaseApp: ...@@ -291,29 +226,20 @@ class BaseApp:
report_orm = None # Override, please report_orm = None # Override, please
va_product_code = '' # Override, please va_product_code = '' # Override, please
def __init__(self, argv): def __init__(self, conf):
self.option = self.get_option(argv) self.conf = conf
self.conf_all = ConfigParser() self.prod_session = self.models = None
self.conf_all.read(self.option.conf) factory = self.get_factory('report_db_url')
self.conf = self.conf_all['main'] self.rpt_session = factory()
self.pid = make_pid_file(self.conf['pid_file']) register(self.rpt_session)
if not self.pid:
return
self.prod_session = self.handler = self.odbc_conn = self.models = None
self.log = create_log(self.conf['log_file'])
if 'models' in self.conf: if 'models' in self.conf:
self.models = get_module_object(self.conf['models']) self.models = get_module_object(self.conf['models'])
if 'service' in self.conf: if 'service' in self.conf:
self.service = get_module_object(self.conf['service']) self.service = get_module_object(self.conf['service'])
try:
self.handler = self.service.Handler(self)
except AttributeError as e:
pass
module_conf = dict(self.conf)
if 'db_url' in self.conf: if 'db_url' in self.conf:
if self.conf['db_url'] == 'odbc': if self.conf['db_url'] == 'odbc':
import pyodbc import pyodbc
odbc_profile = dict(self.conf_all.items('odbc')) odbc_profile = self.get_prefix_config('odbc.')
self.odbc_conn = pyodbc.connect(**odbc_profile) self.odbc_conn = pyodbc.connect(**odbc_profile)
else: else:
factory = self.get_factory('db_url') factory = self.get_factory('db_url')
...@@ -323,9 +249,6 @@ class BaseApp: ...@@ -323,9 +249,6 @@ class BaseApp:
self.h2h_session = factory() self.h2h_session = factory()
else: else:
self.h2h_session = None self.h2h_session = None
factory = self.get_factory('report_db_url')
self.rpt_session = factory()
register(self.rpt_session)
if 'va_db_url' in self.conf: if 'va_db_url' in self.conf:
factory = self.get_factory('va_db_url') factory = self.get_factory('va_db_url')
self.va_session = factory() self.va_session = factory()
...@@ -336,12 +259,17 @@ class BaseApp: ...@@ -336,12 +259,17 @@ class BaseApp:
else: else:
self.va_session = None self.va_session = None
def get_option(self, argv): def get_prefix_config(self, prefix):
return get_option(argv) d = dict()
for key in self.conf:
if key.find(prefix):
k = key[len(prefix):]
d[k] = self.conf[key]
return d
def get_factory(self, name): def get_factory(self, name):
db_url = self.conf[name] db_url = self.conf[name]
engine = create_engine(db_url, echo=self.option.debug_sql) engine = create_engine(db_url)
return sessionmaker(bind=engine) return sessionmaker(bind=engine)
def get_last_id(self, nama): def get_last_id(self, nama):
...@@ -375,6 +303,7 @@ class BaseApp: ...@@ -375,6 +303,7 @@ class BaseApp:
q = self.get_payment_query() q = self.get_payment_query()
no = self.offset no = self.offset
found = False found = False
log = getLogger('update_from_date()')
for pay in q.offset(self.offset).limit(row_limit): for pay in q.offset(self.offset).limit(row_limit):
found = True found = True
no += 1 no += 1
...@@ -400,10 +329,10 @@ class BaseApp: ...@@ -400,10 +329,10 @@ class BaseApp:
msg = f'INSERT {d}' msg = f'INSERT {d}'
rpt = self.report_orm(**source) rpt = self.report_orm(**source)
msg = f'{self.get_prefix_log()} {msg}' msg = f'{self.get_prefix_log()} {msg}'
log_method = self.log.info log_method = log.info
except InvalidSource as e: except InvalidSource as e:
msg = str(e) msg = str(e)
log_method = self.log.warning log_method = log.warning
rpt = None rpt = None
e = self.get_estimate(no) e = self.get_estimate(no)
log_method(f'#{no}/{self.count} {msg}, estimate {e}') log_method(f'#{no}/{self.count} {msg}, estimate {e}')
...@@ -419,9 +348,6 @@ class BaseApp: ...@@ -419,9 +348,6 @@ class BaseApp:
pass pass
def update_last(self): def update_last(self):
if self.handler:
self.last.nilai = self.handler.get_last_time()
else:
self.last.nilai = self.get_last_time() self.last.nilai = self.get_last_time()
with transaction.manager: with transaction.manager:
self.rpt_session.add(self.last) self.rpt_session.add(self.last)
...@@ -429,45 +355,29 @@ class BaseApp: ...@@ -429,45 +355,29 @@ class BaseApp:
def get_count(self): # Override, please def get_count(self): # Override, please
pass pass
def run_payment(self): def run(self):
self.last_pay = None self.last_pay = self.last = None
self.offset = 0 self.offset = 0
self.tgl_akhir = date.today() if 'tgl_awal' in self.conf:
if self.option.update_from_date: self.tgl_awal = self.conf['tgl_awal']
self.last = None
try:
days_ago = int(self.option.update_from_date)
self.tgl_awal = date.today() + timedelta(days_ago)
except ValueError:
t = self.option.update_from_date.split(',')
self.tgl_awal = datetime.strptime(t[0], '%d-%m-%Y')
if t[1:]:
self.tgl_akhir = datetime.strptime(t[1], '%d-%m-%Y')
else: else:
self.last = self.get_last_id(self.conf_name) self.last = self.get_last_id(self.conf_name)
self.tgl_awal = self.last.as_datetime() self.tgl_awal = self.last.as_datetime()
while True: if 'tgl_akhir' in self.conf:
if self.handler: self.tgl_akhir = self.conf['tgl_akhir']
self.count = self.handler.get_count()
else: else:
self.tgl_akhir = date.today()
while True:
self.count = self.get_count() self.count = self.get_count()
if not self.count: if not self.count:
return return
self.start_time = time() self.start_time = time()
if self.handler:
found = self.handler.update_from_date()
else:
found = self.update_from_date() found = self.update_from_date()
if not found: if not found:
break break
if self.last_pay and self.last: if self.last_pay and self.last:
self.update_last() self.update_last()
def run(self):
if not self.pid:
return
self.run_payment()
def get_va_channel(self, pay_date): def get_va_channel(self, pay_date):
if not self.va_session: if not self.va_session:
return return
...@@ -476,6 +386,7 @@ class BaseApp: ...@@ -476,6 +386,7 @@ class BaseApp:
row = q.first() row = q.first()
if not row: if not row:
return return
log = getLogger('get_va_channel()')
va_pay, va_inv = row va_pay, va_inv = row
va_pay_date = datetime.strptime( va_pay_date = datetime.strptime(
va_pay.transaction_date, '%Y-%m-%d %H:%M:%S') va_pay.transaction_date, '%Y-%m-%d %H:%M:%S')
...@@ -484,7 +395,7 @@ class BaseApp: ...@@ -484,7 +395,7 @@ class BaseApp:
msg = f'Invoice ID {self.invoice_id} ada pembayaran melalui '\ msg = f'Invoice ID {self.invoice_id} ada pembayaran melalui '\
f'VA/QRIS tapi tanggalnya beda yaitu {pay_date} vs '\ f'VA/QRIS tapi tanggalnya beda yaitu {pay_date} vs '\
f'{va_pay_date}' f'{va_pay_date}'
self.log.warning(msg) log.warning(msg)
return return
if va_inv.va_type == 'a': if va_inv.va_type == 'a':
return 'VA' return 'VA'
...@@ -493,18 +404,3 @@ class BaseApp: ...@@ -493,18 +404,3 @@ class BaseApp:
raise Exception( raise Exception(
f'Invoice ID {invoice_id} va_type {inv.va_type} ' f'Invoice ID {invoice_id} va_type {inv.va_type} '
'belum dipahami, perbaiki script') 'belum dipahami, perbaiki script')
def init_db(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)
Base.metadata.create_all(engine)
BaseConf.metadata.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'])
...@@ -118,11 +118,11 @@ class Bphtb(Base, Common): ...@@ -118,11 +118,11 @@ class Bphtb(Base, Common):
# Bit 61 # Bit 61
nop = Column(String(22), nullable=False) nop = Column(String(22), nullable=False)
# bphtb.bphtb_bank.wp_nama # bphtb.bphtb_bank.wp_nama
wp_nama = Column(String(50), nullable=False) wp_nama = Column(String(225), nullable=False)
# bphtb.bphtb_bank.wp_alamat # bphtb.bphtb_bank.wp_alamat
wp_alamat = Column(String(100), nullable=False) wp_alamat = Column(String(225), nullable=False)
# bphtb.bphtb_sspd.op_alamat # bphtb.bphtb_sspd.op_alamat
op_alamat = Column(String(100), nullable=False) op_alamat = Column(String(225), nullable=False)
# bphtb.bphtb_bank.npop # bphtb.bphtb_bank.npop
npop = Column(Float, nullable=False) npop = Column(Float, nullable=False)
# bphtb.bphtb_bank.bumi_luas # bphtb.bphtb_bank.bumi_luas
...@@ -141,6 +141,7 @@ class Bphtb(Base, Common): ...@@ -141,6 +141,7 @@ class Bphtb(Base, Common):
channel_nama = Column(String(32), nullable=False) channel_nama = Column(String(32), nullable=False)
# bphtb.bphtb_reversal.tgl # bphtb.bphtb_reversal.tgl
tgl_batal = Column(DateTime(timezone=True)) tgl_batal = Column(DateTime(timezone=True))
status_pembayaran = Column(Integer)
class Pbb(Base, Common): class Pbb(Base, Common):
......
import sys
from datetime import (
date,
datetime,
)
from sqlalchemy import func from sqlalchemy import func
from sqlalchemy.exc import ProgrammingError from sqlalchemy.exc import ProgrammingError
from opensipkd.string import FixLength from opensipkd.string import FixLength
from opensipkd.waktu import dmyhms from opensipkd.waktu import dmyhms
from iso8583_web.models.meta import Base as BaseConf
from opensipkd.iso8583.bjb.pad.structure import PAYMENT_CODE from opensipkd.iso8583.bjb.pad.structure import PAYMENT_CODE
from opensipkd.iso8583.bjb.pad.doc import Doc as BjbDoc from opensipkd.iso8583.bjb.pad.doc import Doc as BjbDoc
from opensipkd.iso8583.bjb.pad.models import Log from opensipkd.iso8583.bjb.pad.models import Log
from opensipkd.iso8583.multi.doc import Doc as MultiDoc from opensipkd.iso8583.multi.doc import Doc as MultiDoc
from ..models import ( from ..models import Pad
Base, from ..common import (
Pad,
)
from .common import (
get_iso, get_iso,
get_channel_info_by_iso, get_channel_info_by_iso,
get_channel_name_by_row, get_channel_name_by_row,
BaseApp, BaseApp,
init_db as base_init_db,
one_day, one_day,
BANK_NAMES, BANK_NAMES,
) )
...@@ -41,10 +31,6 @@ class App(BaseApp): ...@@ -41,10 +31,6 @@ class App(BaseApp):
def __init__(self, argv): def __init__(self, argv):
super().__init__(argv) super().__init__(argv)
if not self.pid:
return
if self.handler:
return
Usaha = self.models.Usaha Usaha = self.models.Usaha
Pajak = self.models.Pajak Pajak = self.models.Pajak
Customer = self.models.Customer Customer = self.models.Customer
...@@ -100,7 +86,7 @@ class App(BaseApp): ...@@ -100,7 +86,7 @@ class App(BaseApp):
channel = BANK_NAMES[str(row.bank_id)] channel = BANK_NAMES[str(row.bank_id)]
return '0000', channel, None, None, row.bank_id return '0000', channel, None, None, row.bank_id
Doc = BankHandlers[row.bank_id] Doc = BankHandlers[row.bank_id]
iso = get_iso(row.iso_request, Doc, self.option.debug) iso = get_iso(row.iso_request, Doc)
info = get_channel_info_by_iso(iso) info = get_channel_info_by_iso(iso)
return info.get('bit_018', '0000'), info['channel'], iso.get_stan(), \ return info.get('bit_018', '0000'), info['channel'], iso.get_stan(), \
iso.get_ntb(), row.bank_id iso.get_ntb(), row.bank_id
...@@ -153,13 +139,3 @@ class App(BaseApp): ...@@ -153,13 +139,3 @@ class App(BaseApp):
pokok=pay.jml_bayar-pay.denda, denda=pay.denda, bunga=pay.bunga, pokok=pay.jml_bayar-pay.denda, denda=pay.denda, bunga=pay.bunga,
jml_bayar=pay.jml_bayar, channel_id=channel_id, jml_bayar=pay.jml_bayar, channel_id=channel_id,
channel_name=channel_name, bank_id=bank_id) channel_name=channel_name, bank_id=bank_id)
def main(argv=sys.argv[1:]):
app = App(argv)
if app.pid:
app.run()
def init_db(argv=sys.argv[1:]):
base_init_db(Base.metadata, BaseConf.metadata, argv)
...@@ -24,7 +24,6 @@ from opensipkd.waktu import ( ...@@ -24,7 +24,6 @@ from opensipkd.waktu import (
dmyhms, dmyhms,
create_datetime, create_datetime,
) )
from opensipkd.iso8583.bjb.scripts.common import get_module_object
from opensipkd.iso8583.bjb.pbb.default import Doc from opensipkd.iso8583.bjb.pbb.default import Doc
from opensipkd.iso8583.bjb.pbb.structure import PAYMENT_CODE from opensipkd.iso8583.bjb.pbb.structure import PAYMENT_CODE
from opensipkd.iso8583.bjb.pbb.models import Log from opensipkd.iso8583.bjb.pbb.models import Log
...@@ -33,7 +32,7 @@ from ..models import ( ...@@ -33,7 +32,7 @@ from ..models import (
Pbb, Pbb,
Base, Base,
) )
from .common import ( from ..common import (
get_iso, get_iso,
get_channel_info_by_iso, get_channel_info_by_iso,
get_channel_name_by_row, get_channel_name_by_row,
...@@ -103,8 +102,7 @@ class App(BaseApp): ...@@ -103,8 +102,7 @@ class App(BaseApp):
def __init__(self, argv): def __init__(self, argv):
super().__init__(argv) super().__init__(argv)
if not self.pid: self.kd_tp_kasda = self.kd_tp_va = self.kd_tp_qris = None
return
self.set_other_db() self.set_other_db()
if self.prod_session: if self.prod_session:
d = Doc() d = Doc()
...@@ -113,12 +111,12 @@ class App(BaseApp): ...@@ -113,12 +111,12 @@ class App(BaseApp):
self.prod_session.query(d.iso_payment_model).first() self.prod_session.query(d.iso_payment_model).first()
self.iso_payment_orm = d.iso_payment_model self.iso_payment_orm = d.iso_payment_model
except DatabaseError: except DatabaseError:
self.prod_session.rollback()
self.iso_payment_orm = AlternativePayment self.iso_payment_orm = AlternativePayment
self.base_q_iso = self.prod_session.query( self.base_q_iso = self.prod_session.query(
self.iso_payment_orm, self.iso_inquiry_orm).filter( self.iso_payment_orm, self.iso_inquiry_orm).filter(
self.iso_payment_orm.inquiry_id == self.iso_inquiry_orm.id) self.iso_payment_orm.inquiry_id == self.iso_inquiry_orm.id)
if 'models' in self.conf: if 'models' in self.conf:
self.models = get_module_object(self.conf['models'])
self.Psppt = self.models.PembayaranSppt self.Psppt = self.models.PembayaranSppt
self.Sppt = self.models.Sppt self.Sppt = self.models.Sppt
self.base_q_sppt = self.prod_session.query(self.Sppt) self.base_q_sppt = self.prod_session.query(self.Sppt)
...@@ -359,8 +357,3 @@ class App(BaseApp): ...@@ -359,8 +357,3 @@ class App(BaseApp):
q = self.prod_session.query(self.Psppt) q = self.prod_session.query(self.Psppt)
q = self.get_filter_query(q) q = self.get_filter_query(q)
return q.order_by(self.Psppt.tgl_rekam_byr_sppt) return q.order_by(self.Psppt.tgl_rekam_byr_sppt)
def main(argv=sys.argv[1:]):
app = App(argv)
app.run()
from ISO8583.ISO8583 import BitNotSet
from sismiop.services.sukabumi_kota import get_id
from sismiop.models.sukabumi_kota import (
Sppt,
PembayaranSppt as Psppt,
)
from opensipkd.waktu import dmyhms
from opensipkd.iso8583.bjb.pbb.default import Doc
from opensipkd.iso8583.bjb.pbb.default.models import Inquiry
from ..models import Pbb
from ..common import (
get_iso,
get_channel_info_by_iso,
get_channel_name_by_row,
get_channel_name_by_dict,
one_day,
InvalidSource,
)
from .default import (
App as BaseApp,
AlternativePayment as Payment,
to_date,
)
class App(BaseApp):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.iso_inquiry_orm = Inquiry
self.iso_payment_orm = Payment
self.base_q_iso = self.prod_session.query(
self.iso_payment_orm, self.iso_inquiry_orm).filter(
self.iso_payment_orm.inquiry_id == self.iso_inquiry_orm.id)
self.base_q_sppt = self.prod_session.query(Sppt)
def get_sppt(self, psppt):
q = self.base_q_sppt.filter_by(
KD_PROPINSI=psppt.KD_PROPINSI,
KD_DATI2=psppt.KD_DATI2,
KD_KECAMATAN=psppt.KD_KECAMATAN,
KD_KELURAHAN=psppt.KD_KELURAHAN,
KD_BLOK=psppt.KD_BLOK,
NO_URUT=psppt.NO_URUT,
KD_JNS_OP=psppt.KD_JNS_OP,
THN_PAJAK_SPPT=psppt.THN_PAJAK_SPPT)
return q.first()
def get_iso_row(self, psppt):
iso_inq = self.iso_inquiry_orm
awal = psppt.TGL_REKAM_BYR_SPPT.date()
akhir = awal + one_day
q = self.get_iso_query(psppt)
q = q.filter(iso_inq.tgl >= awal, iso_inq.tgl < akhir)
return q.first()
def get_iso_query(self, psppt):
iso_inq = self.iso_inquiry_orm
q = self.base_q_iso.filter_by(
propinsi=psppt.KD_PROPINSI,
kabupaten=psppt.KD_DATI2,
kecamatan=psppt.KD_KECAMATAN,
kelurahan=psppt.KD_KELURAHAN,
blok=psppt.KD_BLOK,
urut=psppt.NO_URUT,
jenis=psppt.KD_JNS_OP,
tahun=psppt.THN_PAJAK_SPPT,
ke=psppt.PEMBAYARAN_SPPT_KE)
return q.order_by(iso_inq.id.desc())
def warning_date(self, psppt):
q = self.get_iso_query(psppt)
row = q.first()
if row:
pay, inq = row
msg = f'Invoice ID {self.invoice_id} berbeda tanggal dengan '\
f'ISO8583 yaitu {psppt.TGL_REKAM_BYR_SPPT.date()} vs '\
f'{inq.tgl}'
self.log.warning(msg)
def get_iso_v1(self, psppt):
if self.h2h_session:
return
row = self.get_iso_row(psppt)
if not row:
self.warning_date(psppt)
return
row_pay, row_inq = row
iso = get_iso(row_pay.iso_request, Doc)
info = get_channel_info_by_iso(iso)
try:
user_id = iso.getBit(107)
except BitNotSet:
user_id = None
try:
channel_id = iso.get_channel().strip()
except BitNotSet:
channel_id = '0000'
return channel_id, info['channel'], iso.get_stan(), \
iso.get_ntb(), str(iso.get_bank_id()), user_id, row_inq.tgl
def get_source(self, psppt):
source = self.get_iso_v1(psppt)
if source:
channel_kode, channel_nama, stan, ntb, bank_id, user_id, \
tgl_inquiry = source
else:
stan = ntb = bank_id = user_id = tgl_inquiry = None
channel_kode = '0000'
channel_nama = 'MANUAL'
return channel_kode, channel_nama, stan, ntb, bank_id, user_id, \
tgl_inquiry
def create_data(self, psppt): # Override
self.invoice_id = get_id(psppt)
sppt = self.get_sppt(psppt)
if not sppt:
msg = f'Invoice ID {self.invoice_id} tidak ada di tabel sppt'
raise InvalidSource(msg)
channel_kode, channel_nama, stan, ntb, bank_id, user_id, \
tgl_inquiry = self.get_source(psppt)
bayar = psppt.JML_SPPT_YG_DIBAYAR
denda = psppt.DENDA_SPPT or 0
pokok = sppt.PBB_YG_HARUS_DIBAYAR_SPPT
tgl = to_date(psppt.TGL_PEMBAYARAN_SPPT)
if ntb:
ntb = ntb.strip()
return dict(
kd_propinsi=psppt.KD_PROPINSI, kd_dati2=psppt.KD_DATI2,
kd_kecamatan=psppt.KD_KECAMATAN, kd_kelurahan=psppt.KD_KELURAHAN,
kd_blok=psppt.KD_BLOK, no_urut=psppt.NO_URUT,
kd_jns_op=psppt.KD_JNS_OP, thn_pajak_sppt=psppt.THN_PAJAK_SPPT,
pembayaran_sppt_ke=psppt.PEMBAYARAN_SPPT_KE, stan=stan, ntb=ntb,
jml_sppt_yg_dibayar=bayar, denda_sppt=denda, discount=0,
tgl_pembayaran_sppt=tgl, tgl_inquiry=tgl_inquiry,
tgl_rekam_byr_sppt=psppt.TGL_REKAM_BYR_SPPT,
nm_wp_sppt=sppt.NM_WP_SPPT, channel_kode=channel_kode,
channel_nama=channel_nama, bank_id=bank_id, user_id=user_id,
pbb_yg_harus_dibayar_sppt=pokok)
def get_report(self, psppt): # Override
q = self.rpt_session.query(Pbb).filter_by(
kd_propinsi=psppt.KD_PROPINSI,
kd_dati2=psppt.KD_DATI2,
kd_kecamatan=psppt.KD_KECAMATAN,
kd_kelurahan=psppt.KD_KELURAHAN,
kd_blok=psppt.KD_BLOK,
no_urut=psppt.NO_URUT,
kd_jns_op=psppt.KD_JNS_OP,
thn_pajak_sppt=psppt.THN_PAJAK_SPPT,
pembayaran_sppt_ke=psppt.PEMBAYARAN_SPPT_KE)
return q.first()
def get_last_time(self): # Override
return dmyhms(self.last_pay.TGL_REKAM_BYR_SPPT)
def get_filter_query(self, q): # Override
return q.filter(
Psppt.TGL_REKAM_BYR_SPPT >= self.tgl_awal,
Psppt.TGL_REKAM_BYR_SPPT < self.tgl_akhir + one_day)
def get_payment_query(self): # Override
q = self.prod_session.query(Psppt)
q = self.get_filter_query(q)
return q.order_by(Psppt.TGL_REKAM_BYR_SPPT)
import sys
from configparser import ConfigParser
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from zope.sqlalchemy import register
import transaction
from opensipkd.views.models import Conf as BaseConf
from ..models import Base
from ..common import (
my_registry,
append_csv,
)
def main(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)
Base.metadata.create_all(engine)
BaseConf.metadata.create_all(engine)
factory = sessionmaker(bind=engine)
my_registry['db_session'] = db_session = factory()
register(db_session)
with transaction.manager:
append_csv(BaseConf, 'conf.csv', ['nama'])
from configparser import RawConfigParser
import logging
import logging.config
def setup_logging(conf_file):
conf = RawConfigParser()
conf.read(conf_file)
d = {
'version': 1,
'formatters': {},
'handlers': {},
'loggers': {},
}
for section in conf.sections():
if section.find('formatter_') == 0:
name = section.split('formatter_')[1]
data = {'format': conf.get(section, 'format')}
d['formatters'][name] = data
elif section.find('handler_') == 0:
name = section.split('handler_')[1]
data = {'formatter': conf.get(section, 'formatter')}
data['class'] = 'logging.' + conf.get(section, 'class')
if conf.has_option(section, 'stream'):
data['stream'] = 'ext://' + conf.get(section, 'stream')
else:
data['filename'] = conf.get(section, 'filename')
d['handlers'][name] = data
elif section.find('logger_') == 0:
name = section.split('logger_')[1]
if name == 'root':
name = ''
data = {'level': conf.get(section, 'level')}
value = conf.get(section, 'handlers')
data['handlers'] = [x.strip() for x in value.split(',')]
d['loggers'][name] = data
logging.config.dictConfig(d)
import sys
import os
from configparser import ConfigParser
from argparse import ArgumentParser
from datetime import (
datetime,
date,
timedelta,
)
from opensipkd.iso8583.bjb.scripts.common import get_module_object
from .logger import setup_logging
def is_live(pid):
try:
os.kill(pid, 0)
except OSError:
return
return True
def read_pid_file(filename):
try:
with open(filename) as f:
s = f.read()
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()
with open(filename, 'w') as f:
f.write(str(pid))
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)
def get_parser():
pars = ArgumentParser()
pars.add_argument('conf')
pars.add_argument('--update-from-date')
return pars
def get_option(argv):
pars = get_parser()
return pars.parse_args(argv)
def main(argv=sys.argv):
option = get_option(argv[1:])
conf = ConfigParser()
conf.read(option.conf)
cf = dict(conf.items('main'))
if not make_pid_file(cf['pid_file']):
return
setup_logging(option.conf)
if option.update_from_date:
try:
days_ago = int(option.update_from_date)
cf['tgl_awal'] = date.today() + timedelta(days_ago)
except ValueError:
t = option.update_from_date.split(',')
cf['tgl_awal'] = datetime.strptime(t[0], '%d-%m-%Y')
if t[1:]:
cf['tgl_akhir'] = datetime.strptime(t[1], '%d-%m-%Y')
module = get_module_object(conf.get('main', 'module'))
app = module.App(cf)
app.run()
import sys
from datetime import (
date,
datetime,
)
from sqlalchemy import func from sqlalchemy import func
from sqlalchemy.exc import ProgrammingError from sqlalchemy.exc import ProgrammingError
import transaction import transaction
...@@ -11,7 +6,6 @@ from opensipkd.webr.models.default import ( ...@@ -11,7 +6,6 @@ from opensipkd.webr.models.default import (
Payment, Payment,
Invoice, Invoice,
) )
from iso8583_web.models.meta import Base as BaseConf
from opensipkd.iso8583.bjb.webr.structure import PAYMENT_CODE from opensipkd.iso8583.bjb.webr.structure import PAYMENT_CODE
from opensipkd.iso8583.bjb.webr.models import ( from opensipkd.iso8583.bjb.webr.models import (
LogMixin, LogMixin,
...@@ -21,9 +15,8 @@ from ..models import ( ...@@ -21,9 +15,8 @@ from ..models import (
Base, Base,
Webr, Webr,
) )
from .common import ( from ..common import (
BaseApp, BaseApp,
init_db as base_init_db,
BIT_18_NAMES, BIT_18_NAMES,
get_channel_name_by_row, get_channel_name_by_row,
one_day, one_day,
...@@ -41,8 +34,6 @@ class App(BaseApp): ...@@ -41,8 +34,6 @@ class App(BaseApp):
def __init__(self, argv): def __init__(self, argv):
super().__init__(argv) super().__init__(argv)
if not self.pid:
return
self.base_q_pay = self.prod_session.query(Payment) self.base_q_pay = self.prod_session.query(Payment)
try: try:
self.base_q_pay.first() self.base_q_pay.first()
...@@ -144,13 +135,3 @@ class App(BaseApp): ...@@ -144,13 +135,3 @@ class App(BaseApp):
orm = self.IsoLog orm = self.IsoLog
q = self.get_filter_query(q) q = self.get_filter_query(q)
return q.order_by(orm.created) return q.order_by(orm.created)
def main(argv=sys.argv[1:]):
app = App(argv)
if app.pid:
app.run()
def init_db(argv=sys.argv[1:]):
base_init_db(Base.metadata, BaseConf.metadata, argv)
[main]
service = payment_report.pbb.tangkab
db_url = odbc
report_db_url = postgresql://user:pass@localhost/payment_report
pid_file = /home/sugiana/tmp/pbb-report.pid
log_file = /home/sugiana/tmp/pbb-report.log
[odbc]
driver = FreeTDS
server = 192.168.168.31
database = payswitch
port = 5000
uid = FIXME
pwd = FIXME
[main] [main]
module = payment_report.pbb.default
models = sismiop.models.default models = sismiop.models.default
db_url = postgresql://user:pass@localhost/db db_url = postgresql://user:pass@localhost/db
#h2h_db_url = postgresql://user:pass@localhost/db #h2h_db_url = postgresql://user:pass@localhost/db
...@@ -7,3 +8,23 @@ report_db_url = postgresql://user:pass@localhost/db ...@@ -7,3 +8,23 @@ report_db_url = postgresql://user:pass@localhost/db
pid_file = /home/sugiana/tmp/pbb-report.pid pid_file = /home/sugiana/tmp/pbb-report.pid
log_file = /home/sugiana/log/pbb-report.log log_file = /home/sugiana/log/pbb-report.log
nip_pospbb = 888888888 nip_pospbb = 888888888
[formatter_generic]
format = %(asctime)s %(levelname)s %(name)s %(message)s
[formatter_simple]
format = %(asctime)s %(levelname)s %(message)s
[handler_console]
class = StreamHandler
stream = sys.stdout
formatter = generic
[handler_file]
class = FileHandler
filename = /home/sugiana/log/pbb-report.log
formatter = simple
[logger_root]
handlers = console, file
level = INFO
...@@ -34,11 +34,8 @@ setup( ...@@ -34,11 +34,8 @@ setup(
zip_safe=False, zip_safe=False,
entry_points={ entry_points={
'console_scripts': [ 'console_scripts': [
'payment_report_init_db = payment_report.scripts.common:init_db', 'payment_report_init_db = payment_report.scripts.init_db:main',
'pbb_report = payment_report.scripts.pbb:main', 'payment_report = payment_report.scripts.sync:main',
'bphtb_report = payment_report.scripts.bphtb:main',
'pad_report = payment_report.scripts.pad:main',
'webr_report = payment_report.scripts.webr:main',
'pbb_log = payment_report.scripts.pbb_log:main', 'pbb_log = payment_report.scripts.pbb_log:main',
'log2iso_init_db = payment_report.scripts.log2iso_init:main', 'log2iso_init_db = payment_report.scripts.log2iso_init:main',
'log2iso = payment_report.scripts.log2iso:main', 'log2iso = payment_report.scripts.log2iso:main',
......
sqlalchemy-sybase < 2
[main] [main]
module = payment_report.webr.default
db_url = postgresql://user:pass@localhost/db db_url = postgresql://user:pass@localhost/db
#va_db_url = postgresql://user:pass@localhost/db #va_db_url = postgresql://user:pass@localhost/db
report_db_url = postgresql://user:pass@localhost/db report_db_url = postgresql://user:pass@localhost/db
pid_file = /home/sugiana/tmp/webr-report.pid pid_file = /home/sugiana/tmp/webr-report.pid
log_file = /home/sugiana/log/webr-report.log log_file = /home/sugiana/log/webr-report.log
[formatter_generic]
format = %(asctime)s %(levelname)s %(name)s %(message)s
[formatter_simple]
format = %(asctime)s %(levelname)s %(message)s
[handler_console]
class = StreamHandler
stream = sys.stdout
formatter = generic
[handler_file]
class = FileHandler
filename = /home/sugiana/log/webr-report.log
formatter = simple
[logger_root]
handlers = console, file
level = INFO
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!