Ejemplo n.º 1
0
        def decorated_function(*args: List[Any], **kwargs: Dict[str, Any]) -> Callable:
            cache_key = key.format(id=request.full_path, locale=localization.get_locale())  # include querystring
            cache_policy = ''
            if not cacheable:
                cache_policy += ', no-store'  # tells the browser not to cache at all
            else:
                if must_revalidate:  # this looks contradicting if you haven't read the article.
                    # no-cache doesn't mean "don't cache", it means it must check
                    # (or "revalidate" as it calls it) with the server before
                    # using the cached resource
                    cache_policy += ', no-cache'
                else:
                    # Also must-revalidate doesn't mean "must revalidate", it
                    # means the local resource can be used if it's younger than
                    # the provided max-age, otherwise it must revalidate
                    cache_policy += ', must-revalidate'

                if client_only:
                    cache_policy += ', private'
                else:
                    cache_policy += ', public'

                actual_client_timeout = client_timeout
                actual_server_timeout = server_timeout
                if get_season_id() and get_season_id() != 0 and get_season_id() < seasons.current_season_num():
                    actual_client_timeout = 7 * 24 * 60 * 60
                    actual_server_timeout = 7 * 24 * 60 * 60

                cache_policy += ', max-age={client_timeout}'.format(client_timeout=actual_client_timeout)

            headers = {}
            cache_policy = cache_policy.strip(',')
            headers['Cache-Control'] = cache_policy
            now = datetime.datetime.utcnow()

            client_etag = request.headers.get('If-None-Match')

            response = CACHE.get(cache_key)  # type: ignore
            # Respect a hard refresh from the client, if sent.
            # Note: Safari/OSX does not send a Cache-Control (or any additional) header on a hard refresh so people using Safari can't bypass/refresh server cache.
            if response is not None and request.headers.get('Cache-Control', '') != 'no-cache':
                headers['X-Cache'] = 'HIT from Server'
                cached_etag = response.headers.get('ETag')
                if client_etag and cached_etag and client_etag == cached_etag:
                    headers['X-Cache'] = 'HIT from Client'
                    headers['X-Last-Modified'] = response.headers.get('X-LastModified')
                    response = make_response('', 304)
            else:
                response = make_response(f(*args, **kwargs))
                if response.status_code == 200 and request.method in ['GET', 'HEAD']:
                    headers['X-Cache'] = 'MISS'
                    # - Added the headers to the response object instead of the
                    # headers dict so they get cached too
                    # - If you can find any faster random algorithm go for it.
                    response.headers.add('ETag', binascii.hexlify(os.urandom(4)))
                    response.headers.add('X-Last-Modified', str(now))
                    CACHE.set(cache_key, response, timeout=actual_server_timeout)

            response.headers.extend(headers)
            return response
Ejemplo n.º 2
0
def card_history(c: Card) -> str:
    data: Dict[int, bool] = {}
    for format_name, status in c.legalities.items():
        if 'Penny Dreadful ' in format_name and status == 'Legal':
            season_id = seasons.SEASONS.index(
                format_name.replace('Penny Dreadful ', '')) + 1
            data[season_id] = True
    data[seasons.current_season_num()] = c.legalities.get('Penny Dreadful', None) == 'Legal'
    s = '   '
    for i in range(1, seasons.current_season_num() + 1):
        s += f'{i} '
        s += ':white_check_mark:' if data.get(i, False) else ':no_entry_sign:'
        s += '   '
    s = s.strip()
    s += '\n<' + fetcher.decksite_url('/seasons/all/cards/{name}/'.format(
        name=fetch_tools.escape(c.name, skip_double_slash=True))) + '>'
    return s
Ejemplo n.º 3
0
 def title(self) -> str:
     if not self.page_title():
         return 'pennydreadfulmagic.com'
     if get_season_id() == seasons.current_season_num():
         season = ''
     elif get_season_id() == 0:
         season = ' - All Time'
     else:
         season = ' - Season {n}'.format(n=get_season_id())
     return '{page_title}{season} – pennydreadfulmagic.com'.format(page_title=self.page_title(), season=season)
