xcpcio 0.63.6__py3-none-any.whl → 0.64.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of xcpcio might be problematic. Click here for more details.

xcpcio/__version__.py CHANGED
@@ -1,4 +1,4 @@
1
1
  # This file is auto-generated by Hatchling. As such, do not:
2
2
  # - modify
3
3
  # - track in version control e.g. be sure to add to .gitignore
4
- __version__ = VERSION = '0.63.6'
4
+ __version__ = VERSION = '0.64.0'
@@ -5,13 +5,15 @@ Dependency injection system for Contest API Server.
5
5
  """
6
6
 
7
7
  from pathlib import Path
8
- from typing import Annotated
8
+ from typing import Annotated, Dict
9
9
 
10
10
  from fastapi import Depends
11
11
 
12
+ from xcpcio.ccs.reader.base_ccs_reader import BaseCCSReader
13
+ from xcpcio.ccs.reader.contest_package_reader import ContestPackageReader
14
+
12
15
  from .services.contest_service import ContestService
13
16
 
14
- # Global contest service instance cache
15
17
  _contest_service_instance = None
16
18
 
17
19
 
@@ -41,7 +43,10 @@ def configure_dependencies(contest_package_dir: Path) -> None:
41
43
  contest_package_dir: Path to contest package directory
42
44
  """
43
45
  global _contest_service_instance
44
- _contest_service_instance = ContestService(contest_package_dir)
46
+ reader_dict: Dict[str, BaseCCSReader] = {}
47
+ contest_package_reader = ContestPackageReader(contest_package_dir)
48
+ reader_dict[contest_package_reader.get_contest_id()] = contest_package_reader
49
+ _contest_service_instance = ContestService(reader_dict)
45
50
 
46
51
 
47
52
  # Type alias for dependency injection
@@ -8,6 +8,7 @@ from fastapi import APIRouter
8
8
 
