U
    ¿dC                     @   s2  d Z ddlmZ ddlmZ ddlmZ ddlmZ ddlmZ ddl	m
Z
 ddlmZ dd	lmZ dd
lmZ ddlmZ ddlmZ ddlmZ ddlmZ ddlmZ ddlmZ ddlmZ dZdZdZdZ dd Z!dd Z"dd Z#G dd deZ$G dd deZ%G dd  d eZ&G d!d" d"ej'Z(d#S )$z
Campaign models.py
    )JSONDecodeError)loads)Path)
is_zipfile)ZipFile)User)ValidationError)models)format_lazy)ugettext_lazyvalidate_language_code)AnnotationTaskRegistry)BaseMetadata)Market)Metadata)RESULT_TYPES   i  
   c           
      C   s   t | }t|tstdd}|D ]}|| kr"td|q"d}|D ]&}t|| tsJtd||| qJd}|D ]&}t|| tsztd||| qz|d }|d	 }	t||	 d
S )a.  Validates manifest JSON data.

    Parameters:
    - manifest_json:str contains manifest.json contents for validation.

    Raises:
    - JSONDecodeError in case of invalid JSON contents;
    - ValidationError in case of missing manifest data.

    Returns:
    - True if validation is successful.
    z*manifest.json should contain single object)CAMPAIGN_URLCAMPAIGN_NAMECAMPAIGN_KEYCAMPAIGN_NO
REDUNDANCYTASKS_TO_ANNOTATORSz&manifest.json should contain {0!r} key)r   r   r   z7manifest.json key {0!r} should be string type, is {1!r})r   r   z=manifest.json key {0!r} should be number (int) type, is {1!r}r   r   T)	r   
isinstancedictr   keysformatstrint!_validate_tasks_to_annotators_map)
manifest_jsonmanifest_dataZrequired_keysZrequired_keyZstring_typesZstring_typeZ	int_typesZint_typetasks_to_annotators
redundancy r&   >/var/www/rival/public_html/translation-eval/Campaign/models.py_validate_manifest_json    s>    
  
r(   c                 C   s  t | tstd| | D ]}t |ts8td|t|dksRtd||\}}}}}dd |||fD }|dd ||fD  t|std|d	d ||fD }	t|	std
|| dkstd||d | }
|
|kstd||||qdS )a  Validates TASKS_TO_ANNOTATORS data.

    Description:
        This should be an array of arrays, like this:
            "TASKS_TO_ANNOTATORS": [
                ["eng", "trk", "uniform", 18, 36],
                ["trk", "eng", "uniform", 18, 36]
            ]

        Each inner array should have five values:
            1. str: source language code
            2. str: target language code
            3. str: task map setup mode
            4. int: number of annotators
            5. int: number of tasks

        Currently, the only supported task map setup mode is "uniform";
        this requires the following invariant:

            annotators * 2 * redundancy == tasks

    Parameters:
    - tasks_to_annototators:dict contains TASKS_TO_ANNOTATORS dict;
    - redundancy:int specifies campaign redundancy.

    Raises:
    - ValidationError in case of missing manifest data.

    Returns:
    - True if validation is successful.
    zGmanifest.json key 'TASKS_TO_ANNOTATORS' should have list type, is {0!r}zQmanifest.json key 'TASKS_TO_ANNOTATORS' list item should have list type, is {0!r}   zMmanifest.json key 'TASKS_TO_ANNOTATORS' list item should be 5-tuple, is {0!r}c                 S   s   g | ]}t |tqS r&   )r   r   .0xr&   r&   r'   
<listcomp>   s     z5_validate_tasks_to_annotators_map.<locals>.<listcomp>c                 S   s   g | ]}t |tqS r&   )r   r    r*   r&   r&   r'   r-      s     zkmanifest.json key 'TASKS_TO_ANNOTATORS' list item should have <str, str, str, int, int> signature, is {0!r}c                 S   s   g | ]}t |qS r&   r   r*   r&   r&   r'   r-      s    zYmanifest.json key 'TASKS_TO_ANNOTATORS' list item has invalid language codes, check {0!r}uniformzZmanifest.json key 'TASKS_TO_ANNOTATORS' list item onlysupports "uniform" mode, check {0!r}   zfmanifest.json key 'TASKS_TO_ANNOTATORS' list item has bad task map ({0} * 2 * {1} != {2}), check {3!r}T)r   listr   r   lenextendalllower)r$   r%   itemZsource_codeZtarget_codemode
annotatorstasksZcorrect_typesZvalid_language_codesexpectedr&   r&   r'   r!   Y   sl     

   r!   c           
   
   C   sf  t | j}| j ds*td| jt| sBtd| jt| }d| krftd| j|	d
