Finish Schoolhouse programming submission flow

This commit is contained in:
Fizzlepoof
2026-05-16 19:13:10 +00:00
parent d31de19227
commit e5a100d1a0
15 changed files with 753 additions and 74 deletions

View File

@@ -1,4 +1,7 @@
import json
import mimetypes
import time
from pathlib import Path
from urllib import request
from urllib.parse import urljoin
@@ -27,5 +30,96 @@ class PaperlessClient:
def get_document(self, document_id: int | str) -> dict:
return self._req('GET', f'/api/documents/{document_id}/')
def update_document(self, document_id: int | str, payload: dict) -> dict:
return self._req('PATCH', f'/api/documents/{document_id}/', payload)
def add_note(self, document_id: int | str, note: str) -> dict:
return self._req('POST', f'/api/documents/{document_id}/notes/', {'note': note})
def _list_endpoint(self, path: str) -> list[dict]:
data = self._req('GET', path)
if isinstance(data, list):
return data
if isinstance(data, dict):
results = data.get('results')
if isinstance(results, list):
return results
return []
def list_tags(self) -> list[dict]:
return self._list_endpoint('/api/tags/?page_size=200')
def create_tag(self, name: str) -> dict:
return self._req('POST', '/api/tags/', {'name': name})
def get_or_create_tag(self, name: str) -> dict:
needle = name.strip().casefold()
for tag in self.list_tags():
if str(tag.get('name', '')).strip().casefold() == needle:
return tag
return self.create_tag(name)
def list_document_types(self) -> list[dict]:
return self._list_endpoint('/api/document_types/?page_size=200')
def list_correspondents(self) -> list[dict]:
return self._list_endpoint('/api/correspondents/?page_size=200')
def list_tasks(self, task_id: str | None = None) -> list[dict]:
path = '/api/tasks/?page_size=100'
if task_id:
path += f'&task_id={task_id}'
return self._list_endpoint(path)
def wait_for_task(self, task_id: str, timeout_seconds: int = 90, poll_seconds: float = 1.0) -> dict:
deadline = time.time() + timeout_seconds
last = {}
while time.time() < deadline:
tasks = self.list_tasks(task_id=task_id)
if tasks:
last = tasks[0]
status = str(last.get('status') or '').upper()
if status in {'SUCCESS', 'FAILURE', 'CANCELLED'}:
return last
time.sleep(poll_seconds)
return last
def upload_document(self, file_path: str, title: str) -> dict:
path = Path(file_path)
boundary = "----DorisSchoolhousePaperlessUpload"
mime = mimetypes.guess_type(path.name)[0] or "application/octet-stream"
parts: list[bytes] = []
for key, value in {"title": title}.items():
parts.extend([
f"--{boundary}\r\n".encode(),
f'Content-Disposition: form-data; name="{key}"\r\n\r\n'.encode(),
str(value).encode(),
b"\r\n",
])
parts.extend([
f"--{boundary}\r\n".encode(),
f'Content-Disposition: form-data; name="document"; filename="{path.name}"\r\n'.encode(),
f"Content-Type: {mime}\r\n\r\n".encode(),
path.read_bytes(),
b"\r\n",
f"--{boundary}--\r\n".encode(),
])
body = b"".join(parts)
req = request.Request(
urljoin(self.base_url, "/api/documents/post_document/"),
data=body,
headers={
"Authorization": f"Token {self.token}",
"Accept": "application/json",
"Content-Type": f"multipart/form-data; boundary={boundary}",
},
method="POST",
)
with request.urlopen(req, timeout=120) as resp:
raw = resp.read().decode("utf-8", errors="replace")
if not raw:
return {}
parsed = json.loads(raw)
if isinstance(parsed, str):
return {"task_id": parsed}
return parsed