6
6
import time
7
7
from enum import auto
8
8
from enum import IntEnum
9
+ from threading import RLock
9
10
10
11
from django .utils .translation import gettext_lazy as _
11
12
from kolibri .core .content .errors import InsufficientStorageSpaceError
72
73
"completed" : 1 ,
73
74
}
74
75
76
+ ENSURE_INITIATED_RLOCK = RLock ()
77
+
75
78
76
79
class ChannelNotImported (Exception ):
77
80
pass
@@ -639,6 +642,20 @@ def to_state(self):
639
642
640
643
return state
641
644
645
+ def validate_state (self , state ):
646
+ """Check a serialized representation of this download manager's state.
647
+
648
+ Returns True if the state is valid.
649
+ """
650
+
651
+ required_keys = (
652
+ "collection_name" ,
653
+ "collection_sequence" ,
654
+ "stage" ,
655
+ )
656
+
657
+ return all (key in state for key in required_keys )
658
+
642
659
def _next_task_or_stage (self , user ):
643
660
if not self ._tasks_pending :
644
661
# No more tasks pending in this stage, move to the next one
@@ -749,8 +766,9 @@ def ensure_initiated(api_function):
749
766
"""Decorator to initiate only once in the first API call."""
750
767
751
768
def wrapper (* args , ** kwargs ):
752
- if _collection_download_manager is None :
753
- _read_content_manifests ()
769
+ with ENSURE_INITIATED_RLOCK :
770
+ if _collection_download_manager is None :
771
+ _read_content_manifests ()
754
772
return api_function (* args , ** kwargs )
755
773
756
774
return wrapper
@@ -766,6 +784,15 @@ def _save_state_in_request_session(request):
766
784
request .session ["COLLECTIONS_STATE" ] = new_state
767
785
768
786
787
+ def _read_state_from_request_session (request ):
788
+ saved_state = request .session .get ("COLLECTIONS_STATE" , {})
789
+
790
+ if _collection_download_manager .validate_state (saved_state ):
791
+ return saved_state
792
+
793
+ return None
794
+
795
+
769
796
def _get_collections_info_by_name_sequence (name , sequence ):
770
797
if name not in _collections_by_name_sequence :
771
798
return None
@@ -843,7 +870,7 @@ def get_all_collections_info(request):
843
870
@api_view (["GET" ])
844
871
def get_should_resume (request ):
845
872
"""Return if there is a saved state that should be resumed."""
846
- saved_state = request . session . get ( "COLLECTIONS_STATE" )
873
+ saved_state = _read_state_from_request_session ( request )
847
874
name = None
848
875
sequence = None
849
876
if saved_state is not None :
@@ -881,7 +908,7 @@ def start_download(request):
881
908
collection = _collections_by_name_sequence [name ][sequence ]
882
909
883
910
# Fail if a previous download can be resumed
884
- saved_state = request . session . get ( "COLLECTIONS_STATE" )
911
+ saved_state = _read_state_from_request_session ( request )
885
912
if saved_state is not None :
886
913
raise APIException ("A previous download state was found. Resume it." )
887
914
@@ -910,15 +937,15 @@ def resume_download(request):
910
937
Returns download status.
911
938
"""
912
939
913
- saved_state = request . session . get ( "COLLECTIONS_STATE" )
940
+ saved_state = _read_state_from_request_session ( request )
914
941
if saved_state is None :
915
942
raise APIException ("No download state was found. Can't resume." )
916
943
917
944
# Init the download manager and start downloading
918
945
try :
919
946
_collection_download_manager .from_state (saved_state )
920
- name = saved_state ["name " ]
921
- sequence = saved_state ["sequence " ]
947
+ name = saved_state ["collection_name " ]
948
+ sequence = saved_state ["collection_sequence " ]
922
949
logger .info (f"Download resumed for name={ name } sequence={ sequence } " )
923
950
logger .info (f"Resumed download state: { saved_state } " )
924
951
except DownloadError as err :
@@ -979,7 +1006,7 @@ def cancel_download(request):
979
1006
def get_download_status (request ):
980
1007
"""Return the download status."""
981
1008
982
- saved_state = request . session . get ( "COLLECTIONS_STATE" )
1009
+ saved_state = _read_state_from_request_session ( request )
983
1010
if (
984
1011
_collection_download_manager .is_state_unset ()
985
1012
and saved_state is not None
0 commit comments