d}zt| W n4 tk
r } ztd| j|W 5 d}~X Y nX td	d
 | D }|std| jdd
 | D }|s
td|jt|}|d }t|t|ksHtd|jt|t|tdd |D }	t|	 dS )a  Validates package file.

    Parameters:
    - package_file:FieldFile contains binary contents of package ZIP file.

    Raises:
    - django.core.exceptions.ValidationError if package file is invalid.

    Returns:
    - True if validation is successful.
    z.zipz7Invalid package file {0!r} -- expected '.zip' extensionz8Invalid package file {0!r} -- expected valid ZIP archivezmanifest.jsonz4Invalid package file {0!r} -- expected manifest.jsonzutf-8z+Invalid package file {0!r} -- bad JSON: {1}Nc                 S   s   g | ]}| d qS )Batches/)
startswithr*   r&   r&   r'   r-      s     z*_validate_package_file.<locals>.<listcomp>z6Invalid package file {0!r} -- expected Batches/ folderc                 S   s$   g | ]}| d r|dr|qS )r:   z.json)r;   endswithr*   r&   r&   r'   r-      s   
 
zKInvalid package file {0!r} -- expected at least one batch JSON archive filer   zBInvalid package file {0!r} -- wrong number of batches ({1} != {2})c                 s   s$   | ]}|d  |d |d fV  qdS )r         Nr&   r*   r&   r&   r'   	<genexpr>  s     z)_validate_package_file.<locals>.<genexpr>T)r   namer4   r<   r   r   r   r   namelistreaddecoder(   r   anyr   r1   setprint)
Zpackage_filepackage_pathZpackage_zipr"   excZcontains_batches_folderZbatches_jsonr#   Zmanifest_tasksZtask_data_from_manifestr&   r&   r'   _validate_package_file   sz    
   rI   c                	   @   s   e Zd ZdZejeedeededdZ	ej
eddiejdd	ed
eddZejedd	eddZejedeededdZejedeededdZG dd dZdd Zdd Zdd Zde_dd Zde_d S )!CampaignTeamz!
    Models a campaign team.
    z	Team name(max. {value} characters)value
max_lengthverbose_name	help_textis_staffTz%(app_label)s_%(class)s_owner%(app_label)s_%(class)ssz
Team ownerz(must be staff member))limit_choices_to	on_deleterelated_namerelated_query_namerP   rQ   z%(app_label)s_%(class)s_memberszTeam members)rV   rW   rP   zRequired annotationsz(value in range=[1,{value}]))rP   rQ   zRequired hoursc                   @   s   e Zd ZdgZdZdZdS )zCampaignTeam.Meta	_str_nameZTeamTeamsN__name__
__module____qualname__orderingrP   verbose_name_pluralr&   r&   r&   r'   Meta9  s   r`   c                 C   s   d | j| jS )Nz	{0} ({1}))r   teamNameownerselfr&   r&   r'   _generate_str_name>  s    zCampaignTeam._generate_str_namec                 C   s*   z|    W dS  tk
r$   Y dS X dS )z>
        Validates the current CampaignTeam instance.
        TFN)
full_cleanr   rc   r&   r&   r'   is_validA  s
    zCampaignTeam.is_validc                 C   s
   | j  S )z7
        Proxy method returning members count.
        )memberscountrc   r&   r&   r'   teamMembersM  s    zCampaignTeam.teamMembersz# of team membersc                 C   sx   | j | j d  }d}tD ]8}| j D ](}|| jkr:q*||jj|dd 7 }q*qd}|dkrn|t| }d	|S )z
        Proxy method returning completion status in percent. This is defined as
        # of completed annotations / # of required annotations.

        r=   r   T)	createdBy	completedg        z{:.2%})
requiredAnnotationsrh   ri   r   r3   rb   objectsfilterfloatr   )rd   Z	count_allZ
count_doneresult_typeuserZ
completionr&   r&   r'   completionStatusY  s    
 zCampaignTeam.completionStatuszCompletion statusN)r[   r\   r]   __doc__r	   	CharFieldMAX_TEAMNAME_LENGTH_fra   