9
9
  from . import (
10
10
  access,
11
+ accounts,
11
12
  awards,
12
13
  clarifications,
13
14
  contests,
@@ -30,6 +31,7 @@ def create_router() -> APIRouter:
30
31
 
31
32
  # Include all route modules
32
33
  router.include_router(access.router, tags=["Access"])
34
+ router.include_router(accounts.router, tags=["Accounts"])
33
35
  router.include_router(awards.router, tags=["Awards"])
34
36
  router.include_router(clarifications.router, tags=["Clarifications"])
35
37
  router.include_router(contests.router, tags=["Contests"])
@@ -11,10 +11,8 @@ logger = logging.getLogger(__name__)
11
11
 
12
12
  @router.get(
13
13
  "/contests/{contest_id}/access",
14
- summary="Get Access Information",
15
- description="Get access capabilities and visible endpoints for current client",
14
+ summary="Get access information",
16
15
  response_model=Dict[str, Any],
17
16
  )
18
17
  async def get_access(contest_id: str, service: ContestServiceDep) -> Dict[str, Any]:
19
- """Get access information for the current client"""
20
- return service.get_access_info(contest_id)
18
+ return service.get_access(contest_id)
@@ -0,0 +1,35 @@
1
+ import logging
2
+ from typing import Any, Dict, List
3
+
4
+ from fastapi import APIRouter
5
+ from fastapi import Path as FastAPIPath
6
+
7
+ from ..dependencies import ContestServiceDep
8
+
9
+ router = APIRouter()
10
+ logger = logging.getLogger(__name__)
11
+
12
+
13
+ @router.get(
14
+ "/contests/{contest_id}/accounts",
15
+ summary="Get all the accounts",
16
+ response_model=List[Dict[str, Any]],
17
+ )
18
+ async def get_accounts(
19
+ contest_id: str = FastAPIPath(..., description="Contest identifier"),
20
+ service: ContestServiceDep = None,
21
+ ) -> List[Dict[str, Any]]:
22
+ return service.get_accounts(contest_id)
23
+
24
+
25
+ @router.get(
26
+ "/contests/{contest_id}/accounts/{account_id}",
27
+ summary="Get the given account",
28
+ response_model=Dict[str, Any],
29
+ )
30
+ async def get_account(
31
+ contest_id: str = FastAPIPath(..., description="Contest identifier"),
32
+ account_id: str = FastAPIPath(..., description="Account identifier"),
33
+ service: ContestServiceDep = None,
34
+ ) -> Dict[str, Any]:
35
+ return service.get_account(contest_id, account_id)
@@ -3,7 +3,6 @@ from typing import Any, Dict, List
3
3
 
4
4
  from fastapi import APIRouter, Path
5
5
 
6
- from ...model import Award, Awards
7
6
  from ..dependencies import ContestServiceDep
8
7
 
9
8
  router = APIRouter()
@@ -12,27 +11,23 @@ logger = logging.getLogger(__name__)
12
11
 
13
12
  @router.get(
14
13
  "/contests/{contest_id}/awards",
15
- summary="Get Awards",
16
- description="Get all awards in the contest",
17
- response_model=Awards,
14
+ summary="Get all the awards standings for this contest",
15
+ response_model=List[Dict[str, Any]],
18
16
  )
19
17
  async def get_awards(
20
18
  contest_id: str = Path(..., description="Contest identifier"), service: ContestServiceDep = None
21
19
  ) -> List[Dict[str, Any]]:
22
- """Get all awards"""
23
20
  return service.get_awards(contest_id)
24
21
 
25
22
 
26
23
  @router.get(
27
24
  "/contests/{contest_id}/awards/{award_id}",
28
- summary="Get Award",
29
- description="Get specific award information",
30
- response_model=Award,
25
+ summary="Get the specific award for this contest",
26
+ response_model=Dict[str, Any],
31
27
  )
32
28
  async def get_award(
33
29
  contest_id: str = Path(..., description="Contest identifier"),
34
30
  award_id: str = Path(..., description="Award identifier"),
35
31
  service: ContestServiceDep = None,
36
32
  ) -> Dict[str, Any]:
37
- """Get specific award information"""
38
33
  return service.get_award(contest_id, award_id)
@@ -1,9 +1,8 @@
1
1
  import logging
2
- from typing import Any, Dict, List, Optional
2
+ from typing import Any, Dict, List
3
3
 
4
- from fastapi import APIRouter, Path, Query
4
+ from fastapi import APIRouter, Path
5
5
 
6
- from ...model import Clarification, Clarifications
7
6
  from ..dependencies import ContestServiceDep
8
7
 
9
8
  router = APIRouter()
@@ -12,31 +11,24 @@ logger = logging.getLogger(__name__)
12
11
 
13
12
  @router.get(
14
13
  "/contests/{contest_id}/clarifications",
15
- summary="Get Clarifications",
16
- description="Get all clarifications, optionally filtered",
17
- response_model=Clarifications,
14
+ summary="Get all the clarifications for this contest",
15
+ response_model=List[Dict[str, Any]],
18
16
  )
19
17
  async def get_clarifications(
20
18
  contest_id: str = Path(..., description="Contest identifier"),
21
- from_team_id: Optional[str] = Query(None, description="Filter by sender team ID (empty string for null)"),
22
- to_team_id: Optional[str] = Query(None, description="Filter by recipient team ID (empty string for null)"),
23
- problem_id: Optional[str] = Query(None, description="Filter by problem ID (empty string for null)"),
24
19
  service: ContestServiceDep = None,
25
20
  ) -> List[Dict[str, Any]]:
26
- """Get all clarifications, optionally filtered"""
27
- return service.get_clarifications(contest_id, from_team_id, to_team_id, problem_id)
21
+ return service.get_clarifications(contest_id)
28
22
 
29
23
 
30
24
  @router.get(
31
25
  "/contests/{contest_id}/clarifications/{clarification_id}",
32
- summary="Get Clarification",
33
- description="Get specific clarification information",
34
- response_model=Clarification,
26
+ summary="Get the given clarifications for this contest",
27
+ response_model=Dict[str, Any],
35
28
  )
36
29
  async def get_clarification(
37
30
  contest_id: str = Path(..., description="Contest identifier"),
38
31
  clarification_id: str = Path(..., description="Clarification identifier"),
39
32
  service: ContestServiceDep = None,
40
33
  ) -> Dict[str, Any]:
41
- """Get specific clarification information"""
42
34
  return service.get_clarification(contest_id, clarification_id)
@@ -1,74 +1,90 @@
1
+ import json
1
2
  import logging
2
- from pathlib import Path
3
- from typing import Any, Dict, List
3
+ from typing import Any, Dict, List, Optional
4
4
 
5
- from fastapi import APIRouter, HTTPException
5
+ from fastapi import APIRouter, Query
6
6
  from fastapi import Path as FastAPIPath
7
- from fastapi.responses import FileResponse
7
+ from fastapi.responses import FileResponse, StreamingResponse
8
8
 
9
- from ...model import Contest, State
10
9
  from ..dependencies import ContestServiceDep
11
10
 
12
11
  router = APIRouter()
13
12
  logger = logging.getLogger(__name__)
14
13
 
15
14
 
16
- @router.get("/contests", summary="Get Contests", description="Get list of all contests", response_model=List[Contest])
15
+ @router.get(
16
+ "/contests",
17
+ summary="Get all the contests",
18
+ response_model=List[Dict[str, Any]],
19
+ )
17
20
  async def get_contests(service: ContestServiceDep) -> List[Dict[str, Any]]:
18
- """Get all contests"""
19
21
  return service.get_contests()
20
22
 
21
23
 
22
24
  @router.get(
23
25
  "/contests/{contest_id}",
24
- summary="Get Contest",
25
- description="Get specific contest information",
26
- response_model=Contest,
26
+ summary="Get the given contest",
27
+ response_model=Dict[str, Any],
27
28
  )
28
29
  async def get_contest(
29
- contest_id: str = FastAPIPath(..., description="Contest identifier"), service: ContestServiceDep = None
30
+ contest_id: str = FastAPIPath(..., description="Contest identifier"),
31
+ service: ContestServiceDep = None,
30
32
  ) -> Dict[str, Any]:
31
- """Get specific contest information"""
32
33
  return service.get_contest(contest_id)
33
34
 
34
35
 
35
36
  @router.get(
36
37
  "/contests/{contest_id}/state",
37
- summary="Get Contest State",
38
- description="Get current contest state (started, ended, frozen, etc.)",
39
- response_model=State,
38
+ summary="Get the current contest state",
39
+ response_model=Dict[str, Any],
40
40
  )
41
41
  async def get_state(
42
- contest_id: str = FastAPIPath(..., description="Contest identifier"), service: ContestServiceDep = None
42
+ contest_id: str = FastAPIPath(..., description="Contest identifier"),
43
+ service: ContestServiceDep = None,
43
44
  ) -> Dict[str, Any]:
44
- """Get contest state"""
45
45
  return service.get_contest_state(contest_id)
46
46
 
47
47
 
48
48
  @router.get(
49
- "/contests/{contest_id}/contest/banner",
50
- summary="Get Contest Banner",
51
- description="Get banner image for the contest",
49
+ "/contests/{contest_id}/banner",
50
+ summary="Get the banner for the given contest",
52
51
  response_class=FileResponse,
53
52
  )
54
53
  async def get_contest_banner(
55
- contest_id: str = FastAPIPath(..., description="Contest identifier"), service: ContestServiceDep = None
54
+ contest_id: str = FastAPIPath(..., description="Contest identifier"),
55
+ service: ContestServiceDep = None,
56
56
  ) -> FileResponse:
57
- """Get contest banner file"""
58
- service.validate_contest_id(contest_id)
59
-
60
- # Expected href pattern for this endpoint
61
- expected_href = f"contests/{contest_id}/contest/banner"
62
-
63
- try:
64
- banners = service.contest_data.get("banner", [])
65
- for banner in banners:
66
- href = banner["href"]
67
- if href == expected_href:
68
- filename = banner["filename"]
69
- banner_file: Path = service.contest_package_dir / "contest" / filename
70
- if banner_file.exists():
71
- mime_type = banner["mime"]
72
- return FileResponse(path=banner_file, media_type=mime_type, filename=filename)
73
- except Exception as e:
74
- raise HTTPException(status_code=404, detail=f"Banner not found. [contest_id={contest_id}] [err={e}]")
57
+ file_attr = service.get_contest_banner(contest_id)
58
+ return FileResponse(path=file_attr.path, media_type=file_attr.media_type, filename=file_attr.name)
59
+
60
+
61
+ @router.get(
62
+ "/contests/{contest_id}/problemset",
63
+ summary="Get the problemset document for the given contest",
64
+ response_class=FileResponse,
65
+ )
66
+ async def get_contest_problem_set(
67
+ contest_id: str = FastAPIPath(..., description="Contest identifier"),
68
+ service: ContestServiceDep = None,
69
+ ) -> FileResponse:
70
+ file_attr = service.get_contest_problemset(contest_id)
71
+ return FileResponse(path=file_attr.path, media_type=file_attr.media_type, filename=file_attr.name)
72
+
73
+
74
+ @router.get(
75
+ "/contests/{contest_id}/event-feed",
76
+ summary="Get event feed for contest",
77
+ description="Get events for the contest in NDJSON format. Each line contains a single event object.",
78
+ )
79
+ async def get_event_feed(
80
+ contest_id: str = FastAPIPath(..., description="Contest identifier"),
81
+ stream: bool = Query(False, description="Whether to stream the output or stop immediately"),
82
+ since_token: Optional[str] = Query(None, description="Return events after this token"),
83
+ service: ContestServiceDep = None,
84
+ ) -> StreamingResponse:
85
+ async def generate():
86
+ events = service.get_event_feed(contest_id, since_token)
87
+ for event in events:
88
+ yield json.dumps(event, ensure_ascii=False, separators=(",", ":")) + "\n"
89
+
90
+ return StreamingResponse(generate(), media_type="application/x-ndjson")
@@ -13,17 +13,18 @@ logger = logging.getLogger(__name__)
13
13
  @router.get(
14
14
  "/",
15
15
  summary="API Information",
16
- description="Get API version and provider information",
17
16
  response_model=Dict[str, Any],
18
17
  )
19
18
  async def get_api_info(service: ContestServiceDep) -> Dict[str, Any]:
20
- """Get API information and provider details"""
21
19
  return service.get_api_info()
22
20
 
23
21
 
24
- @router.get("/health", summary="Health Check", description="Check server health status", response_model=Dict[str, Any])
22
+ @router.get(
23
+ "/health",
24
+ summary="Health Check",
25
+ response_model=Dict[str, Any],
26
+ )
25
27
  async def health_check(service: ContestServiceDep) -> Dict[str, Any]:
26
- """Health check endpoint"""
27
28
  return {
28
29
  "status": "healthy",
29
30
  "timestamp": datetime.now().isoformat(),
@@ -3,10 +3,6 @@ from typing import Any, Dict, List
3
3
 
4
4
  from fastapi import APIRouter, Path
5
5
 
6
- from ...model import (
7
- Group,
8
- Groups,
9
- )
10
6
  from ..dependencies import ContestServiceDep
11
7
 
12
8
  router = APIRouter()
@@ -15,27 +11,24 @@ logger = logging.getLogger(__name__)
15
11
 
16
12
  @router.get(
17
13
  "/contests/{contest_id}/groups",
18
- summary="Get Groups",
19
- description="Get all team groups in the contest",
20
- response_model=Groups,
14
+ summary="Get all the groups for this contest",
15
+ response_model=List[Dict[str, Any]],
21
16
  )
22
17
  async def get_groups(
23
- contest_id: str = Path(..., description="Contest identifier"), service: ContestServiceDep = None
18
+ contest_id: str = Path(..., description="Contest identifier"),
19
+ service: ContestServiceDep = None,
24
20
  ) -> List[Dict[str, Any]]:
25
- """Get all groups"""
26
21
  return service.get_groups(contest_id)
27
22
 
28
23
 
29
24
  @router.get(
30
25
  "/contests/{contest_id}/groups/{group_id}",
31
- summary="Get Group",
32
- description="Get specific group information",
33
- response_model=Group,
26
+ summary="Get the given group for this contest",
27
+ response_model=Dict[str, Any],
34
28
  )
35
29
  async def get_group(
36
30
  contest_id: str = Path(..., description="Contest identifier"),
37
31
  group_id: str = Path(..., description="Group identifier"),
38
32
  service: ContestServiceDep = None,
39
33
  ) -> Dict[str, Any]:
40
- """Get specific group information"""
41
34
  return service.get_group(contest_id, group_id)
@@ -3,10 +3,6 @@ from typing import Any, Dict, List
3
3
 
4
4
  from fastapi import APIRouter, Path
5
5
 
6
- from ...model import (
7
- JudgementType,
8
- JudgementTypes,
9
- )
10
6
  from ..dependencies import ContestServiceDep
11
7
 
12
8
  router = APIRouter()
@@ -15,27 +11,24 @@ logger = logging.getLogger(__name__)
15
11
 
16
12
  @router.get(
17
13
  "/contests/{contest_id}/judgement-types",
18
- summary="Get Judgement Types",
19
- description="Get all judgement types available in the contest",
20
- response_model=JudgementTypes,
14
+ summary="Get all the judgement types for this contest",
15
+ response_model=List[Dict[str, Any]],
21
16
  )
22
17
  async def get_judgement_types(
23
- contest_id: str = Path(..., description="Contest identifier"), service: ContestServiceDep = None
18
+ contest_id: str = Path(..., description="Contest identifier"),
19
+ service: ContestServiceDep = None,
24
20
  ) -> List[Dict[str, Any]]:
25
- """Get all judgement types"""
26
21
  return service.get_judgement_types(contest_id)
27
22
 
28
23
 
29
24
  @router.get(
30
25
  "/contests/{contest_id}/judgement-types/{judgement_type_id}",
31
- summary="Get Judgement Type",
32
- description="Get specific judgement type information",
33
- response_model=JudgementType,
26
+ summary="Get the given judgement type for this contest",
27
+ response_model=Dict[str, Any],
34
28
  )
35
29
  async def get_judgement_type(
36
30
  contest_id: str = Path(..., description="Contest identifier"),
37
31
  judgement_type_id: str = Path(..., description="Judgement type identifier"),
38
32
  service: ContestServiceDep = None,
39
33
  ) -> Dict[str, Any]:
40
- """Get specific judgement type information"""
41
34
  return service.get_judgement_type(contest_id, judgement_type_id)
@@ -3,7 +3,6 @@ from typing import Any, Dict, List, Optional
3
3
 
4
4
  from fastapi import APIRouter, Path, Query
5
5
 
6
- from ...model import Judgement, Judgements
7
6
  from ..dependencies import ContestServiceDep
8
7
 
9
8
  router = APIRouter()
@@ -12,29 +11,25 @@ logger = logging.getLogger(__name__)
12
11
 
13
12
  @router.get(
14
13
  "/contests/{contest_id}/judgements",
15
- summary="Get Judgements",
16
- description="Get all judgements, optionally filtered by submission",
17
- response_model=Judgements,
14
+ summary="Get all the judgements for this contest",
15
+ response_model=List[Dict[str, Any]],
18
16
  )
19
17
  async def get_judgements(
20
18
  contest_id: str = Path(..., description="Contest identifier"),
21
19
  submission_id: Optional[str] = Query(None, description="Filter judgements by submission ID"),
22
20
  service: ContestServiceDep = None,
23
21
  ) -> List[Dict[str, Any]]:
24
- """Get all judgements, optionally filtered by submission"""
25
22
  return service.get_judgements(contest_id, submission_id)
26
23
 
27
24
 
28
25
  @router.get(
29
26
  "/contests/{contest_id}/judgements/{judgement_id}",
30
- summary="Get Judgement",
31
- description="Get specific judgement information",
32
- response_model=Judgement,
27
+ summary="Get the given judgement for this contest",
28
+ response_model=Dict[str, Any],
33
29
  )
34
30
  async def get_judgement(
35
31
  contest_id: str = Path(..., description="Contest identifier"),
36
32
  judgement_id: str = Path(..., description="Judgement identifier"),
37
33
  service: ContestServiceDep = None,
38
34
  ) -> Dict[str, Any]:
39
- """Get specific judgement information"""
40
35
  return service.get_judgement(contest_id, judgement_id)
@@ -3,10 +3,6 @@ from typing import Any, Dict, List
3
3
 
4
4
  from fastapi import APIRouter, Path
5
5
 
6
- from ...model import (
7
- Language,
8
- Languages,
9
- )
10
6
  from ..dependencies import ContestServiceDep
11
7
 
12
8
  router = APIRouter()
@@ -15,27 +11,24 @@ logger = logging.getLogger(__name__)
15
11
 
16
12
  @router.get(
17
13
  "/contests/{contest_id}/languages",
18
- summary="Get Languages",
19
- description="Get all programming languages available for submission",
20
- response_model=Languages,
14
+ summary="Get all the languages for this contest",
15
+ response_model=List[Dict[str, Any]],
21
16
  )
22
17
  async def get_languages(
23
- contest_id: str = Path(..., description="Contest identifier"), service: ContestServiceDep = None
18
+ contest_id: str = Path(..., description="Contest identifier"),
19
+ service: ContestServiceDep = None,
24
20
  ) -> List[Dict[str, Any]]:
25
- """Get all programming languages"""
26
21
  return service.get_languages(contest_id)
27
22
 
28
23
 
29
24
  @router.get(
30
25
  "/contests/{contest_id}/languages/{language_id}",
31
- summary="Get Language",
32
- description="Get specific language information",
33
- response_model=Language,
26
+ summary="Get the given language for this contest",
27
+ response_model=Dict[str, Any],
34
28
  )
35
29
  async def get_language(
36
30
  contest_id: str = Path(..., description="Contest identifier"),
37
31
  language_id: str = Path(..., description="Language identifier"),
38
32
  service: ContestServiceDep = None,
39
33
  ) -> Dict[str, Any]:
40
- """Get specific language information"""
41
34
  return service.get_language(contest_id, language_id)
@@ -1,15 +1,10 @@
1
1
  import logging
2
- from pathlib import Path
3
2
  from typing import Any, Dict, List
4
3
 
5
- from fastapi import APIRouter, HTTPException
4
+ from fastapi import APIRouter
6
5
  from fastapi import Path as FastAPIPath
7
6
  from fastapi.responses import FileResponse
8
7
 
9
- from ...model import (
10
- Organization,
11
- Organizations,
12
- )
13
8
  from ..dependencies import ContestServiceDep
14
9
 
15
10
  router = APIRouter()
@@ -18,36 +13,32 @@ logger = logging.getLogger(__name__)
18
13
 
19
14
  @router.get(
20
15
  "/contests/{contest_id}/organizations",
21
- summary="Get Organizations",
22
- description="Get all organizations in the contest",
23
- response_model=Organizations,
16
+ summary="Get all the organizations for this contest",
17
+ response_model=List[Dict[str, Any]],
24
18
  )
25
19
  async def get_organizations(
26
- contest_id: str = FastAPIPath(..., description="Contest identifier"), service: ContestServiceDep = None
20
+ contest_id: str = FastAPIPath(..., description="Contest identifier"),
21
+ service: ContestServiceDep = None,
27
22
  ) -> List[Dict[str, Any]]:
28
- """Get all organizations"""
29
23
  return service.get_organizations(contest_id)
30
24
 
31
25
 
32
26
  @router.get(
33
27
  "/contests/{contest_id}/organizations/{organization_id}",
34
- summary="Get Organization",
35
- description="Get specific organization information",
36
- response_model=Organization,
28
+ summary="Get the given organization for this contest",
29
+ response_model=Dict[str, Any],
37
30
  )
38
31
  async def get_organization(
39
32
  contest_id: str = FastAPIPath(..., description="Contest identifier"),
40
33
  organization_id: str = FastAPIPath(..., description="Organization identifier"),
41
34
  service: ContestServiceDep = None,
42
35
  ) -> Dict[str, Any]:
43
- """Get specific organization information"""
44
36
  return service.get_organization(contest_id, organization_id)
45
37
 
46
38
 
47
39
  @router.get(
48
40
  "/contests/{contest_id}/organizations/{organization_id}/logo",
49
41
  summary="Get Organization Logo",
50
- description="Get logo file for a specific organization",
51
42
  response_class=FileResponse,
52
43
  )
53
44
  async def get_organization_logo(
@@ -55,28 +46,5 @@ async def get_organization_logo(
55
46
  organization_id: str = FastAPIPath(..., description="Organization identifier"),
56
47
  service: ContestServiceDep = None,
57
48
  ) -> FileResponse:
58
- """Get organization logo file"""
59
- service.validate_contest_id(contest_id)
60
-
61
- # Get organization from indexed data
62
- org = service.organizations_by_id.get(organization_id)
63
- if not org:
64
- raise HTTPException(status_code=404, detail=f"Organization {organization_id} not found")
65
-
66
- # Expected href pattern for this endpoint
67
- expected_href = f"contests/{contest_id}/organizations/{organization_id}/logo"
68
-
69
- try:
70
- logos = org["logo"]
71
- for logo in logos:
72
- href = logo["href"]
73
- if href == expected_href:
74
- filename = logo["filename"]
75
- logo_file: Path = service.contest_package_dir / "organizations" / organization_id / filename
76
- if logo_file.exists():
77
- mime_type = logo["mime"]
78
- return FileResponse(path=logo_file, media_type=mime_type, filename=filename)
79
- except Exception as e:
80
- raise HTTPException(
81
- status_code=404, detail=f"Logo file not found. [organization_id={organization_id}] [err={e}]"
82
- )
49
+ file_attr = service.get_organization_logo(contest_id, organization_id)
50
+ return FileResponse(path=file_attr.path, media_type=file_attr.media_type, filename=file_attr.name)