summaryrefslogtreecommitdiff
path: root/bbo_finder.py
blob: 3931b1fa8f5ec608b9329b7e82b4bbfb7f8e6f0d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
# coding: utf-8
import csv, io, os, re, sys
from datetime import datetime, date, timedelta
from urllib.parse import urlparse, parse_qs, unquote
from xml.etree import ElementTree
from tempfile import NamedTemporaryFile

import requests
import requests_cache
from requests.auth import HTTPBasicAuth
from requests.adapters import HTTPAdapter
from requests.packages.urllib3.util.retry import Retry
from bs4 import BeautifulSoup as bs

from dealconvert.formats.lin import LINFormat
from dealconvert.formats.pbn import PBNFormat
from bs_api.client import BsApiClient
from usebio.bs.result import Importer as XMLImporter

lin_converter = LINFormat()
api_client = BsApiClient(api_username=os.getenv('BSAPI_USERNAME'), api_password=os.getenv('BSAPI_PASSWORD'))

requests_cache.install_cache('bbo_cache', backend='sqlite', expire_after=60*60*24*2, allowable_codes=(200, 404, ))

http = requests_cache.CachedSession()
retries = Retry(total=5, backoff_factor=15, status_forcelist=[503])
http.mount('https://', HTTPAdapter(max_retries=retries))

http.headers.update({'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4300.0 Safari/537.36'})

with http.cache_disabled():
    login_form = http.get('https://www.bridgebase.com/myhands/myhands_login.php', params={
        't': '/myhands/index.php?'
    })
login = http.post('https://www.bridgebase.com/myhands/myhands_login.php',
                  data={
                      'username': os.getenv('BBO_USERNAME'),
                      'password': os.getenv('BBO_PASSWORD'),
                      't': '/myhands/index.php?',
                      'count': 1,
                      'submit': 'Login',
                      'keep': 'on'
                  })


def get_url_params(url):
    return parse_qs(urlparse(url).query)

def get_bbo_url_content(url, params=None):
    url = '%s/%s' % (
        'https://www.bridgebase.com/myhands',
        url)
    params.update({'offset': 0})
    r = http.get(
        url,
        params=params
    )
    r.raise_for_status()
    if r.status_code != 200:
        raise IOError('Logged out')
    return r.text

def get_timeframe(days=7):
    if len(sys.argv) < 3:
        end_time = datetime.combine(date.today(), datetime.min.time())
    else:
        end_time = datetime.strptime('%s 00:00:00' % (sys.argv[2]), '%Y-%m-%d %H:%M:%S')
    start_time = end_time - timedelta(days=days)
    return start_time, end_time

def get_tournaments_for_bbo_user(username, days=7):
    start_time, end_time = get_timeframe(days)
    content = get_bbo_url_content('hands.php', {
        'username': username,
        'start_time': int(start_time.timestamp()),
        'end_time': int(end_time.timestamp())
    })
    tree = bs(content, 'lxml')
    tournaments = []
    for link in tree.select(
            'tr.tourneySummary td.tourneyName a[href]'):
        t_url = urlparse(link['href'])
        t_id = parse_qs(t_url.query)['t'][0]
        tournaments.append((t_id, link.text))
    return tournaments

def get_board_layout_str(board_layout):
    return ' '.join(['.'.join([''.join([card for card in suit]) for suit in hand]) for hand in board_layout])

def get_boards_for_tournament(t_id):
    r = http.get('https://webutil.bridgebase.com/v2/tview.php',
                 params={'t': t_id})
    try:
        summary_link = bs(r.text, 'lxml').select('td a[href]')[0]['href']
    except IndexError:
        return []
    tour_params = get_url_params(summary_link)
    trav_content = get_bbo_url_content('hands.php', params=tour_params)
    lin_links = bs(trav_content, 'lxml').select('td.movie a[onclick]')
    boards = []
    for lin_link in lin_links:
        lin_link = lin_link['onclick']
        lin_parts = re.match(r'hv_popuplin\((.*)\)', lin_link)
        if lin_parts:
            lin_record = unquote(lin_parts.group(1).split('|')[-1]).split('|')
            board_layout = None
            board_number = None
            for field in lin_record:
                if not board_layout and ',' in field:
                    board_layout = lin_converter._parse_md_field(field)
                if not board_number and 'Board ' in field:
                    board_number = int(field.replace('Board ', ''))
            if board_layout and board_number:
                board_layout = get_board_layout_str(board_layout)
                boards.append((board_number, board_layout))
    return boards

