Skip to content

Commit 22451b6

Browse files
committed
Improve task push
1 parent c300416 commit 22451b6

1 file changed

Lines changed: 123 additions & 31 deletions

File tree

push_tasks.py

Lines changed: 123 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,12 @@
44
"""
55

66
import argparse
7+
import base64
8+
import gzip
79
import json
810
import os
911
import sys
12+
import time
1013
import urllib.error
1114
import urllib.request
1215
from pathlib import Path
@@ -15,7 +18,7 @@
1518

1619
def read_json_files(directory: str) -> List[tuple]:
1720
"""
18-
Read all JSON files from the specified directory.
21+
Read all JSON files from the specified directory recursively.
1922
2023
Returns:
2124
List of tuples (filename, json_data)
@@ -27,7 +30,7 @@ def read_json_files(directory: str) -> List[tuple]:
2730
print(f"Error: Directory '{directory}' does not exist", file=sys.stderr)
2831
sys.exit(1)
2932

30-
for json_file in release_path.glob("*.json"):
33+
for json_file in release_path.rglob("*.json"):
3134
try:
3235
with open(json_file, "r", encoding="utf-8") as f:
3336
data = json.load(f)
@@ -41,61 +44,120 @@ def read_json_files(directory: str) -> List[tuple]:
4144
return json_files
4245

4346

44-
def publish_task(
45-
url: str, task_data: dict, visibility: str, origin: str, auth_token: str
46-
) -> bool:
47+
def publish_task_batch(
48+
url: str, tasks_batch: List[tuple], visibility: str, origin: str, auth_token: str
49+
) -> dict:
4750
"""
48-
Publish a single task to the remote URL.
51+
Publish a batch of tasks to the remote URL with gzip compression and base64 encoding.
4952
5053
Args:
5154
url: The target URL
52-
task_data: The JSON data to publish
55+
tasks_batch: List of tuples (filename, task_data)
5356
visibility: Visibility setting ('public' or 'hidden')
5457
origin: Origin of the tasks
5558
auth_token: Authorization token from environment
5659
5760
Returns:
58-
True if successful, False otherwise
61+
Dictionary with 'success' count and 'failed' list of filenames
5962
"""
60-
# Prepare the payload
61-
payload = {"task": task_data, "visibility": visibility, "origin": origin}
63+
result = {"success": 0, "failed": []}
64+
65+
# Prepare the tasks list (just the task data, no wrapping)
66+
tasks_list = [task_data for _, task_data in tasks_batch]
67+
68+
# Convert to JSON, compress with gzip, and encode with base64
69+
json_data = json.dumps(tasks_list).encode("utf-8")
70+
compressed_data = gzip.compress(json_data)
71+
base64_payload = base64.b64encode(compressed_data).decode("ascii")
72+
73+
# Prepare the final payload structure matching the API
74+
payload = {
75+
"payload": base64_payload,
76+
"visibility": visibility,
77+
"origin": origin,
78+
}
6279

6380
# Prepare headers
6481
headers = {
6582
"Content-Type": "application/json",
83+
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
6684
}
6785

6886
if auth_token:
6987
headers["X-AUTH-KEY"] = auth_token
7088

7189
# Convert payload to JSON bytes
72-
data = json.dumps(payload).encode("utf-8")
90+
request_data = json.dumps(payload).encode("utf-8")
7391

7492
# Create request
75-
req = urllib.request.Request(url, data=data, headers=headers, method="POST")
93+
req = urllib.request.Request(url, data=request_data, headers=headers, method="POST")
7694

