Skip to content

Commit

Permalink
WIP ensure files are not downloaded unnecessarily (not really)
Browse files Browse the repository at this point in the history
  • Loading branch information
gonzalo-bulnes committed Jan 6, 2022
1 parent 7623547 commit 4a24b98
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 23 deletions.
26 changes: 21 additions & 5 deletions securedrop_client/api_jobs/sync.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,33 @@
import logging
from typing import Any
from typing import Any, List, Union

from sdclientapi import API
from sqlalchemy.orm.session import Session

from securedrop_client.api_jobs.base import ApiJob
from securedrop_client.storage import create_or_update_user, get_remote_data, update_local_storage
from securedrop_client.gui import state

logger = logging.getLogger(__name__)


def is_downloaded(submission_id: str) -> bool:
"""Check if a submission was already downloaded."""
# TODO: I'm not sure what's the best way to do this, for now I'll default
# to downloading everything (sometimes again). That's a sub-optimal option.
return False

class MetadataSyncJob(ApiJob):
"""
Update source metadata such that new download jobs can be added to the queue.
"""

NUMBER_OF_TIMES_TO_RETRY_AN_API_CALL = 2

def __init__(self, data_dir: str, state: Any) -> None:
def __init__(self, data_dir: str, gui_state: state.Writer) -> None:
super().__init__(remaining_attempts=self.NUMBER_OF_TIMES_TO_RETRY_AN_API_CALL)
self.data_dir = data_dir
self._state = state
self._state = gui_state

def call_api(self, api_client: API, session: Session) -> Any:
"""
Expand All @@ -44,10 +51,19 @@ def call_api(self, api_client: API, session: Session) -> Any:

for source in sources:
source_id = source.uuid
files = []
files: List[Union[state.FileId, state.DownloadedFileId]] = []
for submission in submissions:
submission_id = submission.uuid
if submission.source_uuid == source_id and submission.is_file():
files.append(submission.uuid)
# Avoid using flags, see https://www.youtube.com/watch?v=2JB1_e5wZmU
# This job turns network data into reliably typed local data.
# @creviera I really like this idea, let's discuss it! :)
# (See for example, how the state.DownloadedFileId instances get printed when debugging, isn't that nice?)
if is_downloaded(submission_id):
files.append(state.DownloadedFileId(submission_id))
else:
files.append(state.FileId(submission_id))

conversation_id = source_id
#print(f"Found files for conversation {conversation_id}: {files}")
self._state.upsert_conversation(conversation_id, files)
Expand Down
20 changes: 11 additions & 9 deletions securedrop_client/gui/state.py
Original file line number Diff line number Diff line change
@@ -1,29 +1,31 @@
import textwrap
from typing import Dict, List, NewType, Optional
from typing import Dict, List, NewType, Optional, Union
from uuid import UUID

from PyQt5.QtCore import QObject

ConversationId = NewType("ConversationId", UUID)
FileId = NewType("FileId", UUID)
#DowloadedFileId = NewType("DownloadedFileId", UUID)

class DownloadedFileId(UUID):
def __repr__(self) -> str:
return f"{self} (downloaded)"


class State(QObject):
def __init__(self) -> None:
super().__init__()

self._selected_conversation: Optional[ConversationId] = None
self._conversations: Dict[ConversationId, List[FileId]] = {}
self._conversations: Dict[ConversationId, List[Union[FileId, DownloadedFileId]]] = {}

def selected_conversation(self) -> Optional[ConversationId]:
return self._selected_conversation

def conversation_files(self, id: ConversationId) -> List[FileId]:
print(f"ID: {id}, state: {self._conversations}")
a = self._conversations.get(str(id), [])

print(f"to be returned: {a}")
return self._conversations.get(str(id), [])
default: List[Union[FileId, DownloadedFileId]] = []
return self._conversations.get(str(id), default)

def __repr__(self) -> str:
return textwrap.dedent(
Expand All @@ -44,8 +46,8 @@ def set_selected_conversation(self, id: ConversationId) -> None:
self._state._selected_conversation = id
print("selected source/convesation:", id)

def upsert_conversation(self, id: ConversationId, files: List[FileId]) -> None:
print(f"Upserting conversation {id}, {files}")
def upsert_conversation(self, id: ConversationId, files: List[Union[FileId, DownloadedFileId]]) -> None:
#print(f"Upserting conversation {id}, {files}")
self._state._conversations[id] = files

def __repr__(self) -> str:
Expand Down
22 changes: 13 additions & 9 deletions securedrop_client/logic.py
Original file line number Diff line number Diff line change
Expand Up @@ -1103,15 +1103,19 @@ def _get_conversation_from_database(self, conversation_id: str) -> List:
@login_required
def download_conversation(self, id: str) -> None:
print(f"Downloading files for conversation: {id}, state: {self._state}")
file_ids = self._state.conversation_files(state.ConversationId(uuid.UUID(id)))
print(f"Files: {file_ids}")
for file_id in file_ids:
job = FileDownloadJob(str(file_id), self.data_dir, self.gpg)
job.success_signal.connect(self.on_file_download_success, type=Qt.QueuedConnection)
job.failure_signal.connect(self.on_file_download_failure, type=Qt.QueuedConnection)
print(f"Enqueing download job for file: {file_id}")
self.add_job.emit(job)
self.file_download_started.emit(file_id)
files = self._state.conversation_files(state.ConversationId(uuid.UUID(id)))
print(f"Files: {files}")
for file_id in files:
if not isinstance(file_id, state.DownloadedFileId):
job = FileDownloadJob(str(file_id), self.data_dir, self.gpg)
job.success_signal.connect(self.on_file_download_success, type=Qt.QueuedConnection)
job.failure_signal.connect(self.on_file_download_failure, type=Qt.QueuedConnection)
print(f"Enqueing download job for file: {file_id}")
self.add_job.emit(job)
self.file_download_started.emit(file_id)
else:
print(f"Skipping download job for already downloaded file: {file_id}")


@login_required
def send_reply(self, source_uuid: str, reply_uuid: str, message: str) -> None:
Expand Down

0 comments on commit 4a24b98

Please sign in to comment.