Ejemplo n.º 4
0
def before_request() -> Optional[wrappers.Response]:
    if not request.path.endswith('/'):
        return None  # Let flask do the redirect-routes-not-ending-in-slashes thing before we interfere with routing. Avoids #8277.
    if request.path.startswith('/seasons') and len(request.path) > len(
            '/seasons/') and get_season_id() >= seasons.current_season_num():
        return redirect(re.sub('/seasons/[^/]*', '', request.path))
    if request.path.startswith('/seasons/0'):
        return redirect(request.path.replace('/seasons/0', '/seasons/all'))
    g.p = perf.start()
    return None
Ejemplo n.º 5
0
def person_api(person: str, season_id: int = -1) -> Response:
    if season_id == -1:
        season_id = seasons.current_season_num()
    try:
        p = ps.load_person_by_discord_id_or_username(person, season_id)
        p.decks_url = url_for('person_decks_api',
                              person=person,
                              season_id=season_id)
        return return_json(p)
    except DoesNotExistException:
        return return_json(generate_error('NOTFOUND', 'Person does not exist'))
Ejemplo n.º 6
0
def seasonized_url(season_id: Union[int, str]) -> str:
    args = request.view_args.copy()
    if season_id == seasons.current_season_num():
        args.pop('season_id', None)
        endpoint = cast(str, request.endpoint).replace('seasons.', '')
    else:
        args['season_id'] = season_id
        prefix = '' if cast(str, request.endpoint).startswith('seasons.') else 'seasons.'
        endpoint = '{prefix}{endpoint}'.format(prefix=prefix, endpoint=request.endpoint)
    try:
        return url_for(endpoint, **args)
    except BuildError:
        return url_for(cast(str, request.endpoint))
Ejemplo n.º 7
0
def pd_rotation_changes(
        season_id: int) -> Tuple[Sequence[Card], Sequence[Card]]:
    # It doesn't really make sense to do this for 'all' so just show current season in that case.
    if season_id == 0:
        season_id = seasons.current_season_num()
    try:
        from_format_id = multiverse.get_format_id_from_season_id(
            int(season_id) - 1)
    except InvalidArgumentException:
        from_format_id = -1
    try:
        to_format_id = multiverse.get_format_id_from_season_id(season_id)
    except InvalidArgumentException:
        to_format_id = -1
    return changes_between_formats(from_format_id, to_format_id)
Ejemplo n.º 8
0
def h2h_api() -> Response:
    """
    Grab a slice of results from a 0-indexed resultset of head-to-head entries.
    Input:
        {
            'page': <int>,
            'pageSize': <int>,
            'personId': <int>,
            'sortBy': <str>,
            'sortOrder': <'ASC'|'DESC'>,
            'seasonId': <int|'all'>,
            'q': <str>
        }
    Output:
        {
            'page': <int>,
            'objects': [<entry>],
            'total': <int>
        }
    """
    order_by = query.head_to_head_order_by(request.args.get('sortBy'),
                                           request.args.get('sortOrder'))
    page_size = int(request.args.get('pageSize', DEFAULT_LIVE_TABLE_PAGE_SIZE))
    page = int(request.args.get('page', 0))
    start = page * page_size
    limit = f'LIMIT {start}, {page_size}'
    season_id = seasons.season_id(str(request.args.get('seasonId')), None)
    person_id = int(request.args.get('personId', 0))
    q = request.args.get('q', '').strip()
    where = query.text_match_where('opp.mtgo_username', q) if q else 'TRUE'
    entries = ps.load_head_to_head(person_id,
                                   where=where,
                                   order_by=order_by,
                                   limit=limit,
                                   season_id=season_id)
    for entry in entries:
        entry.opp_url = url_for('.person',
                                mtgo_username=entry.opp_mtgo_username,
                                season_id=None if season_id
                                == seasons.current_season_num() else season_id)
    total = ps.load_head_to_head_count(person_id=person_id,
                                       where=where,
                                       season_id=season_id)
    r = {'page': page, 'total': total, 'objects': entries}
    resp = return_json(r, camelize=True)
    resp.set_cookie('page_size', str(page_size))
    return resp