7795
try:
78-
with urllib.request.urlopen(req) as response:
96+
with urllib.request.urlopen(req, timeout=30) as response:
7997
status = response.status
8098
response_body = response.read().decode("utf-8")
8199
if status in (200, 201):
82-
return True
100+
result["success"] = len(tasks_batch)
101+
return result
83102
else:
84103
print(f" Warning: Received status {status}", file=sys.stderr)
85104
print(f" Response: {response_body}", file=sys.stderr)
86-
return False
105+
result["failed"] = [filename for filename, _ in tasks_batch]
106+
return result
87107
except urllib.error.HTTPError as e:
88108
error_body = e.read().decode("utf-8") if e.fp else ""
89109
print(f" HTTP Error {e.code}: {e.reason}", file=sys.stderr)
90110
if error_body:
91-
print(f" Response: {error_body}", file=sys.stderr)
92-
return False
111+
# Truncate very long error messages
112+
if len(error_body) > 500:
113+
print(f" Response: {error_body[:500]}...", file=sys.stderr)
114+
else:
115+
print(f" Response: {error_body}", file=sys.stderr)
116+
result["failed"] = [filename for filename, _ in tasks_batch]
117+
return result
93118
except urllib.error.URLError as e:
94119
print(f" URL Error: {e.reason}", file=sys.stderr)
95-
return False
120+
result["failed"] = [filename for filename, _ in tasks_batch]
121+
return result
96122
except Exception as e:
97123
print(f" Error: {e}", file=sys.stderr)
98-
return False
124+
result["failed"] = [filename for filename, _ in tasks_batch]
125+
return result
126+
127+
128+
def load_env_file(env_path: str = ".env") -> dict:
129+
"""
130+
Load environment variables from a .env file.
131+
132+
Args:
133+
env_path: Path to the .env file
134+
135+
Returns:
136+
Dictionary of environment variables
137+
"""
138+
env_vars = {}
139+
env_file = Path(env_path)
140+
141+
if not env_file.exists():
142+
return env_vars
143+
144+
try:
145+
with open(env_file, "r", encoding="utf-8") as f:
146+
for line in f:
147+
line = line.strip()
148+
# Skip empty lines and comments
149+
if not line or line.startswith("#"):
150+
continue
151+
# Parse KEY=VALUE format
152+
if "=" in line:
153+
key, value = line.split("=", 1)
154+
# Remove quotes if present
155+
value = value.strip().strip("\"'")
156+
env_vars[key.strip()] = value
157+
except Exception as e:
158+
print(f"Warning: Error reading .env file: {e}", file=sys.stderr)
159+
160+
return env_vars
99161

100162

101163
def main():
@@ -121,6 +183,12 @@ def main():
121183
default="release",
122184
help="Directory containing JSON files (default: release)",
123185
)
186+
parser.add_argument(
187+
"--batch-size",
188+
type=int,
189+
default=20,
190+
help="Number of tasks to send in each batch (default: 20)",
191+
)
124192

125193
args = parser.parse_args()
126194

@@ -131,11 +199,16 @@ def main():
131199

132200
visibility = "public" if args.public else "hidden"
133201

134-
# Get auth token from environment
135-
auth_token = os.environ.get("CODEBATTLE_AUTH_TOKEN", "")
202+
# Load .env file
203+
env_vars = load_env_file()
204+
205+
# Get auth token from .env file or environment variable
206+
auth_token = env_vars.get("CODEBATTLE_AUTH_TOKEN") or os.environ.get(
207+
"CODEBATTLE_AUTH_TOKEN", ""
208+
)
136209
if not auth_token:
137210
print(
138-
"Warning: CODEBATTLE_AUTH_TOKEN environment variable not set",
211+
"Warning: CODEBATTLE_AUTH_TOKEN not found in .env file or environment",
139212
file=sys.stderr,
140213
)
141214

@@ -150,20 +223,39 @@ def main():
150223
print(f"\nPublishing {len(json_files)} tasks to {args.url}...")
151224
print(f"Visibility: {visibility}")
152225
print(f"Origin: {args.origin}")
226+
print(f"Batch size: {args.batch_size}")
153227
print()
154228

155-
# Publish each task
229+
# Publish tasks in batches
156230
success_count = 0
157231
fail_count = 0
232+
total_batches = (len(json_files) + args.batch_size - 1) // args.batch_size
233+
234+
for batch_num in range(0, len(json_files), args.batch_size):
235+
batch = json_files[batch_num : batch_num + args.batch_size]
236+
batch_index = batch_num // args.batch_size + 1
237+
238+
print(
239+
f"Batch {batch_index}/{total_batches} ({len(batch)} tasks)... ",
240+
end="",
241+
flush=True,
242+
)
243+
244+
result = publish_task_batch(
245+
args.url, batch, visibility, args.origin, auth_token
246+
)
247+
248+
if result["success"] > 0:
249+
print(f"✓ ({result['success']} tasks)")
250+
success_count += result["success"]
251+
252+
if result["failed"]:
253+
print(f"✗ Failed tasks: {', '.join(result['failed'])}")
254+
fail_count += len(result["failed"])
158255

159-
for filename, task_data in json_files:
160-
print(f"Publishing: {filename}... ", end="", flush=True)
161-
if publish_task(args.url, task_data, visibility, args.origin, auth_token):
162-
print("✓")
163-
success_count += 1
164-
else:
165-
print("✗")
166-
fail_count += 1
256+
# Add a small delay between batches to avoid rate limiting
257+
if batch_index < total_batches:
258+
time.sleep(1)
167259

168260
# Summary
169261
print()

0 commit comments

Comments
 (0)