ForeignKeyr   PROTECTrb   ManyToManyFieldrh   PositiveSmallIntegerFieldMAX_SMALLINTEGER_VALUErm   ZrequiredHoursr`   re   rg   rj   short_descriptionrs   r&   r&   r&   r'   rJ     sH   
rJ   c                       s   e Zd ZdZejedddZeje	ej
eddZejeej
eddZejdd	d	ed
dZejdd	d	eddZG dd dZdd Zdd Z fddZd fdd	Z  ZS )CampaignDataz*
    Models a batch of campaign data.
    z	Data fileBatches)rP   	upload_tor   )rU   rP   r   TFzData valid?)blankdefaulteditablerP   zData ready?c                   @   s   e Zd ZdgZdZdZdS )zCampaignData.MetarX   ZBatchr   NrZ   r&   r&   r&   r'   r`     s   r`   c                 C   s   | j jS NdataFiler@   rc   r&   r&   r'   re     s    zCampaignData._generate_str_namec                 C   s   | j jS r   r   rc   r&   r&   r'   dataName  s    zCampaignData.dataNamec                    s   | j r| jrtt|   dS )zW
        Only activate current campaign data instance if both valid and ready.
        N)	dataValid	dataReadysuperr   activaterc   	__class__r&   r'   r     s    zCampaignData.activateNc                    s2   | j r| jr| jsttdtt| | d S )NzICannot activate campaign data as it is either not valid or not ready yet.)	activatedr   r   r   rw   r   r   clean_fields)rd   excluder   r&   r'   r     s    zCampaignData.clean_fields)N)r[   r\   r]   rt   r	   	FileFieldrw   r   ry   r   rz   marketr   metadataBooleanFieldr   r   r`   re   r   r   r   __classcell__r&   r&   r   r'   r   s  s:       r   c                   @   s   e Zd ZdZejeedeededdZ	ej
ddeddZejedd	d
eddZejeddd
eddZejddeddegdZdd Zedd ZedddZdS )Campaignz(
    Models an evaluation campaign.
    zCampaign namerK   rL   rN   TzCampaign task-specific options)r   nullrP   z%(app_label)s_%(class)s_teamsrS   rY   )r   rV   rW   rP   z%(app_label)s_%(class)s_batchesr   zPackage fileZPackages)r   r   rP   r   
validatorsc                 C   s   | j S r   campaignNamerc   r&   r&   r'   re     s    zCampaign._generate_str_namec                 C   s0   t jj|d}| s(d|}t|| S )z
        Get campaign with name campaign_name from database.

        Returns Campaign instance if exists, otherwise LookupError.
        r   z Failure to identify campaign {0})r   rn   ro   existsr   LookupErrorfirst)clsZcampaign_name_obj_msgr&   r&   r'   get_campaign_or_raise  s
    
zCampaign.get_campaign_or_raise)returnc                 C   sX   t  D ]6}| }d|}t| |d}|r| r|  S qd| j}t|dS )al  
        Get campaign type based on evaldata_{cls_name}_campaign QuerySet.

        For now, we assume that campaigns can only have a single type.

        We use the following check to identify the campaign's type:
        c.evaldata_directassessmentcontexttask_campaign.exists()

        Returns class object, which is a sub class of BaseAnnotationTask.
        zevaldata_{0}_campaignNzUnknown type for campaign {0})r   	get_typesr4   r   getattrr   r   r   )rd   cls_nameZqs_nameZqs_attrZqs_objr   r&   r&   r'   get_campaign_type  s    

zCampaign.get_campaign_typeN)r[   r\   r]   rt   r	   ru   MAX_CAMPAIGNNAME_LENGTHrw   rx   r   	TextFieldZcampaignOptionsr{   rJ   Zteamsr   batchesr   rI   ZpackageFilere   classmethodr   r   r   r&   r&   r&   r'   r     sH   
r   c                   @   sD   e Zd ZdZejeejeddZ	eje
ejeddZdd ZdS )TrustedUserzB
    Models trusted users who are exempt of quality controls.
    r   )rP   r   c                 C   s   d | jj| jjS )Nztrusted:{0}/{1})r   rr   usernamecampaignr   rc   r&   r&   r'   __str__  s    zTrustedUser.__str__N)r[   r\   r]   rt   r	   ry   r   rz   rw   rr   r   r   r   r&   r&   r&   r'   r     s   r   N))rt   jsonr   r   pathlibr   zipfiler   r   django.contrib.auth.modelsr   django.core.exceptionsr   	django.dbr	   django.utils.textr
   rx   django.utils.translationr   rw   Dashboard.modelsr   EvalData.modelsr   r   r   r   r   rv   r}   ZMAX_FILEFILED_SIZEr   r(   r!   rI   rJ   r   r   Modelr   r&   r&   r&   r'   <module>   s6   9_Za=Q