def get_bs_tournament_data(t_id):
    r = requests.get('https://r.bridgespider.com/%d/xml' % (t_id))
    r.raise_for_status()
    tmp = NamedTemporaryFile(delete=False)
    tmp.write(r.content)
    tmp.close()
    bs_result = XMLImporter(tmp.name, autoparse=False)
    os.unlink(tmp.name)
    bs_result.parse_data(results=False)
    players = set()
    for pair in bs_result.pairs.values():
        for player in pair.players:
            if player.pid:
                players.add((player.pid, player.fullname))
    return {
        'name': bs_result.club.session_info.event_name,
        'players': list(players)
    }

def get_bs_tournament_boards(t_id):
    r = requests.get('https://r.bridgespider.com/%d/pbn' % (t_id))
    r.raise_for_status()
    string = io.StringIO(r.text)
    pbn_format = PBNFormat()
    boards = []
    for deal in pbn_format.parse_content(string):
        boards.append((deal.number, get_board_layout_str(deal.hands)))
    return boards

def get_bs_tournaments(days=7):
    start_time, end_time = get_timeframe(days)
    tournaments = []
    for t_id in api_client.get_tournaments(start_time, end_time):
        try:
            tournaments.append((t_id, get_bs_tournament_data(t_id), get_bs_tournament_boards(t_id)))
        except requests.exceptions.HTTPError:
            pass
    return tournaments

def get_bbo_user_db():
    r = requests.get('https://www.bridgenet.pl/beta/admin/nickcezar.php',
                     auth=HTTPBasicAuth(os.getenv('BRIDGENET_USERNAME'), os.getenv('BRIDGENET_PASSWORD')))
    db = {}
    for row in csv.reader(r.content.decode().splitlines()):
        try:
            nick = row[0]
            pid = int(row[1])
            if pid:
                if pid not in db:
                    db[pid] = []
                db[pid].append(nick)
        except ValueError:
            pass
    return db

bbo_user_db = get_bbo_user_db()

def get_bbo_user_for_pid(pid):
    return bbo_user_db.get(pid, [])


def __main__():
    days = int(sys.argv[1]) if len(sys.argv) > 1 else 7

    boards = {}
    bs_players = set()
    tournaments = {}

    bs_tournaments = get_bs_tournaments(days=days)
    for tour in bs_tournaments:
        tournament = ('bs', tour[0], tour[1]['name'])
        tournaments[tournament] = []
        for board in tour[2]:
            if board[1] not in boards:
                boards[board[1]] = set()
            boards[board[1]].add((tournament, board[0]))
        for player in tour[1]['players']:
            for bbo_user in get_bbo_user_for_pid(player[0]):
                p = (bbo_user, player[0], player[1])
                bs_players.add(p)
                tournaments[tournament].append(p)

    for player in bs_players:
        for t in get_tournaments_for_bbo_user(player[0], days=days):
            tournament = ('bbo', t[0], t[1])
            if tournament not in tournaments:
                tournaments[tournament] = []
                for board in get_boards_for_tournament(t[0]):
                    if board[1] not in boards:
                        boards[board[1]] = set()
                    boards[board[1]].add((tournament, board[0]))
            tournaments[tournament].append(player)

    for board, tours in boards.items():
        if len(tours) > 1:
            players = {}
            for t in tours:
                for pl in tournaments[t[0]]:
                    if pl not in players:
                        players[pl] = []
                    players[pl].append(t[0])
            print('Rozdanie %s zagrano więcej niż jeden raz:' % (board))
            for tour in tours:
                print(' - turniej %s %s (%s) jako rozdanie nr %d' % (tour[0][0].upper(), tour[0][1], tour[0][2], tour[1]))
            print('Gracze, którzy rozegrali rozdanie więcej niż jeden raz:')
            for p, t in players.items():
                if len(t) > 1:
                    print(' - #%d %s (%s)' % (p[1], p[2], p[0]))
            print('')

if __name__ == '__main__':
    __main__()