Ejemplo n.º 9
0
 def setup_active_seasons(self, seasons_active: Sequence[int]) -> None:
     total_seasons = len(seasons.SEASONS)
     cube_side_length = round(math.sqrt(total_seasons))
     self.seasons_active = []
     for i, setcode in enumerate(reversed(seasons.SEASONS)):
         season_id = total_seasons - i
         if season_id > seasons.current_season_num():
             continue
         active = season_id in seasons_active
         self.seasons_active.append({
             'season_id':
             season_id,
             'className':
             f'ss-{setcode.lower()} ' +
             ('ss-common' if active else 'inactive'),
             'url':
             url_for('seasons.person',
                     person_id=self.person.id,
                     season_id=season_id) if active else '',
             'edge': (i + 1) % cube_side_length == 0,
         })
Ejemplo n.º 10
0
def get_season_id() -> int:
    season_id = g.get('season_id', seasons.current_season_num())
    if season_id == 'all':
        return 0
    return season_id
Ejemplo n.º 11
0
 def __init__(self, person: ps.Person, cards: List[Card],
              archetypes: List[Archetype], all_archetypes: List[Archetype],
              your_cards: Dict[str,
                               List[str]], seasons_active: Sequence[int],
              season_id: Optional[int]) -> None:
     super().__init__()
     self.all_archetypes = all_archetypes
     self.person = person
     self.people = [person]
     self.decks = person.decks
     self.has_decks = len(person.decks) > 0
     self.archetypes = archetypes
     self.hide_person = True
     self.cards = cards
     self.show_seasons = True
     self.displayed_achievements = [{
         'title':
         a.title,
         'detail':
         titlecase.titlecase(a.display(self.person))
     } for a in Achievement.all_achievements if a.display(self.person)]
     self.achievements_url = url_for('.achievements')
     self.person_achievements_url = url_for('.person_achievements',
                                            person_id=person.id)
     colors: Dict[str, int] = {}
     for d in self.decks:
         for c in d.colors:
             colors[c] = colors.get(c, 0) + 1
     self.charts = [
         {
             'title':
             'Colors Played',
             'type':
             'horizontalBar',
             'labels':
             json.dumps(
                 ['White', 'Blue', 'Black', 'Red', 'Green', 'Colorless']),
             'series':
             json.dumps([
                 colors.get('W'),
                 colors.get('U'),
                 colors.get('B'),
                 colors.get('R'),
                 colors.get('G'),
                 colors.get('C')
             ]),
             'options':
             json.dumps({
                 'responsive': True,
                 'scales': {
                     'xAxes': [{
                         'ticks': {
                             'precision': 0
                         }
                     }]
                 }
             }),  # Only display whole numbers on x axis.
         },
     ]
     self.add_note_url = url_for('post_player_note')
     self.matches_url = url_for(
         '.person_matches',
         person_id=person.id,
         season_id=None
         if season_id == seasons.current_season_num() else season_id)
     self.is_person_page = True
     self.trailblazer_cards = oracle.load_cards(your_cards['trailblazer'])
     self.has_trailblazer_cards = len(self.trailblazer_cards) > 0
     self.unique_cards = oracle.load_cards(your_cards['unique'])
     self.has_unique_cards = len(self.unique_cards) > 0
     self.cards += self.trailblazer_cards + self.unique_cards
     self.setup_active_seasons(seasons_active)
Ejemplo n.º 12
0
def random_legal_deck() -> Optional[Deck]:
    where = 'd.reviewed AND d.created_date > (SELECT start_date FROM season WHERE number = {current_season_num})'.format(current_season_num=seasons.current_season_num())
    having = '(d.competition_id NOT IN ({active_competition_id_query}) OR SUM(cache.wins + cache.draws + cache.losses) >= 5)'.format(active_competition_id_query=active_competition_id_query())
    try:
        return deck.load_decks(where=where, having=having, order_by='RAND()', limit='LIMIT 1')[0]
    except IndexError:
        # For a short while at the start of a season there are no decks that match the WHERE/HAVING clauses.
        return None
Ejemplo n.º 13
0
def test_seasonized_url_for_app() -> None:
    with APP.test_request_context('/decks/'):
        assert view.seasonized_url(1) == '/seasons/1/decks/'
        assert view.seasonized_url(seasons.current_season_num()) == '/decks/'
Ejemplo n.º 14
0
def test_seasonized_url_simple() -> None:
    with APP.test_request_context('/tournaments/'):
        assert view.seasonized_url(1) == '/tournaments/'
        assert view.seasonized_url(
            seasons.current_season_num()) == '/tournaments/'