Skip to content

Commit

Permalink
Wip iteration 2
Browse files Browse the repository at this point in the history
  • Loading branch information
lebaudantoine committed Dec 15, 2023
1 parent e360f59 commit 5304d59
Show file tree
Hide file tree
Showing 12 changed files with 542 additions and 382 deletions.
54 changes: 54 additions & 0 deletions bin/runner.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
"""Wip."""

import logging

import argparse

import asyncio

from httpx import HTTPError
from pydantic import ValidationError


from warren.xi.client import ExperienceIndex

from warren.xi.indexers.moodle.client import (
Moodle
)

from warren.xi.indexers.factory import IndexerFactory


logger = logging.getLogger(__name__)


async def index_moodle(args):
"""Wip."""

xi = ExperienceIndex()
moodle = Moodle()

indexer = await IndexerFactory.create_indexer(args.indexer, lms=moodle, xi=xi, ignore_errors=args.ignore_errors, course_iri=args.course_iri)

try:
logger.debug("Running indexer: %s", indexer)
await indexer.execute()

except (HTTPError, ValidationError):
logger.exception("Runner has stopped")

finally:
logger.debug("Closing clients session")
await moodle.close()
await xi.close()

if __name__ == "__main__":
"""Wip."""

parser = argparse.ArgumentParser(description="Wip.")
parser.add_argument('indexer', type=str, default='courses', help='Wip.', nargs='?')
parser.add_argument('-ie', '--ignore_errors', action='store_true', default=False)
parser.add_argument('course_iri', type=str, nargs='?')

args = parser.parse_args()
asyncio.run(index_moodle(args))
61 changes: 0 additions & 61 deletions bin/wip.py

This file was deleted.

205 changes: 205 additions & 0 deletions src/api/core/warren/xi/client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
"""Wip."""

from abc import ABC, abstractmethod
from http import HTTPStatus
from typing import List, Optional, Protocol, Tuple, Union
from urllib.parse import quote
from uuid import UUID

from httpx import AsyncClient
from pydantic import parse_obj_as

from warren.fields import IRI

from .models import (
Experience,
ExperienceCreate,
ExperienceRead,
ExperienceReadSnapshot,
ExperienceUpdate,
Relation,
RelationCreate,
RelationRead,
RelationType,
RelationUpdate,
)


class Client(Protocol):
"""Protocol representing an asynchronous client."""

_client: AsyncClient

async def close(self):
"""Close client pool of connections asynchronously."""
...


class BaseCRUD(ABC):
"""Wip."""

_client: AsyncClient

def __init__(self, client):
"""Wip."""
self._client = client

def _construct_url(self, path: Union[str, UUID, IRI]) -> str:
"""Wip."""
path = quote(str(path))
return f"{self._base_url.rstrip('/')}/{path.lstrip('/')}"

@property
@abstractmethod
def _base_url(self):
"""Abstract method to get the URL for specific entity."""

@abstractmethod
async def create(self, data):
"""Abstract method for creating an entity."""

@abstractmethod
async def get(self, object_id):
"""Abstract method for getting an entity."""

@abstractmethod
async def update(self, object_id, data):
"""Abstract method for updating an entity."""

@abstractmethod
async def read(self, **kwargs):
"""Abstract method for reading entities."""


class CRUDExperience(BaseCRUD):
"""Wip."""

@property
def _base_url(self) -> str:
"""Wip."""
return "experiences"

async def create(self, data: ExperienceCreate) -> UUID:
"""Wip."""
response = await self._client.post(
url=self._construct_url("/"), data=data.json() # type: ignore[arg-type]
)
response.raise_for_status()
return UUID(response.json())

async def get(self, object_id: Union[UUID, IRI]) -> Union[ExperienceRead, None]:
"""Wip."""
response = await self._client.get(url=self._construct_url(object_id))

if response.status_code == HTTPStatus.NOT_FOUND:
return None

response.raise_for_status()
return ExperienceRead(**response.json())

async def update(
self, object_id: UUID, data: Union[ExperienceUpdate, ExperienceCreate]
) -> Experience:
"""Wip."""
response = await self._client.put(
url=self._construct_url(object_id), data=data.json() # type: ignore[arg-type]
)
response.raise_for_status()
return Experience(**response.json())

async def read(self, **kwargs) -> List[ExperienceReadSnapshot]:
"""Wip."""
response = await self._client.get(url=self._construct_url("/"), params=kwargs)
response.raise_for_status()
return parse_obj_as(List[ExperienceReadSnapshot], response.json())

async def create_or_update(self, data: ExperienceCreate) -> Union[UUID, Experience]:
"""Wip."""
db_experience = await self.get(data.iri)

if db_experience is None:
return await self.create(data)

return await self.update(db_experience.id, data)


class CRUDRelation(BaseCRUD):
"""Wip."""

@property
def _base_url(self) -> str:
"""Wip."""
return "relations"

async def create(self, data: RelationCreate) -> UUID:
"""Wip."""
response = await self._client.post(
url=self._construct_url("/"), data=data.json() # type: ignore[arg-type]
)
response.raise_for_status()
return UUID(response.json())

async def get(self, object_id: UUID) -> Union[RelationRead, None]:
"""Wip."""
response = await self._client.get(url=self._construct_url(object_id))

if response.status_code == HTTPStatus.NOT_FOUND:
return None

response.raise_for_status()
return RelationRead(**response.json())

async def update(
self, object_id: UUID, data: Union[RelationUpdate, RelationCreate]
) -> Relation:
"""Wip."""
response = await self._client.put(
url=self._construct_url(object_id), data=data.json() # type: ignore[arg-type]
)
response.raise_for_status()
return Relation(**response.json())

async def read(self, **kwargs) -> List[RelationRead]:
"""Wip."""
response = await self._client.get(url=self._construct_url("/"), params=kwargs)
response.raise_for_status()
return parse_obj_as(List[RelationRead], response.json())

async def create_bidirectional(
self, source_id: UUID, target_id: UUID, kinds: Tuple[RelationType, RelationType]
) -> Tuple[UUID, UUID]:
"""Wip."""
kind, inverted_kind = kinds

relation = RelationCreate(source_id=source_id, target_id=target_id, kind=kind)

inverted_relation = RelationCreate(
source_id=target_id, target_id=source_id, kind=inverted_kind
)

relation_id = await self.create(relation)
inverted_relation_id = await self.create(inverted_relation)

return relation_id, inverted_relation_id


class ExperienceIndex(Client):
"""Wip."""

experience: CRUDExperience
relation: CRUDRelation

def __init__(self, url: Optional[str] = None):
"""Initialize the Warren HTTP client."""
self._url = url or "http://localhost:8100/api/v1/"

self._client = AsyncClient(
base_url=self._url,
)

self.experience = CRUDExperience(client=self._client)
self.relation = CRUDRelation(client=self._client)

async def close(self):
"""Close the asynchronous HTTP client."""
await self._client.aclose()
19 changes: 19 additions & 0 deletions src/api/core/warren/xi/indexers/client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
"""Wip."""

from typing import Protocol

from ..client import Client


class LMS(Client, Protocol):
"""Wip."""

url: str

async def get_courses(self, *args, **kwargs):
"""Wip."""
...

async def get_course_contents(self, *args, **kwargs):
"""Wip."""
...
Loading

0 comments on commit 5304d59

Please sign in to comment.