-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #3 from Hack-The-Travel/develop
Release v1.0.0 - Birth
- Loading branch information
Showing
13 changed files
with
342 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,8 @@ | ||
# general things to ignore | ||
*.py[cod] | ||
.pytest_cache/ | ||
|
||
conf.py | ||
|
||
# Mr Developer | ||
.idea/ | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
.. :changelog: | ||
Release History | ||
=============== | ||
|
||
1.0.0 (2018-07-02) | ||
++++++++++++++++++ | ||
|
||
* Birth! | ||
|
||
|
||
0.0.1 (2018-06-23) | ||
++++++++++++++++++ | ||
|
||
* Frustration | ||
* Conception |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,37 @@ | ||
sirena-quota | ||
============ | ||
|
||
It's the best way to control tickets quota in Sirena. | ||
It's the best way to control tickets quota in Sirena. | ||
Notice, these scripts works with Sirena Web Services (SWC), not with Sirena XML Gate. | ||
|
||
Behold, the power of: | ||
``` | ||
$ python checker.py | ||
OTA.UT - ok | ||
$ python informer.py | ||
``` | ||
|
||
|
||
## Installation | ||
First, initialize your virtual environment | ||
``` | ||
$ virtualenv .ve | ||
$ source .ve/bin/activate | ||
``` | ||
|
||
Install dependencies | ||
``` | ||
$ pip install -r requirements.txt | ||
``` | ||
|
||
Setup configuration | ||
``` | ||
$ cp conf.sample.py conf.py | ||
$ vim conf.py | ||
✨🎩✨ | ||
``` | ||
|
||
Finally, setup sqlite database | ||
``` | ||
$ python manage.py setup | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
# -*- coding: utf-8 -*- | ||
import re | ||
import requests | ||
import logging | ||
from utils import db_execute | ||
import conf | ||
|
||
|
||
def extract_ticket_quota(ticket_quota_response): | ||
matches = re.findall(r'<quota>(\d+)<\/quota>', ticket_quota_response) | ||
return int(matches[0]) | ||
|
||
|
||
def get_ticket_quota(user, password, gateway='https://ws.sirena-travel.ru/swc-main/bookingService'): | ||
"""Returns ticket quota for PoS (ППр). | ||
This method calls `getTicketQuota` method of Sirena WS. | ||
:param user: Sirena WS user with supervisor permission in the PoS. | ||
:param password: user password. | ||
:param gateway: (optional) Sirena WS gateway to call getTicketQuota method. | ||
:return: ticket quota. | ||
:rtype: int | ||
""" | ||
rq = '''<soapenv:Envelope | ||
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" | ||
xmlns:ser="http://service.swc.comtech/"> | ||
<soapenv:Header/> | ||
<soapenv:Body> | ||
<ser:getTicketQuota> | ||
<dynamicId>0</dynamicId> | ||
</ser:getTicketQuota> | ||
</soapenv:Body> | ||
</soapenv:Envelope>''' | ||
r = requests.post(gateway, auth=(user, password), data=rq) | ||
r.raise_for_status() | ||
return extract_ticket_quota(r.text) | ||
|
||
|
||
def save_check(db_name, account_code, ticket_quota): | ||
db_execute( | ||
db_name, | ||
'''INSERT INTO quota_check (account, quota) | ||
VALUES ('{account}', {quota}) | ||
'''.format(account=account_code, quota=ticket_quota) | ||
) | ||
|
||
|
||
if __name__ == '__main__': | ||
for code, account in conf.accounts.items(): | ||
try: | ||
save_check( | ||
conf.db_name, | ||
code, | ||
get_ticket_quota(account['user'], account['password']) | ||
) | ||
print('{:20} - ok'.format(code)) | ||
except Exception as e: | ||
print('{:20} - error'.format(code)) | ||
logging.critical(e, exc_info=True) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
# -*- coding: utf-8 -*- | ||
accounts = { | ||
'OTA.TCH': { | ||
'user': 'ota_grs101', | ||
'password': 'secret', | ||
}, | ||
'OTA.UT': { | ||
'user': 'ota_grs102', | ||
'password': 'newsecret', | ||
}, | ||
} | ||
sender = ('Anton Yakovlev', '[email protected]') | ||
recipient_info = '[email protected]' | ||
recipient_alert = '[email protected]' | ||
db_name = 'db/quota.db' | ||
smtp_gateway= 'email-smtp.eu-west-1.amazonaws.com' | ||
smtp_port = 587 | ||
smtp_user = 'aws_ses_user' | ||
smtp_password = 'aws_ses_user_password' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
# Ignore everything in this directory | ||
* | ||
# Except this file | ||
!.gitignore |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
# -*- coding: utf-8 -*- | ||
import smtplib | ||
import email.utils | ||
from email.mime.multipart import MIMEMultipart | ||
from email.mime.text import MIMEText | ||
import time | ||
from utils import db_execute | ||
import conf | ||
|
||
|
||
def prepare_body(accounts_info, sender_name): | ||
"""Returns message body in text and html format. | ||
Structure of accounts_info | ||
>>> accounts_info = [{ | ||
>>> 'code': 'OTA.TCH', | ||
>>> 'quota': 985, | ||
>>> 'datetime': '2018-06-23 19:26:16 (MSK)', | ||
>>> 'alert': False, | ||
>>> }] | ||
:param accounts_info: list, accounts info, see above description of element structure. | ||
:param sender_name: str, name of sender used in signature. | ||
:return: tuple of two strings, (body_text, body_html) | ||
:rtype: tuple | ||
""" | ||
status_msg = '' | ||
alert_accounts = list() | ||
for account in accounts_info: | ||
if account['alert']: | ||
alert_accounts.append(account['code']) | ||
status_msg += '{account} - {quota}, проверено {dt}\r\n'\ | ||
.format(account=account['code'], quota=account['quota'], dt=account['datetime']) | ||
alert_msg = '' | ||
if len(alert_accounts): | ||
alert_msg = 'Срочно запросите квоту для: ' + ', '.join(alert_accounts) + '!\r\n\r\n' | ||
body_text = ( | ||
'Приветствую\r\n\r\n' | ||
'{alert}' | ||
'Состояние стоков:\r\n{status_msg}' | ||
'\r\n\r\n' | ||
'--\r\n' | ||
'Дружески,\r\n' | ||
'{sender}' | ||
) | ||
body_html = ( | ||
'<html>' | ||
'<head></head>' | ||
'<body>' | ||
'<p>Приветствую</p>' | ||
'{alert}' | ||
'<p>Состояние стоков:<br />{status_msg}</p>' | ||
'<p>' | ||
'<br /><br />' | ||
'--<br />' | ||
'Дружески,<br />' | ||
'{sender}' | ||
'</p>' | ||
'</body>' | ||
'</html>' | ||
) | ||
return ( | ||
body_text.format(alert=alert_msg, status_msg=status_msg, sender=sender_name), | ||
body_html.format(alert=alert_msg, status_msg=status_msg.replace('\r\n', '<br />'), sender=sender_name) | ||
) | ||
|
||
|
||
def send_mail(sender, recipient, subject, body, smtp_user, smtp_password, | ||
smtp_gateway='email-smtp.eu-west-1.amazonaws.com', smtp_port=587): | ||
"""Sends mail via SMTP server. | ||
:param sender: tuple of two strings, ('Sender Name', '[email protected]') | ||
:param recipient: str, recipient email, e.g. '[email protected]' | ||
:param subject: str, email subject | ||
:param body: tuple of two strings, (body_text, body_html) | ||
:param smtp_gateway: str, smtp server endpoint | ||
:param smtp_port: int, smtp server port | ||
:param smtp_user: str, login of smtp user | ||
:param smtp_password: str, password of smtp user | ||
""" | ||
body_text, body_html = body | ||
msg = MIMEMultipart('alternative') | ||
msg['Subject'] = subject | ||
msg['From'] = email.utils.formataddr(sender) | ||
msg['To'] = recipient | ||
msg.attach(MIMEText(body_text, 'plain')) | ||
msg.attach(MIMEText(body_html, 'html')) | ||
server = smtplib.SMTP(smtp_gateway, smtp_port) | ||
server.ehlo() | ||
server.starttls() | ||
server.ehlo() | ||
server.login(smtp_user, smtp_password) | ||
server.sendmail(sender[1], recipient, msg.as_string()) | ||
server.close() | ||
|
||
|
||
if __name__ == '__main__': | ||
rows = db_execute( | ||
conf.db_name, | ||
'SELECT account, quota, created_at FROM quota_check GROUP BY account HAVING MAX(created_at)' | ||
) | ||
info_items = list() | ||
alert = False | ||
for row in rows: | ||
if row[0] not in conf.accounts: | ||
continue # unknown account code | ||
info_items.append({ | ||
'code': row[0], | ||
'quota': row[1], | ||
'datetime': time.strftime('%Y-%m-%d %H:%M:%S (%Z)', time.localtime(row[2])), | ||
'alert': row[1] <= conf.accounts[row[0]].get('alert', 0), | ||
}) | ||
alert = info_items[-1]['alert'] or alert | ||
send_mail(conf.sender, conf.recipient_info, 'Состояние стоков в Сирене', | ||
prepare_body(info_items, conf.sender[0]), conf.smtp_user, conf.smtp_password) | ||
if alert: | ||
send_mail(conf.sender, conf.recipient_alert, 'Срочно пополните сток в Сирене', | ||
prepare_body(info_items, conf.sender[0]), conf.smtp_user, conf.smtp_password) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
# -*- coding: utf-8 -*- | ||
import argparse | ||
from utils import db_execute | ||
from conf import db_name | ||
|
||
|
||
def setup_store(db_name): | ||
db_execute( | ||
db_name, | ||
'''CREATE TABLE IF NOT EXISTS quota_check ( | ||
id INTEGER PRIMARY KEY, | ||
account VARCHAR, | ||
quota INTEGER, | ||
created_at integer(4) not null default (strftime('%s','now')) | ||
)''' | ||
) | ||
|
||
|
||
def drop_store(db_name): | ||
db_execute(db_name, 'DROP TABLE IF EXISTS quota_check') | ||
|
||
|
||
def parse_args(): | ||
parser = argparse.ArgumentParser() | ||
parser.add_argument('command', type=str, choices=['setup', 'drop'], | ||
help='what to do with store of quota checks') | ||
return parser.parse_args() | ||
|
||
|
||
if __name__ == '__main__': | ||
args = parse_args() | ||
if args.command == 'setup': | ||
setup_store(db_name) | ||
elif args.command == 'drop': | ||
drop_store(db_name) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
requests==2.19.1 | ||
pytest==3.6.2 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
# -*- coding: utf-8 -*- | ||
from .test_checker import TestChecker |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
# -*- coding: utf-8 -*- | ||
import pytest | ||
from checker import extract_ticket_quota | ||
|
||
|
||
class TestChecker: | ||
@pytest.mark.parametrize('rs', [ | ||
'<quota>958</quota>', | ||
'<quota>0958</quota>' | ||
]) | ||
def test_extract_quota(self, rs): | ||
assert extract_ticket_quota(rs) == 958 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
# -*- coding: utf-8 -*- | ||
import sqlite3 | ||
import os | ||
import pytest | ||
from manage import setup_store | ||
|
||
|
||
class TestDB(): | ||
db_name = 'db/test.db' | ||
|
||
def test_init_db(self): | ||
try: | ||
os.remove(self.db_name) | ||
except OSError: | ||
pytest.fail('Unexpected error trying to delete {}'.format(self.db_name), pytrace=True) | ||
conn = sqlite3.connect(self.db_name) | ||
conn.close() | ||
assert os.path.isfile(self.db_name) | ||
|
||
def test_setup_store(self): | ||
try: | ||
setup_store(self.db_name) | ||
except sqlite3.Error: | ||
pytest.fail('Unexpected error trying to setup store', pytrace=True) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
# -*- coding: utf-8 -*- | ||
import sqlite3 | ||
|
||
|
||
def db_execute(db_name, query): | ||
"""Executes SQL query in sqlite database.""" | ||
conn = sqlite3.connect(db_name) | ||
cursor = conn.cursor() | ||
cursor.execute(query) | ||
rows = cursor.fetchall() | ||
conn.commit() | ||
conn.close() | ||
return rows |