Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 33 additions & 30 deletions xeppelin/xeppelin.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from datetime import datetime
import xeppelin.xeppelin_logging as xeppelin_logging
import matplotlib.pyplot as plt
import pkg_resources
import importlib.resources

# put to the parent directory to avoid infinite loops
LOG_DIR = ".."
Expand All @@ -16,10 +16,10 @@ def start(contest_name):
log_file = os.path.join(LOG_DIR, f"{contest_name}.log")
if not os.path.exists(LOG_DIR):
os.makedirs(LOG_DIR)

# Use pkg_resources to find the script path
script_path = pkg_resources.resource_filename('xeppelin', 'xeppelin.sh')
script_path = importlib.resources.files('xeppelin').joinpath('xeppelin.sh')

# Start the xeppelin.sh script in the background
subprocess.Popen([script_path, log_file], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
print(f"Started watching for contest '{contest_name}'. Log file: {log_file}")
Expand All @@ -29,21 +29,21 @@ def stop(contest_name):
subprocess.run(["pkill", "xeppelin.sh"])
print(f"Stopped watching for contest '{contest_name}'.")

def show(contest_name, duration=300, freeze_time=None, title=None):
def show(contest_name, duration=300, freeze_time=None, title=None, template_name: str = 'template'):
log_file = os.path.join(LOG_DIR, f"{contest_name}.log")
if not os.path.exists(log_file):
print(f"No log file found for contest '{contest_name}'.")
return

with open(log_file, 'r') as f:
log_lines = f.readlines()

solved_times = xeppelin_logging.parse_solved_info(log_lines)
contest_start = xeppelin_logging.find_contest_start(log_lines)
contest_start = xeppelin_logging.find_contest_start(log_lines, template_name)
if not contest_start:
print("Could not find contest start!")
return

activities = xeppelin_logging.group_activities(log_lines, contest_start)
fig = xeppelin_logging.plot_activities(contest_name if title is None else title, activities, solved_times, duration, freeze_time)
fig.savefig(os.path.join(LOG_DIR, f"{contest_name}.png"))
Expand All @@ -62,76 +62,79 @@ def main():
epilog='For more information, visit: https://github.com/KiK0s/xeppelin'
)
subparsers = parser.add_subparsers(dest='command', help='Available commands')

# Create parser for "start" command
start_parser = subparsers.add_parser(
'start',
'start',
help='Start watching the current directory for a contest',
description='Start monitoring the current directory for contest activities. This command will track file modifications and log them for later visualization.',
epilog='Example: xeppelin start icpc-wf'
)
start_parser.add_argument('contest_name',
start_parser.add_argument('contest_name',
help='Name of the contest to start watching. This will be used for the log file name.')

# Create parser for "stop" command
stop_parser = subparsers.add_parser(
'stop',
'stop',
help='Stop watching for a contest',
description='Stop monitoring the specified contest and terminate the file watching process.',
epilog='Example: xeppelin stop icpc-wf'
)
stop_parser.add_argument('contest_name',
stop_parser.add_argument('contest_name',
help='Name of the contest to stop watching. Should match the name used with the start command.')

# Create parser for "show" command
show_parser = subparsers.add_parser(
'show',
'show',
help='Display visualization of contest activities',
description='Generate and display a visualization of contest activities from the log file. This shows your coding activity timeline and problem submissions.',
epilog='''Examples:
xeppelin show icpc-wf
xeppelin show icpc-wf --duration 240
xeppelin show icpc-wf --freeze 4:00
xeppelin show icpc-wf --duration 300 --freeze 240 --title "ICPC World Finals"'''
xeppelin show icpc-wf --template main
xeppelin show icpc-wf --duration 300 --freeze 240 --template main --title "ICPC World Finals"'''
)
show_parser.add_argument('contest_name',
show_parser.add_argument('contest_name',
help='Name of the contest to visualize. Should match the name used with the start command.')
show_parser.add_argument('--duration', type=int, default=300,
show_parser.add_argument('--duration', type=int, default=300,
help='Maximum time (in minutes) to show on the visualization axis (default: 300)')
show_parser.add_argument('--freeze', type=str, default=240,
show_parser.add_argument('--freeze', type=str, default=240,
help='Add a freeze period indicator starting at specified time (format: HH:MM or minutes as integer)')
show_parser.add_argument('--template', type=str, default='template',
help='Name of the template file (default: template)')
# show_parser.add_argument('--problemset', type=str, default='abcdefghijklmno')
show_parser.add_argument('--title', type=str, default=None,
help='Custom title for the visualization (default: contest name)')

# Create parser for "log" command
log_parser = subparsers.add_parser(
'log',
'log',
help='Log submission information for a contest',
description='Manually add submission information to the contest log file. Use this to record when problems were solved.',
epilog='Example: xeppelin log icpc-wf "A solved 1:30"'
)
log_parser.add_argument('contest_name',
log_parser.add_argument('contest_name',
help='Name of the contest to log submissions for')
log_parser.add_argument('submission_info',
log_parser.add_argument('submission_info',
help='Submission information to log (e.g., "A solved 1:30" means problem A was solved at 1 hour and 30 minutes)')

# Parse arguments
args = parser.parse_args()

if args.command is None:
parser.print_help()
return

# Execute the appropriate command
if args.command == 'start':
start(args.contest_name)
elif args.command == 'stop':
stop(args.contest_name)
elif args.command == 'show':
show(args.contest_name, args.duration, args.freeze, args.title)
show(args.contest_name, args.duration, args.freeze, args.title, args.template)
elif args.command == 'log':
log_submissions(args.contest_name, args.submission_info)

if __name__ == "__main__":
main()
main()
Loading