Skip to content

Commit

Permalink
v2 beta (#340)
Browse files Browse the repository at this point in the history
* beta start

* update lock

* get ticks

* time since cols

* fix error

* fix the test again

* formatting

* adding clock
  • Loading branch information
pnxenopoulos authored Jun 23, 2024
1 parent 92686b3 commit 3aedb12
Show file tree
Hide file tree
Showing 16 changed files with 493 additions and 312 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ jobs:
- name: Test
shell: bash
run: |
poetry run coverage run -m pytest --durations=10 -s --traceconfig --log-cli-level=DEBUG tests/
poetry run coverage run -m pytest --durations=10
poetry run coverage report -m
# - name: Archive code coverage results
Expand Down
13 changes: 0 additions & 13 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,19 +32,6 @@ jobs:
- name: Install awpy
run: |
poetry install --no-interaction
# - name: Publish to test PyPI
# run: |
# poetry publish --build --repository testpypi --username __token__ --password ${{ secrets.TEST_PYPI_API_TOKEN }} -vvv

- name: Debug Step
run: |
echo "Listing directory contents"
ls -la
echo "Checking Poetry version"
poetry --version
echo "Checking installed packages"
poetry show
- name: Publish to PyPI
run: |
Expand Down
48 changes: 34 additions & 14 deletions awpy/demo.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,18 @@
from demoparser2 import DemoParser # pylint: disable=E0611
from loguru import logger

from awpy.parsers import (
from awpy.parsers.clock import parse_times
from awpy.parsers.events import (
parse_bomb,
parse_damages,
parse_grenades,
parse_infernos,
parse_kills,
parse_rounds,
parse_smokes,
parse_ticks,
parse_weapon_fires,
)
from awpy.parsers.rounds import parse_rounds
from awpy.parsers.ticks import parse_ticks
from awpy.utils import apply_round_num

PROP_WARNING_LIMIT = 40
Expand Down Expand Up @@ -193,21 +194,40 @@ def _parse_events(self) -> None:
raise ValueError(no_events_error_msg)

if self.parse_rounds is True:
self.rounds = parse_rounds(self.parser)
self.rounds = parse_rounds(
self.parser, self.events
) # Must pass parser for round start/end events

self.kills = apply_round_num(self.rounds, parse_kills(self.events))
self.damages = apply_round_num(self.rounds, parse_damages(self.events))
self.bomb = apply_round_num(self.rounds, parse_bomb(self.events))
self.smokes = apply_round_num(
self.rounds, parse_smokes(self.events), tick_col="start_tick"
self.kills = parse_times(
apply_round_num(self.rounds, parse_kills(self.events)), self.rounds
)
self.infernos = apply_round_num(
self.rounds, parse_infernos(self.events), tick_col="start_tick"
self.damages = parse_times(
apply_round_num(self.rounds, parse_damages(self.events)), self.rounds
)
self.weapon_fires = apply_round_num(
self.rounds, parse_weapon_fires(self.events)
self.bomb = parse_times(
apply_round_num(self.rounds, parse_bomb(self.events)), self.rounds
)
self.smokes = parse_times(
apply_round_num(
self.rounds, parse_smokes(self.events), tick_col="start_tick"
),
self.rounds,
tick_col="start_tick",
)
self.infernos = parse_times(
apply_round_num(
self.rounds, parse_infernos(self.events), tick_col="start_tick"
),
self.rounds,
tick_col="start_tick",
)
self.weapon_fires = parse_times(
apply_round_num(self.rounds, parse_weapon_fires(self.events)),
self.rounds,
)
self.grenades = parse_times(
apply_round_num(self.rounds, parse_grenades(self.parser)), self.rounds
)
self.grenades = apply_round_num(self.rounds, parse_grenades(self.parser))

# Parse ticks
if self.parse_ticks is True:
Expand Down
1 change: 1 addition & 0 deletions awpy/parsers/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Module for specific parsing functions."""
120 changes: 120 additions & 0 deletions awpy/parsers/clock.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
"""Module for time and clock parsing functions."""

import math
from typing import Literal, Union

import pandas as pd

ROUND_START_DEFAULT_TIME_IN_SECS = 20
FREEZE_DEFAULT_TIME_IN_SECS = 115
BOMB_DEFAULT_TIME_IN_SECS = 40


def parse_clock(
seconds_since_phase_change: int,
max_time_ticks: Union[Literal["start", "freeze", "bomb"], int],
tick_rate: int = 64,
) -> str:
"""Parse the remaining time in a round or phase to a clock string.
Args:
seconds_since_phase_change (int): The number of seconds since the phase change.
max_time_ticks (Union[Literal['start', 'freeze', 'bomb'], int]): The maximum
time in ticks for the phase.
tick_rate (int, optional): The tick rate of the server. Defaults to 64.
Returns:
str: The remaining time in MM:SS format.
"""
if max_time_ticks == "start":
max_time_ticks = ROUND_START_DEFAULT_TIME_IN_SECS * tick_rate
elif max_time_ticks == "freeze":
max_time_ticks = FREEZE_DEFAULT_TIME_IN_SECS * tick_rate
elif max_time_ticks == "bomb":
max_time_ticks = BOMB_DEFAULT_TIME_IN_SECS * tick_rate

# Calculate the remaining time in ticks
remaining_ticks = max_time_ticks - seconds_since_phase_change

# Convert remaining ticks to total seconds
remaining_seconds = remaining_ticks / tick_rate

# Round up the seconds
remaining_seconds = math.ceil(remaining_seconds)

# Calculate minutes and seconds
minutes = remaining_seconds // 60
seconds = remaining_seconds % 60

# Format as MM:SS with leading zeros
return f"{int(minutes):02}:{int(seconds):02}"


def _find_clock_time(row: pd.Series) -> str:
"""Find the clock time for a row.
Args:
row: A row from a dataframe with ticks_since_* columns.
"""
times = {
"start": row["ticks_since_round_start"],
"freeze": row["ticks_since_freeze_time_end"],
"bomb": row["ticks_since_bomb_plant"],
}
# Filter out NA values and find the key with the minimum value
min_key = min((k for k in times if pd.notna(times[k])), key=lambda k: times[k])
return parse_clock(times[min_key], min_key)


def parse_times(
df: pd.DataFrame, rounds_df: pd.DataFrame, tick_col: str = "tick"
) -> pd.DataFrame:
"""Adds time_since_* columns to the dataframe.
Args:
df (pd.DataFrame): The dataframe to add the time columns to.
rounds_df (pd.DataFrame): The rounds dataframe.
tick_col (str): The column name of the tick column.
Returns:
pd.DataFrame: The dataframe with the timesince_* columns added.
"""
if tick_col not in df.columns:
tick_col_missing_msg = f"{tick_col} not found in dataframe."
raise ValueError(tick_col_missing_msg)

df_with_round_info = df.merge(rounds_df, on="round", how="left")
df_with_round_info["ticks_since_round_start"] = (
df_with_round_info[tick_col] - df_with_round_info["start"]
)
df_with_round_info["ticks_since_freeze_time_end"] = (
df_with_round_info[tick_col] - df_with_round_info["freeze_end"]
)
df_with_round_info["ticks_since_bomb_plant"] = (
df_with_round_info[tick_col] - df_with_round_info["bomb_plant"]
)

# Apply the function to the selected columns
for col in df_with_round_info.columns:
if col.startswith("ticks_since_"):
df_with_round_info[col] = (
df_with_round_info[col]
.map(lambda x: pd.NA if x < 0 else x)
.astype(pd.Int64Dtype())
)

df_with_round_info = df_with_round_info.drop(
columns=[
"start",
"freeze_end",
"end",
"official_end",
"winner",
"reason",
"bomb_plant",
]
)

df_with_round_info["clock"] = df_with_round_info.apply(_find_clock_time, axis=1)

return df_with_round_info
Loading

0 comments on commit 3aedb12

Please sign in to comment.