Skip to content

Commit

Permalink
v2.0.0a2 (pnxenopoulos#331)
Browse files Browse the repository at this point in the history
* redo docs

* cli introduction

* adds cli test module

* test cli rework

* fix line length

* more formatting
  • Loading branch information
pnxenopoulos authored and rorybush committed Jul 15, 2024
1 parent c196e42 commit 495dfed
Show file tree
Hide file tree
Showing 11 changed files with 397 additions and 21 deletions.
31 changes: 31 additions & 0 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: docs

on:
push:
tags:
- 'release-v*.*.*'

jobs:
build-and-create-docs:
runs-on: ubuntu-latest

steps:
- uses: actions/setup-python@v5
- uses: actions/checkout@v4
with:
fetch-depth: 0 # otherwise, you will failed to push refs to dest repo
- name: Build and commit
uses: sphinx-notes/pages@v2
- name: Push changes
uses: ad-m/github-push-action@master
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
branch: gh-pages
if: github.event_name != 'pull_request'
- name: Trigger RTDs build
uses: dfm/rtds-action@v1
with:
webhook_url: ${{ secrets.RTDS_WEBHOOK_URL }}
webhook_token: ${{ secrets.RTDS_WEBHOOK_TOKEN }}
commit_ref: ${{ github.ref }}
if: github.event_name != 'pull_request'
10 changes: 1 addition & 9 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:
run: |
poetry config virtualenvs.create true --local
poetry config virtualenvs.in-project true --local
poetry config repositories.testpypi https://test.pypi.org/legacy/
poetry config repositories.testpypi https://test.pypi.org/simple/
- name: Install awpy
run: |
Expand All @@ -37,14 +37,6 @@ jobs:
run: |
poetry publish --build --repository testpypi --username __token__ --password ${{ secrets.TEST_PYPI_API_TOKEN }}
- name: Check that installation from test PyPI passes
run: |
poetry add awpy --source testpypi
- name: Publish to PyPI
run: |
poetry publish --build --username __token__ --password ${{ secrets.PYPI_API_TOKEN }}
- name: Check that installation from PyPI passes
run: |
poetry add awpy
14 changes: 9 additions & 5 deletions .readthedocs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,15 @@ version: 2
build:
os: ubuntu-22.04
tools:
python: "3.11"
# You can also specify other tool versions:
# nodejs: "16"
# rust: "1.55"
# golang: "1.17"
python: "3.9"
post_create_environment:
# Install poetry
- pip install poetry
# Tell poetry to not use a virtual environment
- poetry config virtualenvs.create false
post_install:
# Install dependencies
- poetry install

# Build documentation in the docs/ directory with Sphinx
sphinx:
Expand Down
56 changes: 56 additions & 0 deletions awpy/cli.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""Command-line interface for Awpy."""

<<<<<<< HEAD
from pathlib import Path
from typing import Literal, Optional

Expand All @@ -11,13 +12,26 @@
from awpy import Demo
from awpy.data import AWPY_DATA_DIR
from awpy.data.usd_data import USD_LINKS
=======
import json
import os
import tempfile
import zipfile
from pathlib import Path

import click
from loguru import logger

from awpy import Demo
>>>>>>> 9afdcf2 (v2.0.0a2 (#331))


@click.group()
def awpy() -> None:
"""A simple CLI interface for Awpy."""


<<<<<<< HEAD
@awpy.command(
help="Get Counter-Strike 2 resources like map images, nav meshes or usd files."
)
Expand Down Expand Up @@ -117,3 +131,45 @@ def parse(
other_props=other_props[0].split(",") if other_props else None,
)
demo.compress(outpath=outpath)
=======
@awpy.command(help="Parse a Counter-Strike 2 demo file.")
@click.argument("demo", type=click.Path(exists=True))
@click.option("--verbose", is_flag=True, default=False, help="Enable verbose mode.")
@click.option("--noticks", is_flag=True, default=False, help="Disable tick parsing.")
def parse(demo: Path, *, verbose: bool = False, noticks: bool = False) -> None:
"""Parse a file given its path."""
demo_path = Path(demo) # Pathify
demo = Demo(path=demo_path, verbose=verbose, ticks=not noticks)
zip_name = demo_path.stem + ".zip"

with (
tempfile.TemporaryDirectory() as tmpdirname,
zipfile.ZipFile(zip_name, "w", zipfile.ZIP_DEFLATED) as zipf,
):
for df_name, df in [
("kills", demo.kills),
("damages", demo.damages),
("bomb", demo.bomb),
("smokes", demo.smokes),
("infernos", demo.infernos),
("weapon_fires", demo.weapon_fires),
("rounds", demo.rounds),
("grenades", demo.grenades),
]:
df_filename = os.path.join(tmpdirname, f"{df_name}.data")
df.to_parquet(df_filename, index=False)
zipf.write(df_filename, f"{df_name}.data")

if not noticks:
ticks_filename = os.path.join(tmpdirname, "ticks.data")
demo.ticks.to_parquet(ticks_filename, index=False)
zipf.write(ticks_filename, "ticks.data")

header_filename = os.path.join(tmpdirname, "header.json")
with open(header_filename, "w") as f:
json.dump(demo.header, f)
zipf.write(header_filename, "header.json")

if verbose:
logger.success(f"Zipped dataframes for {zip_name}")
>>>>>>> 9afdcf2 (v2.0.0a2 (#331))
46 changes: 45 additions & 1 deletion awpy/demo.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@

import pandas as pd
from demoparser2 import DemoParser # pylint: disable=E0611
<<<<<<< HEAD
from pydantic import BaseModel, ConfigDict, Field, FilePath
=======
from loguru import logger
>>>>>>> 9afdcf2 (v2.0.0a2 (#331))

from awpy.parsers import (
parse_bomb,
Expand Down Expand Up @@ -40,6 +44,7 @@ class DemoHeader(BaseModel):
class Demo(BaseModel): # pylint: disable=too-many-instance-attributes
"""Class to store a demo's data. Called with `Demo(file="...")`."""
<<<<<<< HEAD
model_config = ConfigDict(arbitrary_types_allowed=True)
file: FilePath
Expand All @@ -48,6 +53,27 @@ class Demo(BaseModel): # pylint: disable=too-many-instance-attributes
parser: DemoParser = Field(default=None)
header: DemoHeader = Field(default=None)
events: dict[str, pd.DataFrame] = Field(default=dict)
=======
def __init__(
self, path: Path, *, verbose: bool = False, ticks: bool = True
) -> None:
"""Instantiate a Demo object.

Args:
path (Path): Path to demofile.
verbose (bool, optional): Whether to be log verbosely. Defaults to False.
ticks (bool, optional): Whether to parse ticks. Defaults to True.

Raises:
FileNotFoundError: If the specified `path` does not exist.
"""
# Pathify any input
self.path = Path(path)

# Save params
self.verbose = verbose
self.parse_ticks = ticks if ticks else False
>>>>>>> 9afdcf2 (v2.0.0a2 (#331))

# Data
kills: Optional[pd.DataFrame] = Field(default=None)
Expand All @@ -64,9 +90,25 @@ def __init__(self, **data: dict[str, Any]) -> None:
"""Initialize the Demo class. Performs any parsing."""
super().__init__(**data)
<<<<<<< HEAD
self.parser = DemoParser(str(self.file))
self._parse_demo()
self._parse_events()
=======
if self.path.exists():
self.parser = DemoParser(str(self.path))
if self.verbose:
logger.success(f"Created parser for {self.path}")
self._parse_demo()
if self.verbose:
logger.success(f"Parsed raw events for {self.path}")
self._parse_events()
if self.verbose:
logger.success(f"Processed events for {self.path}")
else:
demo_path_not_found_msg = f"{path} does not exist!"
raise FileNotFoundError(demo_path_not_found_msg)
>>>>>>> 9afdcf2 (v2.0.0a2 (#331))
def _parse_demo(self) -> None:
"""Parse the demo header and file."""
Expand All @@ -75,6 +117,7 @@ def _parse_demo(self) -> None:
raise ValueError(no_parser_error_msg)
self.header = parse_header(self.parser.parse_header())
self.events = dict(
self.parser.parse_events(
self.parser.list_game_events(),
Expand Down Expand Up @@ -138,7 +181,8 @@ def _parse_events(self) -> None:
self.rounds, parse_weapon_fires(self.events)
)
self.grenades = apply_round_num(self.rounds, parse_grenades(self.parser))
self.ticks = apply_round_num(self.rounds, parse_ticks(self.parser))
if self.parse_ticks is True:
self.ticks = apply_round_num(self.rounds, parse_ticks(self.parser))
@property
def is_parsed(self) -> bool:
Expand Down
5 changes: 5 additions & 0 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 14 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,13 @@ build-backend = "setuptools.build_meta"
[project]
name = "awpy"
<<<<<<< HEAD
<<<<<<< HEAD
version = "2.0.0rc1"
=======
version = "2.0.0b2"
=======
version = "2.0.0a2"
>>>>>>> 9afdcf2 (v2.0.0a2 (#331))
description = "Counter-Strike 2 demo parsing, analysis and visualization"
readme = "README.md"
>>>>>>> upstream/main
Expand Down Expand Up @@ -51,10 +55,17 @@ loguru = ">=0.7.2"
matplotlib = ">=3.9.0"
numpy = "^1.26.4"
pandas = ">=2.2.2"
<<<<<<< HEAD
setuptools = ">=70.1.0"
demoparser2 = ">=0.27.0"
tqdm = "^4.66.4"
usd-core = "^24.5"
=======
setuptools = ">=69.2.0"
sphinx-rtd-theme = "==2.0.0"
demoparser2 = ">=0.25.0"
tqdm = "^4.66.4"
>>>>>>> 9afdcf2 (v2.0.0a2 (#331))

[tool.poetry.dev-dependencies]
black = { version = "^24.4.2", extras = ["jupyter"] }
Expand All @@ -72,6 +83,9 @@ ipykernel = "^6.29.4"
[tool.poetry.scripts]
awpy = "awpy.cli:awpy"

[tool.poetry.scripts]
awpy = "awpy.cli:awpy"

# Test & Coverage config
[tool.coverage.run]
branch = true
Expand Down
4 changes: 4 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,11 @@ def teardown(): # noqa: PT004, ANN201
"""Cleans testing environment by deleting all .dem and .json files."""
yield
for file in os.listdir():
<<<<<<< HEAD
if file.endswith(".json"):
=======
if file.endswith((".json", ".dem", ".zip")):
>>>>>>> 9afdcf2 (v2.0.0a2 (#331))
os.remove(file)


Expand Down
Loading

0 comments on commit 495dfed

Please sign in to comment.