a #enM@sddlZddlZddlZddlmZddlZddlmZmZddlm Z m Z m Z ddl m Z ddlmZddlmZddlmZdd lmZmZmZmZmZmZmZdd lmZdd lmZdd lm Z dd l!m"Z"ddl#m$Z$m%Z%ddl&m'Z'ddl(m)Z)ddl*m+Z,ddl-m.Z.m/Z/ddl0m1Z1ddl2m3Z3ddl4m5Z6ddl7m8Z8ddZ9Gdddej:Z;Gdddej<Z=Gdddej>Z?Gd d!d!e"Z@d,d"d#ZAd-d$d%ZBGd&d'd'e@ZCGd(d)d)e@ZDGd*d+d+e"ZEdS).N) FileWrapper) copyfileobjmove)ObjectDoesNotExistSuspiciousFileOperationValidationError)InMemoryUploadedFile) transaction) FileResponse) HttpResponse)status serializersviewsetsfilters exceptions permissionsparsers)action)AllowAny)Response)APIView)modelspending_actions) status_codes)ProcessingNode)tasks)get_and_check_projectget_asset_download_filename) TagsField)path_traversal_check) gettext_lazy)settingscs&ddtfddddDDS)NcSsg|]}|D]}|q qSr#).0Z filesListfiler#r#/webodm/app/api/tasks.py sz!flatten_files..cs |SN)getlist)keyZ request_filesr#r&!zflatten_files..cSsg|]}|qSr#r#)r$keysr#r#r&r'"r-)mapr+r#r+r& flatten_filess  r0c@seZdZddZdS)TaskIDsSerializercCs|jSr()idselfobjr#r#r&to_representation&sz#TaskIDsSerializer.to_representationN)__name__ __module__ __qualname__r6r#r#r#r&r1%sr1c@szeZdZejejjdZ eje jdZ e Z e Ze ZeddZddZddZdd ZGd d d Zd S) TaskSerializer)querysetF)requiredcCs|jdurt|jSdSdSr()processing_nodestrr3r#r#r&get_processing_node_name1s  z'TaskSerializer.get_processing_node_namecCs|Sr()get_statisticsr3r#r#r&r@7szTaskSerializer.get_statisticscCsH|jdurDttdd|jj}t|dkrDd|dvrD|ddSgS)a+ When a task has been associated with a processing node and if the processing node supports the "rerun-from" parameter this method returns the valid values for "rerun-from" for that particular processing node. TODO: this could be improved by returning an empty array if a task was created and purged by the processing node (which would require knowing how long a task is being kept see https://github.com/OpenDroneMap/NodeODM/issues/32 :return: array of valid rerun-from parameters NcSsd|vo|ddkS)Nnamez rerun-fromr#)dr#r#r&r,Gr-z3TaskSerializer.get_can_rerun_from..rdomain)r=listfilteravailable_optionslen)r4r5Zrerun_from_optionr#r#r&get_can_rerun_from:s  z!TaskSerializer.get_can_rerun_fromc@seZdZejZdZdZdS)zTaskSerializer.Meta)orthophoto_extent dsm_extent dtm_extent)processing_timer last_error created_atpending_actionavailable_assetssizeN)r7r8r9rTaskmodelexcluderead_only_fieldsr#r#r#r&MetaMsrVN)r7r8r9r PrimaryKeyRelatedFieldrProjectobjectsallprojectrr=SerializerMethodFieldZprocessing_node_nameZcan_rerun_from statisticsrtagsr?r@rHrVr#r#r#r&r:)s r:c@seZdZdZejjdddZ e j e j e j fZdZddZd)d d Zed d gdddZed d gdddZed d gdddZed dgdd*ddZd+ddZd,ddZed d gdd-ddZed d gdd.ddZed d gdd/d d!Zd0d"d#Zd1d%d&Zd'd(ZdS)2 TaskViewSetz Task get/add/delete/update A task represents a set of images and other input to be sent to a processing node. Once a processing node completes processing, results are stored in the task. rIrJrK__all__cCs*|jdkrtjg}ntjg}dd|DS)aH Instantiates and returns the list of permissions that this view requires. We don't use object level permissions on tasks, relying on project's object permissions instead (but standard model permissions still apply) and with the exception of 'retrieve' (task GET) for public tasks access retrievecSsg|] }|qSr#r#)r$ permissionr#r#r&r'ir-z/TaskViewSet.get_permissions..)rrrZDjangoModelPermissions)r4permission_classesr#r#r&get_permissions]s  zTaskViewSet.get_permissionsNZchange_projectc Cstt|||z|jj||d}Wnttfy>tYn0||_d|_d|_ | t j |jtddiS)Npkr[FsuccessT)rr;getrrrNotFoundrOpartialrMsave worker_tasks process_taskdelayr2r)r4rOrequestrg project_pkpermstaskr#r#r&set_pending_actionks zTaskViewSet.set_pending_actionTpost)detailmethodscOs|jtjg|Ri|Sr()rtrCANCELr4argskwargsr#r#r&cancel|szTaskViewSet.cancelcOs|jtjg|Ri|Sr()rtrRESTARTryr#r#r&restartszTaskViewSet.restartcOs|jtjg|Rddi|S)Nrr)Zdelete_project)rtrREMOVEryr#r#r&removeszTaskViewSet.removeric Cs|t||z|jj||d}Wnttfy<tYn0tdt|j dd}t d |j d|dS)z Retrieve the console output for this task. An optional "line" query param can be passed to retrieve only the output starting from a certain line number. rfrline N)rr;rirrrrjmaxint query_paramsrjoinconsoleoutputrstripsplit)r4rprgrqrsline_numr#r#r&rs zTaskViewSet.outputcCsBt|||jj|d}t|j||}t|dd}t|j S)N)r[T)many) rr;rErOrderingFilterfilter_querysetrpr:rdata)r4rprqr serializerr#r#r&rDs   zTaskViewSet.listc CsZz|jj||d}Wnttfy2tYn0|jsHt||jj t |}t |j S)Nrf) r;rirrrrjpublicrr[r2r:rrr4rprgrqrsrr#r#r&raszTaskViewSet.retrievec Cst||dz|jj||d}Wnttfy>tYn0d|_t| |_ |j dkrntjt dd| | tj|jt|}t|jtjdS)zC Commit a task after all images have been uploaded rerfFrz0You need to upload at least 1 file before commitrvr )rr;rirrrrjrkrG scan_images images_count_ update_sizerlrmrnror2r:rrr HTTP_200_OKrr#r#r&commits  zTaskViewSet.commitc Cst||dz|jj||d}Wnttfy>tYn0t|j}t |dkrftjt dd| |t | |_ t||jdd}|jdd|td ditjd S) z& Add images to a task rerfrzNo files uploadedrTrrkraise_exceptionrhr)rr;rirrrrjr0FILESrGrhandle_images_uploadrrr:ris_validrlrr r)r4rprgrqrsfilesrr#r#r&uploads     zTaskViewSet.uploadc Cst||dz|jj||d}Wnttfy>tYn0|}|rftdt |j dt j dStdt dit j dSdS) z" Duplicate a task rerfT)rhrsrerrorzCannot duplicate taskN)rr;rirrrrj duplicaterr:rr rr)r4rprgrqrsnew_taskr#r#r&rs zTaskViewSet.duplicatecCs,t||d}|jdr`tjjj|d|jvr2tjndd}t ||jdd}|j dd| nt |j }t|dkrtjtd d t|tjjj|d|jvrtjndd}||t||_t ||jdd}|j dd| tj|jWdn1s0Yt|jtjd S) Nrerk resize_to)r[rOTrrrz.Cannot create task, you need at least 2 imagesrr)rrrirrRrYcreaterRESIZEr:rrlr0rrGrrrr atomicrrrrmrnror2rr HTTP_201_CREATED)r4rprqr[rsrrr#r#r&rs,           .zTaskViewSet.createFc Cst||dz|jj||d}Wnttfy>tYn0d|jvr~zt||jddWntjy|tYn0t ||j|d}|j dd| t j |jt|jS)Nrerfr[rTr)rr;rirrrrjrPermissionDeniedr:rrlrmrnror2r)r4rprgrqrkrsrr#r#r&updates   zTaskViewSet.updatecOs d|d<|j|g|Ri|S)NTrk)r)r4rprzr{r#r#r&partial_update&szTaskViewSet.partial_update)NNre)NN)N)NN)NN)NN)NN)N)NNF)r7r8r9__doc__rrRrYrZdeferr;rMultiPartParser JSONParser FormParserparser_classesZordering_fieldsrdrtrr|r~rrrDrarrrrrrr#r#r#r&r_Rs2                 " r_c@s4eZdZejjdddZe fZ ifddZ dS)TaskNestedViewrIrKrJc CsVz|jjfi|j|d}Wnttfy<tYn0|jsRt||j j |S)N)rg) r;annotaterirrrrjrrr[r2)r4rprgrrsr#r#r&get_and_check_task/sz!TaskNestedView.get_and_check_taskN) r7r8r9rrRrYrZrr;rrcrr#r#r#r&r+src Cstj|}|dur|}t|j}t|d}|dkpB|jdd}|rRt|}nt t |t |dpjdd}t |dpd|d<d |||d <||d <|rd |d <|S)NrbgחAZ _force_streamFrapplication/zip content_type Content-Type{}; filename={}Content-DispositionzContent-Lengthyes_stream)ospathbasenamestatst_sizeopenGETrir r r mimetypes guess_typeformat) rpZfilePathcontent_dispositiondownload_filenamefilenamefilesizer%streamresponser#r#r&download_file_response<s"    rcCsPtt|t|dpdd}t|dp.d|d<d|||d<d|d<|S) Nrrrrrrrr)r rrrr)rprrrrr#r#r&download_file_streamWsrc@seZdZdddZdS) TaskDownloadsNc Cs|||}z||\}}Wn ty>ttdYn0|s^tj|s^ttd|j dt ||}|st ||d|dSt ||d|dSdS)7 Downloads a task asset (if available) Asset does not existr attachment)rN)rget_asset_file_or_zipstreamFileNotFoundErrorrrjrrrisfilerrirrr) r4rprgrqassetrsZasset_fsZ is_zipstreamrr#r#r&riis  zTaskDownloads.get)NNrr7r8r9rir#r#r#r&rhsrc@seZdZdddZdS) TaskAssetsNrcCsz|||}zt|||d}Wn tyFttdYn0tj |r`tj |rnttdt ||dS)rrrinline) rr assets_pathrrrjrrrexistsisdirr)r4rprgrqZunsafe_asset_pathrs asset_pathr#r#r&ris  zTaskAssets.get)NNrrr#r#r#r&rsrc@s.eZdZejfZejejej fZ dddZ dS)TaskAssetsImportNc Cs|t||d}t|j}|jdd}|jdtd}|sVt|dkrVtjtdd|rvt|dkrvtjtd d|jd }|jd }|jd d} d} t|dkr&|dur&|dur&| dur&|jd d} zt |}t | } t | } Wn t ytjddYn0t dd|}t jtj|d} t j| r^|dkr^t | t| d} | | t|dtr|dD]} | | qnBt|dd}| |Wdn1s0YWdn1s0Y|d| kr&tdditjdSt "t!j"j#j$|d||rJ|ndt%j&t'j(d}|)|*d}| dur t|dkr t|d~} t|dtr|dD]} | | qn>t|dd}t+|| Wdn1s0YWdn1s0Yn| dur6t,-| |t.j/0|j1Wdn1sZ0Yt2|}t|jtj3dS)NreurlrAz Imported Taskrz-Cannot create task, you need to upload 1 filerrz:Cannot create task, either specify a URL or upload 1 file.Z dzchunkindexZdzuuidZdztotalchunkcountZdzchunkbyteoffsetz Some parameters are not integersz[^0-9a-zA-Z-]+rz.uploadabrZuploadedTrFzfile://all.zip)r[auto_processing_noderA import_urlr rOzall.zipzwb+)4rr0rrrirrGrrr ValueErrorresubrrrr"FILE_UPLOAD_TEMP_DIRrunlinkrseek isinstancerchunkswritetemporary_file_pathreadrr rr rrrRrYrrRUNNINGrIMPORTcreate_task_directoriesrrshutilrrmrnror2r:r)r4rprqr[rr task_nameZ chunk_indexuuidZtotal_chunk_countZtmp_upload_fileZ byte_offsetfdchunkr%rsZdestination_filerr#r#r&rusn    ,    N     L  .zTaskAssetsImport.post)N) r7r8r9rrrcrrrrrrur#r#r#r&rsr)N)N)FrrrZ wsgiref.utilrrrrdjango.core.exceptionsrrrdjango.core.files.uploadedfiler django.dbr django.httpr r rest_frameworkr r rrrrrZrest_framework.decoratorsrZrest_framework.permissionsrrest_framework.responserZrest_framework.viewsrapprrnodeodmrnodeodm.modelsrworkerrrmcommonrrr^r app.securityr django.utils.translationr!rZwebodmr"r0BaseSerializerr1ModelSerializerr:ZViewSetr_rrrrrrr#r#r#r&sF     $           )Z