Skip to content

Commit

Permalink
feat: add get_uploaded_file_objects to Client
Browse files Browse the repository at this point in the history
  • Loading branch information
edgarrmondragon committed May 2, 2022
1 parent 70d9deb commit c1b9cc2
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 20 deletions.
4 changes: 0 additions & 4 deletions README.md
Expand Up @@ -31,10 +31,6 @@ Code samples and API documentation are available at [citric.readthedocs.io](http

If you'd like to contribute to this project, please see the [contributing guide](https://citric.readthedocs.io/en/latest/contributing/getting-started.html).

## Contributing

If you'd like to contribute to this project, please see the [contributing guide](https://citric.readthedocs.io/en/latest/contributing/getting-started.html).

## Credits

- [Claudio Jolowicz][claudio] and [his amazing blog post][hypermodern].
Expand Down
12 changes: 3 additions & 9 deletions docs/examples.md
Expand Up @@ -88,9 +88,6 @@ Common plugins are `Authdb` (default), `AuthLDAP` and `Authwebserver`.
## Get uploaded files and move them to S3

```python
import base64
import io

import boto3
from citric import Client

Expand All @@ -102,14 +99,11 @@ with Client(
"secret",
) as client:
survey_id = 12345
files = client.get_uploaded_files(survey_id)
for file in files:
content = base64.b64decode(files[file]["content"]) # Decode content
question_id = files[file]["meta"]["question"]["qid"]
for file in client.get_uploaded_file_objects(survey_id):
s3.upload_fileobj(
io.BytesIO(content),
file.content,
"my-s3-bucket",
f"uploads/{survey_id}/{question_id}/{file}",
f"uploads/sid={survey_id}/qid={file.meta.question.qid}/{file.meta.filename}",
)
```

Expand Down
10 changes: 9 additions & 1 deletion noxfile.py
Expand Up @@ -180,7 +180,15 @@ def docs_build(session: Session) -> None:
@session(name="docs-serve", python=main_python_version)
def docs_serve(session: Session) -> None:
"""Build the documentation."""
args = session.posargs or ["--open-browser", "--watch", ".", "docs", "build"]
args = session.posargs or [
"--open-browser",
"--watch",
".",
"--ignore",
"**/.nox/*",
"docs",
"build",
]
session.install(".[docs]")

build_dir = Path("build")
Expand Down
96 changes: 90 additions & 6 deletions src/citric/client.py
Expand Up @@ -4,11 +4,13 @@

import base64
import datetime
import io
import sys
from dataclasses import dataclass
from os import PathLike
from pathlib import Path
from types import TracebackType
from typing import Any, BinaryIO, Iterable, Mapping, Sequence, TypeVar
from typing import Any, BinaryIO, Generator, Iterable, Mapping, Sequence, TypeVar

import requests

Expand All @@ -24,6 +26,61 @@
_T = TypeVar("_T", bound="Client")


@dataclass
class QuestionReference:
"""Uploaded file question reference."""

title: str
"""Question title."""

qid: int
"""Question ID."""


@dataclass
class FileMetadata:
"""Uploaded file metadata."""

title: str
"""File title."""

comment: str
"""File comment."""

name: str
"""File name."""

filename: str
"""LimeSurvey internal file name."""

size: float
"""File size in bytes."""

ext: str
"""File extension."""

question: QuestionReference
""":class:`~citric.client.QuestionReference` object."""

index: int
"""File index."""


@dataclass
class UploadedFile:
"""A file uploaded to a survey response."""

meta: FileMetadata
""":class:`~citric.client.FileMetadata` object."""

content: io.BytesIO
"""File content as `io.BytesIO`_.
.. _io.BytesIO:
https://docs.python.org/3/library/io.html#io.BytesIO
"""


class Client:
"""LimeSurvey Remote Control client.
Expand Down Expand Up @@ -715,6 +772,34 @@ def get_uploaded_files(
"""
return self.__session.get_uploaded_files(survey_id, token)

def get_uploaded_file_objects(
self,
survey_id: int,
token: str | None = None,
) -> Generator[UploadedFile, None, None]:
"""Iterate over uploaded files in a survey response.
Args:
survey_id: Survey for which to download files.
token: Optional participant token to filter uploaded files.
Yields:
:class:`~citric.client.UploadedFile` objects.
"""
files_data = self.get_uploaded_files(survey_id, token)
for file in files_data:
metadata: dict = files_data[file]["meta"]
question: dict = metadata.pop("question")
content = base64.b64decode(files_data[file]["content"])

yield UploadedFile(
meta=FileMetadata(
**metadata,
question=QuestionReference(**question),
),
content=io.BytesIO(content),
)

def download_files(
self,
directory: str | Path,
Expand All @@ -734,14 +819,13 @@ def download_files(
dirpath = Path(directory)

filepaths = []
files_data = self.get_uploaded_files(survey_id, token=token)
uploaded_files = self.get_uploaded_file_objects(survey_id, token=token)

for file in files_data:
metadata = files_data[file]["meta"]
filepath = dirpath / metadata["filename"]
for file in uploaded_files:
filepath = dirpath / file.meta.filename
filepaths.append(filepath)
with open(filepath, "wb") as f:
f.write(base64.b64decode(files_data[file]["content"]))
f.write(file.content.read())

return filepaths

Expand Down

0 comments on commit c1b9cc2

Please sign in to comment.